Tools
Let the agent call your own systems mid-conversation: look up an order, book a slot, save a lead
A tool is an HTTP request your agent sends to your system in the middle of a conversation: check an order's status, look up free appointment slots, create an order, save a lead to a sheet. The agent decides when to call it. It collects the values the tool needs from the customer, waits for your answer, and speaks from it.
Tools work on calls and in WhatsApp chats. An agent can have up to 20.
How a tool call works
The agent decides
The model sees each tool's name, description and parameters, never its address, headers or secrets. From the description it decides that now is the moment. For example: "the customer asked where their order is, and I have the order number."
Voice213 builds the request
It fills your request template with the values the agent chose, plus your tool's secrets and a few built-in values, and sends it from Voice213's servers.
Your server answers
Return a short JSON object (or plain text) with the facts the agent needs. The answer goes back to the agent, cut to 4,000 characters.
The agent carries on
It tells the customer what it found, in its own language. If your server failed, it tells them that and follows your Error handling instruction.
Create a tool
Open Tools → Add tool:
| Field | Rules |
|---|---|
| Name | Lowercase English letters, digits and underscores, starting with a letter: get_order_status. Not a built-in name (end_call, search_knowledge, get_current_time). |
| Description | What the tool does and when to use it, at least 10 characters. The agent decides from this alone, so be specific: "Get the status and delivery date of an order. Use it when the customer asks where their order is and gives the order number." |
| Tool arguments | Up to 20 values the agent collects first. Each one has a name, a type (string, number, integer or boolean), a description ("the order number, digits only") and whether it's required. |
| Method and URL | GET, POST, PUT, PATCH or DELETE to a public https:// address, up to 2,048 characters. Private and local addresses are refused, also when a host name only resolves to one at the moment of the call. |
| Request headers | Up to 20, for example Authorization: Bearer {{api_key}}. |
| Body | For POST, PUT and PATCH: a JSON template (below). Leave it empty to send the parameters as a JSON object. |
| Secret keys | Up to 10 values, such as API keys, stored encrypted and used in templates as {{name}}. The agent never sees them, and the dashboard only shows the last four characters of a long one. |
| Execution mode | Wait for result, Fire and forget, or Background (below). |
| Request timeout | 1 to 60 seconds (15 by default). |
| What the agent says while waiting | A short line for a slow request: "let me check that for you…". |
| Error handling | What the agent tells the customer when the request fails: "say we'll call them back within the hour". |
Test runs the request for real with sample values and shows your server's answer, so you can check the template before the agent uses it.
Templates and placeholders
Put {{name}} anywhere in the URL, the headers or the body:
- the arguments the agent filled in, by their names;
- the tool's secret keys, by their names. A secret always wins over a parameter with the same name, so a model can never overwrite your key;
- four built-in values:
| Placeholder | Value |
|---|---|
{{caller_phone}} | The customer's number, when known (WhatsApp: +213…) |
{{caller_name}} | The customer's name, when known (their WhatsApp profile name) |
{{conversation_id}} | This conversation's id, the same one your webhooks receive |
{{agent_id}} | The agent's id |
Each value is encoded for where it goes: URL-encoded in the address, and as a JSON value in the body. A placeholder that is a whole JSON string, "{{quantity}}", takes the value's own type, so a number stays a number. Inside a longer string, the value is escaped into it:
{
"phone": "{{caller_phone}}",
"wilaya": "{{wilaya}}",
"quantity": "{{quantity}}",
"note": "Confirmed by phone, conversation {{conversation_id}}"
}{
"phone": "+213555123456",
"wilaya": "Oran",
"quantity": 2,
"note": "Confirmed by phone, conversation 6ac2aab5f3fdade650f3b120"
}For GET and DELETE without a placeholder in the URL, the parameters are sent as the query string: GET https://api.myshop.dz/orders?order_id=1042.
Modes
| Mode | On a call | Use it for |
|---|---|---|
| Wait for result (default) | The agent waits for your answer and speaks from it | Lookups: order status, stock, prices, free slots |
| Fire and forget | The request is sent and the agent carries on at once | Recording things: save a lead, log a callback request |
| Background | The agent carries on, and your answer is handed to it when it lands | Slow work the conversation shouldn't wait for |
In WhatsApp chats, every tool waits for its answer before the agent replies.
Your endpoint
Answer quickly and briefly, with only what the agent needs to say:
import crypto from "node:crypto";
// The key you stored as a tool secret, sent as a header: X-Api-Key: {{api_key}}
const TOOL_KEY = process.env.AGENT_TOOL_KEY;
if (!TOOL_KEY) throw new Error("Set AGENT_TOOL_KEY"); // never run with an empty key: "" would match a missing header
const sameKey = (given = "") =>
given.length === TOOL_KEY.length && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(TOOL_KEY));
app.get("/agent/orders/:id", (req, res) => {
if (!sameKey(req.get("x-api-key"))) return res.status(401).end();
const order = findOrder(req.params.id);
if (!order) return res.status(404).json({ error: "No order with this number" });
res.json({ status: order.status, deliveryDate: order.deliveryDate, wilaya: order.wilaya });
});- Authenticate the request. Tool requests aren't signed, so put a key in a header (
X-Api-Key: {{api_key}}, withapi_keystored as a secret key) and check it in constant time. TheUser-AgentisVoice213-Agent/1.0. - Redirects stay on your server. A redirect to another path on the same origin is followed; one to another host or scheme is not, so your headers and keys never go anywhere else. The redirect itself comes back as the tool's (failed) answer.
- Placeholders stay inside their segment. A value is URL-encoded, and a value of
.or..in the address is refused, so an argument can't walk your URL to another path. - Status codes matter. A 2xx answer is a success. Anything else tells the agent that the request failed with that status and your message, followed by your Error handling instruction.
- Keep it small. Your answer is cut to 4,000 characters, and only the first 200 KB are read. Long answers slow the agent down.
- Make writes idempotent. If a customer confirms twice, the agent may call the tool twice. Use
{{conversation_id}}to recognise a repeat.
Built-in tools
Every agent also has End call, Knowledge search and Current time. See Built-in tools.
Tools in tests
Automated test scenarios never reach your systems, because they could create real orders. The agent is told the tool succeeded with the values it chose. Test chats in the dashboard simulate tools the same way, unless you turn on Run tools for real. Test calls and real conversations always call your tools.
The tool.called webhook reports each of your tools that runs during a call.
Was this page helpful?