Web tools are JavaScript functions that run in the user’s browser, allowing your agent to interact directly with your web application. They are registered via the Voice Agents SDK and triggered by the agent during conversation.
Web tools only work when the agent is deployed via the Hamsa Voice Agents SDK. They do not function on phone calls.
Since web tools run as JavaScript in the browser, they can do anything your web application can:
- Navigate the user — take them to a specific page (e.g., “Show me the backpacks” → navigate to
/products/backpacks)
- Open UI elements — show a modal, expand a panel, trigger a popup
- Interact with the page — scroll to a section, highlight an element, fill a form
- Read page data — get the current URL, check cart contents, read form values
- Call client-side APIs — access local storage, query IndexedDB, use browser APIs
How They Work
User speaks → Agent decides to call a web tool → SDK triggers your registered function →
Function runs in browser → Returns string or JSON → Response sent back to agent
Web tools are registered as JavaScript functions when starting a conversation via the SDK:
const tools = [
{
function_name: "navigate_to_page",
description: "Navigate the user to a specific page on the website",
parameters: [
{
name: "path",
type: "string",
description: "The URL path to navigate to"
}
],
required: ["path"],
fn: async (path) => {
window.location.href = path;
return "Navigated to " + path;
}
},
{
function_name: "open_product_modal",
description: "Open a product details modal for a specific product",
parameters: [
{
name: "productId",
type: "string",
description: "The product ID to display"
}
],
required: ["productId"],
fn: async (productId) => {
const product = await getProduct(productId);
showProductModal(product);
return JSON.stringify({ name: product.name, price: product.price });
}
}
];
agent.start({
agentId: "YOUR_AGENT_ID",
tools: tools,
voiceEnablement: true
});
Return Values
Web tools can return:
- A string — simple confirmation sent back to the agent
- A JSON object — structured data the agent (or flow) can use
// String return — agent uses it conversationally
fn: async (path) => {
window.location.href = path;
return "Done, the user is now on the backpacks page.";
}
// JSON return — can be extracted in flow agents
fn: async (productId) => {
const product = await getProduct(productId);
return JSON.stringify({
name: product.name,
price: product.price,
inStock: product.available
});
}
Behavior in Single Prompt vs Flow Agents
Single Prompt Agents
- The agent decides when to call the tool based on conversation context
- The return value (string or JSON) is sent back to the LLM as-is
- The model uses it as context for its next response
- No structured extraction — the model interprets the response naturally
Flow Agents
- Web tools are used via Web Tool Nodes
- You can extract specific values from JSON responses using output mapping
- The extracted values can be used in transitions to route the conversation
- You can configure a processing message the agent speaks while the tool runs
Web Tool Node: Check_Cart
Output Mapping:
cart_total: $.total
item_count: $.items.length
↓ (Equation: {{cart_total}} > 100)
Conversation Node: "Your cart total is {{cart_total}}. Would you like to checkout?"
↓ (Equation: {{cart_total}} <= 100)
Conversation Node: "You have {{item_count}} items. Can I help you find anything else?"
Examples
Website Navigation
{
function_name: "show_category",
description: "Navigate the user to a product category page",
parameters: [
{ name: "category", type: "string", description: "Product category name" }
],
required: ["category"],
fn: async (category) => {
const slug = category.toLowerCase().replace(/\s+/g, '-');
window.location.href = `/products/${slug}`;
return `Showing ${category} products`;
}
}
Opening a Modal
{
function_name: "show_product_details",
description: "Open a modal with detailed product information",
parameters: [
{ name: "productId", type: "string", description: "The product ID" }
],
required: ["productId"],
fn: async (productId) => {
const product = await fetch(`/api/products/${productId}`).then(r => r.json());
document.getElementById('product-modal').showModal();
renderProductDetails(product);
return JSON.stringify({ name: product.name, price: product.price });
}
}
Reading Cart Data
{
function_name: "get_cart_summary",
description: "Get the current shopping cart contents and total",
parameters: [],
required: [],
fn: async () => {
const cart = getCartFromLocalStorage();
return JSON.stringify({
items: cart.items.length,
total: cart.total,
currency: cart.currency
});
}
}
{
function_name: "submit_contact_form",
description: "Submit the contact form with the provided details",
parameters: [
{ name: "name", type: "string", description: "Contact name" },
{ name: "email", type: "string", description: "Email address" },
{ name: "message", type: "string", description: "Message content" }
],
required: ["name", "email", "message"],
fn: async (name, email, message) => {
const response = await fetch('/api/contact', {
method: 'POST',
body: JSON.stringify({ name, email, message })
});
return response.ok ? "Form submitted successfully" : "Submission failed";
}
}
Best Practices
- Write clear descriptions — the LLM uses the description to decide when to call the tool
- Return useful data — return information the agent can relay to the user
- Handle errors gracefully — return an error message string rather than throwing exceptions
- Keep execution fast — long-running tools create silence in the conversation
- Use JSON returns in flow agents — enables extraction and conditional routing
Versioning
Like server-side tools, web tools are tracked via persistent IDs. When you register a tool in your SDK code, use the Persistent ID provided in the dashboard if you want to link it to a dashboard-managed tool record. This ensures that any call logs or analytics in the dashboard are correctly associated with the specific web tool.