POST to that URL whenever one fires. For phone calls, the payload includes ready-to-download recording and transcript URLs.
https://agents.assemblyai.com. Send your API key in the Authorization header (a Bearer prefix is accepted), same as the rest of the API.
Set up a webhook handler
The reference section below covers every field and rule.1
Generate a signing secret
Every delivery is signed with a secret you choose. Generate one, store it somewhere your handler can read it, and keep it. The API never returns it.It must be 32 to 256 printable ASCII characters with no whitespace.
2
Write the handler
Your handler does two things: verify the signature, then acknowledge with a Run it:
2xx as fast as possible. Do any slow work after responding.uvicorn receiver:app --port 8000 or node receiver.mjs.3
Expose it over HTTPS
The subscription URL must be Copy the
https:// and publicly reachable; it’s checked when you create the subscription. For local development, put ngrok in front of your handler:https://….ngrok-free.app URL it prints. In production, use your own HTTPS host.4
Create the subscription
Register your URL, the events you want, and the secret from step 1. Pass Save the
agent_id to receive events for one agent, or leave it out for every agent on your account.id from the response. You need it to update or delete the subscription later.5
Trigger an event and watch it arrive
Call your agent’s phone number, or start a WebSocket session against it, then end it. Within a few seconds your handler logs the delivery.If nothing arrives, check the delivery history. Every attempt is recorded against the session (for a call, the session id is the A
call_id):status of failed with "error": "HTTP 401: invalid signature" means your handler rejected it, usually because of the raw-bytes problem from step 2 or a secret mismatch. See Troubleshooting for the rest.6
Use the event
What you do next depends on the channel:
- Phone calls:
call.endedincludesrecording_urlandtranscript_url. They’re presigned, work immediately, and expire one hour after the event, so download the files rather than storing the URLs.call.connectedhas both set tonull. - WebSocket sessions:
session.completedfires when the session ends, before the recording and transcript are written (s3_prefixisnullin the payload). They typically appear within about 90 seconds. Fetch them withGET /v1/sessions/{session_id}from the Sessions API, polling every few seconds untilartifactsis non-empty.
Before you go to production
- Deduplicate on
event_id. Delivery is at-least-once, and the same event can arrive more than once.event_idis stable across retries;X-AAI-Delivery-Idchanges per attempt, so don’t dedupe on it. - Respond within 15 seconds. Return
2xxfirst, do slow work after. A slow handler gets retried, which means duplicates. - Know what isn’t retried. Any
4xxother than408,425, and429is treated as your endpoint rejecting the event and is not retried.5xx, timeouts, and connection errors are. - Don’t rely on ordering.
call.connectedis not guaranteed to arrive beforecall.ended. - Register the final URL. Redirects are not followed; a
301counts as a failure. - Rotate secrets with
PATCH. Sending a newsecretbumpssecret_version. Deliveries already in flight were signed with the old one, so accept both during the changeover.
Reference
Events
Delivery headers
Signature
t: Unix timestamp in seconds when the delivery was signed.v1: hexHMAC-SHA256, keyed with your subscription secret, over the string"{t}."followed by the raw request body bytes.
v1, compare in constant time, and reject any delivery whose t is more than 300 seconds from your clock. The timestamp is what stops a captured request being replayed.
Payloads
Session events carry asession object:
session.started has the same shape with "status": "created" and duration_seconds, ended_at, and public_close_reason set to null.
Call events carry a call object:
call_id and session_id are the same value. from_number is the caller’s real number, so treat the payload as personal data.
Subscription fields
Manage subscriptions
PATCH accepts any of url, events, secret, and enabled.
Delivery history
status (pending, delivered, or failed), attempt_count, http_status_code, error, delivered_at, and next_retry_at. Deleting a subscription removes its entries.