Guides
Function tools
Let an agent call your API, transfer the call, end it or send an SMS during a conversation.
A function tool is something the agent's LLM may decide to do mid-call. You describe when to use it and what arguments it takes; the platform makes the request and gives the result back to the model.
In the dashboard: Agent Studio → Tools tab.
Where tools live
Tools are part of the conversation task: agent_config.tasks[i].tools_config.api_tools. It has two parts: tools (what the LLM sees) and tools_params (how each tool is carried out), matched by name.
{
"tools": [
{"name": "check_order_status", "key": "custom_task",
"description": "Use when the customer asks about the status of an order.",
"pre_call_message": "Let me check that for you.",
"parameters": {"type": "object",
"properties": {"order_id": {"type": "string", "description": "The order number"}},
"required": ["order_id"]}}
],
"tools_params": {
"check_order_status": {
"method": "GET",
"url": "https://api.example.test/orders",
"api_token": "Bearer <your token>",
"headers": {"X-Source": "voice"},
"param": {"order_id": "%(order_id)s"}
}
}
}Tool kinds
key | What it does |
|---|---|
custom_task | An HTTP request to your API |
check_availability_of_slots, book_appointment | Calendar-style tools. They are plain HTTP tools with ready-made parameter templates; you supply the URL |
transfer_call | Hands the caller to a person. Needs param.call_transfer_number: an E.164 phone number. A SIP URI is stored but refused when the call tries to transfer |
end_call | Ends the call. Needs no tools_params entry |
send_sms | Texts the person on the call. Needs param.message (at most 1,000 characters) |
Rules checked on save
name: letters, digits,_and-, at most 64 characters, unique.description: required. It tells the LLM when to call the tool.parameters: a JSON schema of typeobject; property typesstring,integer,number,boolean,array,object;requiredmust name existing properties.method:GET,POST,PUT,PATCHorDELETE(defaultPOST).url: an http(s) URL, required for the three HTTP kinds.scope:global(default) ornodewith a list of existing node ids, for graph agents.
A failure is a 400 with the path, for example agent_config.tasks.0.tools_config.api_tools.tools.0.description is required: it tells the LLM when to call 'check_order_status'.
How a request is built
api_tokenis sent verbatim as theAuthorizationheader.parambecomes the query string forGETandDELETE, and the JSON body otherwise.- Placeholders
%(name)s,%(name)iand%(name)fare filled from the tool's arguments, thenagent_id,call_sid,to_number,from_number, the call'suser_dataand values a graph has collected. An unknown placeholder becomes an empty string. - The request has a 10 second timeout and does not follow redirects.
URLs into private networks are refused
Before every tool request the URL is resolved. If it points at a private, loopback, link-local, reserved or multicast address the request is not sent and the tool result is an error starting with blocked:. This also applies to pre-call webhooks. Operators can lift it for local development only.
Helper endpoints
These help you build a tool before saving it. See Function tools reference.
| Endpoint | Use |
|---|---|
GET /tools/catalog | Templates for each kind, the allowed keys and methods. send_sms is listed under extra_tools |
POST /tools/validate | Check one tool or a whole api_tools object |
POST /tools/from-curl | Turn a pasted cURL command into a tool |
POST /tools/test | Run the tool's request once and see the answer |
The helper endpoints use the dashboard's form: one object per tool, with its tools_params entry under value.
curl -X POST https://api.kshyamai.space/tools/from-curl \
-H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef" \
-H "Content-Type: application/json" \
-d '{"curl": "curl -H \"Authorization: Bearer abc\" https://api.example.test/orders?order_id=ORD-1"}'The method, URL, headers and query or body fields become parameters, and Authorization moves to api_token. Errors: curl is required: paste a cURL command, The command must start with curl, The cURL command has no http(s) URL.
Test a tool
curl -X POST https://api.kshyamai.space/tools/test \
-H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef" \
-H "Content-Type: application/json" \
-d '{"agent_id": "3c90c3cc-0d44-4b50-8822-8dd25736052a", "name": "check_order_status",
"arguments": {"order_id": "ORD-1"}}'{
"ok": true, "status_code": 200, "body": {"status": "delivered"}, "error": null, "duration_ms": 12,
"request": {"method": "GET", "url": "https://api.example.test/orders?order_id=ORD-1",
"headers": {"Content-Type": "application/json", "Authorization": "**********", "X-Source": "voice"},
"body": null}
}This makes the real request. An API that answers non-2xx, or cannot be reached, still gives 200 with ok: false and error. A 400 means the request never went out: arguments.order_id is required by this function, Only tools that call an API can be tested; transfer_call does not make a request, or The function's url is not allowed: ....
Pre-call webhooks
There are two different things with this name.
- Per tool: set
pre_call_webhook_paramon a tool's params. Just before the tool runs, the platform posts the execution plus those fields topre_call_webhook_url, or to the agent'swebhook_urlif none is set. A failing endpoint never fails the tool. - Per call:
agent_config.pre_call_webhook_url(oragent_data.pre_call_webhook_urlon one call) is posted the execution before dialing. If it answers with a JSON object within 5 seconds, its keys are merged into the call's variables. A failure never stops the call.
Tool call logs
Every tool run is recorded on the execution as tool_call_logs:
[{"name": "check_order_status", "key": "custom_task", "arguments": {"order_id": "ORD-1"},
"status": "success", "result": {"status": "delivered"}, "status_code": 200, "duration_ms": 12,
"at": "2026-10-13T09:30:02.114000+00:00"}]Results longer than 5,000 characters are cut. The raw logs also get a request and a response entry under component function_call, with credentials masked. A transfer fills transfer_call_data on the execution. See Post-call data.
send_sms
The text comes from the tool's param.message, not from the LLM. It is sent once per call and text, and only if the do-not-call list, consent (when required), the do-not-disturb check and the agent's calling hours all allow it. Otherwise the tool result is {"sent": false, "reason": "..."}. The test chat never sends.
Limitations
- The two calendar kinds are generic HTTP tools. There is no built-in calendar provider integration.
- On the
ownengine, a transfer is reported and handed to the telephony provider; it has not been proven on a live call. Theownengine does not fire per-tool pre-call webhooks; themockengine and the test chat do. send_smssends during the call, not after it, and real SMS providers have only been tested against local fakes. See Compliance.- Agents saved before secrets were encrypted may still hold
api_tokenin plain text until the operator runs the rotation script.