Registered webhooks also get
call.completed when a web session ends. In that payload, call_sid and to_number are null. Use conversation_id to identify the session. The per-call webhook_url field exists only on phone calls and batches.Add a webhook
You can add a webhook in two ways.- For every call
- For one call
Register a URL once. It gets every matching event in your workspace until you delete it.
- If you leave out
events, the URL gets both events. - If you register the same URL again, you get the existing webhook back. No duplicate is created.
- A workspace can have up to 20 webhooks.
webhook_url, it gets the event only once.
Payloads
- call.completed
- batch.completed
transcript, role is model for the agent and user for the person. timestamp is a Unix time in seconds. If the agent called any tools, the payload also has a tool_calls list.Verify the signature
Every webhook has anX-Webhook-Signature header. It is an HMAC-SHA256 of the raw request body, hex-encoded, signed with your workspace’s webhook secret.
1
Get your secret
{"webhook_secret": "..."}. You can fetch it again at any time.2
Sign the raw body
Compute the HMAC over the exact bytes you received. Do not parse the JSON and convert it back to a string first. That can change the bytes and break the check.
3
Compare in constant time
Use a constant-time compare, such as
hmac.compare_digest or crypto.timingSafeEqual. A normal == can leak timing information.Delivery
Know these rules before you depend on webhooks:- One attempt only. If your server is down or returns an error, the webhook is not sent again. Use
GET /api/v1/calls/{call_id}to fill any gaps. - 10 second timeout. Reply quickly. Do slow work after you respond.
- Redirects are not followed. Give the final URL.
- It can arrive unsigned. In rare cases the secret cannot be loaded, and the webhook is sent without a signature. Reject any request with a missing or wrong signature.
Manage webhooks
See the Webhooks API reference for full schemas.