call.ended the moment a session reaches a terminal state, call.analyzed when post-call analysis finishes. Use them to write results into your CRM, ticketing system, or data warehouse without polling the sessions API.
Events
For a given session,
call.ended is always delivered before call.analyzed. call.analyzed is sent only when analysis actually completes — calls where analysis is skipped (no user speech or analysis disabled) or fails produce call.ended only.
Transcripts are deliberately excluded from webhook payloads — fetch them on
demand with
GET /v1/agent/sessions/{session_id}. See conversation
history.Configure the endpoint
Webhooks live in thewebhooks section of your agent’s configuration. post_call is a list, so one agent can fan events out to several systems at once. Set them in the console on your agent’s Webhooks page, or via the config API:
null or [] to stop deliveries. A single post_call object is still accepted and is treated as a one-element list.
Webhook settings are part of the agent configuration, so they follow the
draft-and-publish flow — publish for
changes to apply to production calls.
Payload
Every payload carries theevent name and a session object with a fixed set of session facts. Note the field names differ from the sessions API, which uses session_id / started_at / ended_at for the same facts. call.ended adds ended_reason; call.analyzed adds the analysis entity:
ended_reason is hangup for a call that terminated normally and error when the session failed. New values may be added as richer end causes ship — treat unrecognized values as informational rather than rejecting the event.
end_user_id and metadata are echoed exactly as you set them when creating the session — use them to correlate the event with records in your own system. branch_id is an internal configuration-lineage identifier — safe to ignore. What lands in summary, data, and criteria_results is defined by your analysis configuration.
Verify the signature
When an entry has asecret, every request to that endpoint carries a signature header with the send time and an HMAC-SHA256 of {timestamp}.{raw_body}, keyed with that entry’s own secret:
t is the Unix time (seconds) the request was sent — each retry is signed fresh. v1 is the hex HMAC. Verify in three steps:
- Parse
tandv1from the header. - Recompute HMAC-SHA256 over the string
{t}.followed by the raw request body bytes — before any JSON parsing or re-serialization — and compare againstv1in constant time. - Reject requests whose
tis more than 5 minutes from your clock. Because the timestamp is inside the MAC, a replayed capture can’t be refreshed.
Future scheme revisions would ship under a new element (
v2=…) alongside v1
— parse the elements you know and ignore the rest, as the snippets above do.Delivery semantics
Endpoints are delivered in parallel and independently: each gets its own attempts, its own retry budget, and its own signature keyed with its own secret. An endpoint that is down and exhausts all three attempts has no effect on the others.
Respond with a
2xx status within the timeout; a 500 response or a timed-out request counts as a failed attempt. Acknowledge first and process asynchronously — slow handlers burn their own retry budget.
Idempotency. At-least-once delivery means the same event can arrive more than once. Each session produces at most one call.ended and one call.analyzed per endpoint, so dedupe on the pair (event, session.id) before writing to downstream systems.
Preview calls made from the Builder never
trigger webhooks — debugging sessions don’t reach your production endpoint. To
test end to end, run a real session against your published agent.
Auto-ticket unresolved calls
call.analyzed closes the loop on conversations the agent couldn’t: judge every call with a success criterion, and open a ticket in your helpdesk whenever the verdict isn’t success — no mid-call decision, no dashboard watching.
First give the agent’s analysis configuration a criterion that captures resolution:
analysis.criteria
webhooks.post_call (see Configure the endpoint) and act on the verdict:
verifyWebhook is the function from Verify the signature; openTicket stands in for your helpdesk’s API. Escalating on anything but success includes unknown verdicts — the model couldn’t judge the call, which usually deserves human eyes too. Tighten the check to failure only if unknowns prove noisy.
Edges worth handling:
- Calls with nothing to analyze never produce
call.analyzed(see Events). If every call must reach the helpdesk regardless of analysis, watchcall.endedtoo. - Payloads carry no transcript. To include one in the ticket, fetch
GET /v1/agent/sessions/{session_id}from your handler — see conversation history. - Set
end_user_idandmetadatawhen creating sessions so tickets attach to the right customer record without a lookup.
Post-call ticketing is silent — the caller has already hung up. When the
caller should leave the call holding a ticket number, have the agent open it
mid-call with a webhook
tool, and keep
this recipe as the safety net behind it.
Going further
Post-call analysis
Define the summary, data fields, and criteria that
call.analyzed delivers.Conversation history
Fetch the transcript, tool timeline, and recordings by
session_id.Versions & publishing
How configuration changes — including webhooks — go live.
Agent configuration
The full config schema behind
PATCH /v1/agent/agents/{agent_id}/config.
