delivery field:
- App message (default) - Tavus emits a
conversation.tool_callevent (orconversation.perception_tool_callfor perception tools) on the data channel; your frontend handles it. - API call - Tavus makes an HTTPS request to a URL you configure, either a customer-controlled callback or a third-party API directly. Auth + body shape is configured per tool.
App message delivery
The default. Your client receives the tool call inline with the conversation events.App message delivery (default)
conversation.tool_call event carrying a tool_call_id. To return a result, send a conversation.tool_result event back with the matching tool_call_id - that’s how Tavus pairs the result with the in-flight dispatch and applies the configured on_resolve.
conversation.tool_result (sent by your client)
If your client never sends a result, the dispatch eventually drops out of context. There’s no hard timeout on this path - the PAL just won’t have the data.
API delivery
Tavus calls an HTTPS endpoint each time the tool fires. The exact request shape depends on whether you’re hitting a third-party API directly (anyauth.type other than hmac) or a customer-controlled callback (auth.type: hmac).
API delivery
delivery.api fields
URL templating
{placeholders} in the URL path or query string are filled at request time from the LLM’s tool arguments and URL-encoded. The hostname must be static.
Reserved system placeholders
In addition to the LLM’s tool arguments, Tavus injects a small set of system placeholders at request time. They’re available everywhere placeholders are interpolated - URL,query_params values, and body_template. Useful for idempotency keys, correlation IDs in your logs, and Tavus-side tracing.
Request body and query string
Only arguments declared in your tool’sparameters.properties ride along. Body and query string are decided independently - you can combine them in any way.
Body
The HTTP method decides whether a body is sent.GET / HEAD / DELETE never carry a body - body_template on those methods is rejected at validation time.
Query string
query_params applies to every method - you can attach a query string to a POST as easily as to a GET.
Example - reshape flat tool args into a nested API body
Suppose the tool’sparameters schema defines two flat fields:
Tool parameters (what the LLM emits)
query.text and filters.region. Use body_template to remap:
delivery.api with body_template
{ "search_term": "pizza", "region": "tokyo" }, Tavus sends:
body_template is a regular JSON object - write it as one, no escaping. Each {placeholder} inside a string value is replaced with the matching LLM argument. If a string is exactly one placeholder ("{count}"), the substituted value preserves its native type - so "{count}" with count: 10 produces 10 (number), not "10" (string). Non-string values in the template (numbers, bools, null) pass through unchanged.
Tavus callback shape
Used whenauth.type: hmac is set. Tavus ignores body_template / query_params / URL templating in this mode and sends a fixed JSON envelope, signed with HMAC-SHA256.
Request body (POST/PUT/PATCH)
Request headers Tavus always sets in this mode:
Any extra
delivery.api.headers you configured are merged in.
Response Tavus expects
The same response handling applies to both API modes:
When the status is
error or timeout and on_resolve is not fire_and_forget, the PAL acknowledges the failure to the user (no result content is fed back to the LLM).
Verifying API signatures
When you setauth.type: "hmac" with an auth.secret, Tavus signs the exact request body bytes with HMAC-SHA256 and sends the hex digest in X-Tavus-Signature. Verify it before trusting the payload.
The signing input is the raw body Tavus sends, which is canonical JSON with keys sorted alphabetically. Verify against the raw bytes you received, not a re-serialized version - any re-encoding can change byte order and break the signature.
Python (Flask)
Node.js (Express)
Replace
<api-key> with your actual API key. You can generate one in the PAL Maker.
