VocalDocscontracts 1.65.0

Tools and the events channel

A Tool is a function the model may call during a Call. An http Tool is a signed request Vocal sends to your endpoint; a function Tool is handled by your server over the Call's events channel, a WebSocket that also carries the Call's events as they happen and takes control commands.

On the Phase 0 host. Served: Tools, the events channel and the control route since contracts 1.49.0, the transfer command since 1.50.0. The routes for Tools from integrations (mcp) are served since 1.60.0, and the integrations service that runs them is not deployed; see Integrations.

Create a Tool and attach it

POST /v1/tools creates a Tool with its name, its description for the model, its parameters as a JSON Schema and its type. An http Tool also names its request, with an auth header that is write-only. Attach Tools to an Agent with tool_ids on the draft; publishing freezes a copy of each into the Agent version, and every model of the Agent must declare tool calling. type: "mcp" is refused by this route.

POST /v1/tools/{tool_id}/test sends one real, signed request of an http Tool and answers its status and timings; a function Tool needs a live Call and cannot be tested this way.

Verify an http Tool request

Every request goes out through the egress guard, which refuses private and internal addresses, and carries these headers:

HeaderValue
Vocal-Signaturet=<unix seconds>,v1=<hex>: an HMAC-SHA256 over the timestamp, a period and the raw body, keyed by the UTF-8 bytes of the Organization's tool signing secret
Vocal-Call-IdThe Call the request belongs to
Vocal-Tool-Call-IdThe one tool call
Idempotency-KeyFor a Tool marked idempotent, derived from the two ids

The api mints the signing secret when an Organization first needs it and never shows that one. POST /v1/tool-signing-secret/rotate mints a new one and answers it once, so rotate once to obtain a secret. The previous secret keeps signing for 86,400 seconds by default, and a request in that window carries two v1 values, the new one first; accept either.

Open the events channel

GET /v1/calls/{call_id}/stream upgrades to a WebSocket. Each server message is a StreamServerMessage with a seq; each client message is a StreamClientMessage.

  • include=transcripts adds the transcript messages and needs transcripts:read.
  • include=tool_calls makes the connection a function Tool handler: it receives tool.invoked with the arguments and answers with tool.result. While no handler is connected, a function Tool returns no_handler.
  • Naming either makes opening the connection an audited content read.
  • Reconnect with after_seq to resume within the retained window, the last 1,000 messages kept until 10 minutes after the Call ends (targets); past it, read the Call's events.
  • Up to three connections per Call (a target); a fourth answers 429 stream_limit_reached.

Send a control command

POST /v1/calls/{call_id}/control sends one command to the live Call: say, transfer, end or inject_context, the same commands the events channel takes. It answers 202 with the command id once the Call acknowledges it, within 2 seconds (a target), or 503 session_unreachable; a Call that is not live answers 409 call_not_active. A command passes through the same Policies as model output, and its outcome arrives as command.completed on the channel. transfer is a cold transfer to a destination the Agent version names and the Organization's transfer allowlist holds; any other is refused with 403 transfer_refused.