Tool types
Full schema reference for every tool definition shape — function (HTTP), endCall, transferCall, calculator, query (knowledge base). Covers the saved-tool format (POST /tools) and the inline transient-agent format separately.
zoxaAI has two separate tool formats that look similar but have different field names and validation rules:
| Format | Where it's used | Documented |
|---|---|---|
| Saved-tool format | POST /tools, PUT /tools/{tool_uuid}. UI tool editor. Referenced from agents by UUID string. | This page (below) |
| Inline canonical format | agent.tools[] of POST /api/v1/call when sending a transient agent inline. Also the on-disk CallConfigSnapshot.tools[] form. | Inline tool format |
Pick the right one for the call you're making. The shapes are not interchangeable — sending a saved-tool transferCall config inline will fail validation because the inline schema rejects fields like messageType and audioRecordingId on transferCall.
Why two formats?
Saved tools are configured by humans in the UI and need rich message/audio metadata. Inline tools live in the runtime snapshot the pipecat runner consumes — that schema is canonical and locked down (extra fields rejected) so the runner never has to deal with shape drift. The agent resolver translates between them when a saved tool is referenced by UUID.
Saved-tool definition
All saved-tool definitions share two common keys:
| Field | Notes |
|---|---|
schema_version | Integer, currently 1. |
type | Discriminator — one of function, endCall, transferCall, calculator, query. |
function (HTTP API)
Call an external HTTP API. The LLM fills in parameters; the runner makes the request.
{
"schema_version": 1,
"type": "function",
"config": {
"method": "GET",
"url": "https://api.acme.com/orders/{orderId}",
"headers": { "X-Tenant": "acme" },
"credential_uuid": "cred-abc12345-6789-0123-4567-89abcdef0123",
"parameters": [
{ "name": "orderId", "type": "string", "description": "Order ID", "required": true }
],
"timeout_ms": 5000,
"customMessages": ["Looking that up now...", "Still checking..."],
"customMessageType": "text"
}
}| Field | Type | Required | Notes |
|---|---|---|---|
method | string | ✓ | GET, POST, PUT, PATCH, DELETE. |
url | string | ✓ | Target URL. May contain {paramName} substitutions. |
headers | object | — | Static headers merged into every request. |
credential_uuid | string | — | Reference to an ExternalCredentialModel for auth (e.g. Bearer/OAuth tokens). |
parameters | array | — | What the LLM is allowed to pass. Each {name, type, description, required}. type is one of string, number, boolean. |
timeout_ms | int | — | Request timeout. Default 5000. |
customMessages | string[] (≤5) | — | Filler lines spoken before execution — ONE per invocation, picked per customMessagesOrder. [] = silent. |
customMessagesOrder | "random" / "in-order" | "random" | random = uniform pick among all variants; in-order = sequential within a call, wrapping around. |
customMessageType | "none" / "text" / "audio" | "none" | none = the tool executes silently. text requires ≥1 customMessages entry; audio requires customMessageRecordingId. Only the selected mode's payload is stored — the others are cleared. |
customMessageRecordingId | string | — | Audio recording UUID; required (and only kept) when customMessageType is "audio". |
Validation
methodand parametertypeare strict enums;timeout_msis1000..30000; a non-emptyurlmust behttp(s)://.- Creation (
POST /tools) accepts a placeholder-empty config (url: "", empty KBgroups, empty transferdestination) so the dashboard can store a blank tool for editing. Saving (PUT /tools/{uuid}) refuses incomplete definitions: an HTTP tool needs aurl, a transfer tool adestination, a KB tool at least one group —400with a cleardetailotherwise. definition.typemust always match the tool'scategory(fixed at creation).- Conditional requirements everywhere:
messageType/customMessageTypecustom/textneeds its message(s),audioneeds its recording id.
endCall
Lets the LLM explicitly end the call. Configurable goodbye message.
{
"schema_version": 1,
"type": "endCall",
"config": {
"messageType": "custom",
"customMessage": "Thanks for calling. Goodbye!"
}
}| Field | Type | Required | Notes |
|---|---|---|---|
messageType | "none" / "custom" / "audio" | — | Default "none". |
customMessage | string | — | When messageType="custom". |
audioRecordingId | string | — | When messageType="audio". |
transferCall
Warm or cold transfer the call to a phone number or SIP endpoint.
{
"schema_version": 1,
"type": "transferCall",
"config": {
"destination": "+14155551234",
"messageType": "custom",
"customMessage": "Transferring you now.",
"timeout": 30,
"recordTransfer": true
}
}| Field | Type | Required | Notes |
|---|---|---|---|
destination | string | ✓ | E.164 phone (+1...) or SIP endpoint (PJSIP/123, SIP/123). Validated by regex. Empty string allowed at create-time so the editor can save a draft. |
messageType | "none" / "custom" / "audio" | — | Default "none". |
customMessage | string | — | If messageType="custom". |
audioRecordingId | string | — | If messageType="audio". |
timeout | int (5..120) | — | Seconds to wait for the destination to answer. Default 30. |
recordTransfer | bool | — | Default true. Record the transferred conversation (caller ↔ destination) at the carrier and attach it to the call record. Ignored when full-call carrier recording is on — that file already covers the transfer. |
Transfers need a telephony transport
Every telephony provider zoxaAI supports accepts transferCall; web calls (WebRTC / WebSocket) cannot transfer. The LLM always gets the function, but the runner rejects the transfer if it can't proceed on the active transport — a web call, or a provider whose transfer credentials aren't configured.
calculator
Built-in math evaluator. Lets the LLM compute arithmetic without hallucinating arithmetic. No config.
{
"schema_version": 1,
"type": "calculator"
}query (knowledge base)
Retrieve from one or more named knowledge-base groups during the call.
{
"schema_version": 1,
"type": "query",
"config": {
"groups": [
{
"name": "billing",
"description": "Pricing, refunds, plan changes.",
"documentIds": ["doc-uuid-1", "doc-uuid-2"]
},
{
"name": "support",
"description": "Troubleshooting and how-to.",
"documentIds": ["doc-uuid-3"]
}
],
"messages": ["Let me check on that..."],
"timeout": 5
}
}| Field | Type | Required | Notes |
|---|---|---|---|
groups | array | ✓ | One or more named groups. The LLM picks which to query based on the description. |
groups[].name | string | ✓ | Short label. The LLM sees this as the function name. |
groups[].description | string | ✓ | Tells the LLM when to search this group. |
groups[].documentIds | array (≥1) | ✓ | Document UUIDs from GET /files. |
messages | string[] (≤5) | — | Filler lines spoken during retrieval — ONE per invocation, picked per messagesOrder. [] = silent. |
messagesOrder | "random" / "in-order" | "random" | random = uniform pick among all variants; in-order = sequential within a call, wrapping around. |
timeout | int (1..30) | — | Max seconds to wait for retrieval before reporting failure to the LLM. Default 5. |
Documents must already be processed (i.e. chunks indexed) before queries return useful results. See Files → upload.
testTool (inline only)
A simulated tool for exercising the full tool-calling flow — invocation, dead-air task sound, success/failure handling — without a real backend. Unlike the types above it is not a saved-tool category: it exists only as an inline object in an agent config's tools[] array.
The description drives when the LLM calls it; execution is a configurable delay followed by the configured outcome.
{
"type": "testTool",
"name": "order_lookup",
"description": "Call this whenever the user asks about their order status.",
"config": {
"delaySeconds": 6,
"outcome": "success",
"responseText": "Order #123 ships tomorrow.",
"messages": ["Let me pull that up.", "One moment, checking now."]
}
}| Field | Type | Required | Notes |
|---|---|---|---|
config.delaySeconds | number (0.5..30) | — | Simulated execution time. Default 6. |
config.outcome | "success" / "failure" | — | What the tool returns after the delay. Default success. |
config.responseText | string | — | Returned as the result payload (success) or error detail (failure). Blank = a built-in steering message the agent relays naturally. |
config.messages | string[] (≤5) | — | Filler lines spoken as the simulated execution starts — ONE per invocation, picked per messagesOrder, same as every other tool. [] = silent. |
config.messagesOrder | "random" / "in-order" | "random" | random = uniform pick among all variants; in-order = sequential within a call, wrapping around. |
Referencing tools from agents
An agent config's tools array — saved (POST /agents) or transient (POST /call) — accepts a mix of saved-tool UUID strings and inline tool objects. Inline objects use the canonical inline format, not the saved-tool format above. See Inline tool format for the exact shapes. Every config also carries an endCall tool — auto-seeded when you don't define one.
{
"type": "outbound",
"callConfig": { /* ... */ },
"toNumber": "+14155550199",
"agent": {
"name": "Support bot",
"systemPrompt": "...",
"llm": { "provider": "openai", "model": "gpt-5.4-mini" },
"tools": [
"abc12345-...",
{
"type": "endCall",
"name": "end_call",
"description": "End when the caller says goodbye.",
"config": { "messageType": "custom", "customMessages": ["Goodbye!"] }
}
]
}
}Transfer support
Every telephony provider supports transferCall. Web (browser) calls have no phone leg to transfer — there the runner rejects the transfer and the agent continues the call.