Delivery
VoiceInfra sends an HTTPPOST to the URL configured on your Webhooks page when a call ends.
Respond quickly, and move slow work off the request path. A handler that takes longer than the timeout counts as a failure and gets retried.
Events
There are two.call_failed covers a specific set of outcomes: an error, a concurrency limit, insufficient credit, a missing agent, a session error, an unanswered call, a rejected call, a SIP trunk failure, or a lost connection. Calls logged as rejected-call are always call_failed, whatever else they report.
Both events use the same payload shape. Branch on the
event field rather than assuming one.Request headers
Payload
Every payload has the same envelope: identifying fields at the top level, and the call itself underdata.
Envelope
string
call_completed or call_failed.string
A unique ID for this delivery. Use it to deduplicate if you receive the same event twice.
string
ISO 8601 timestamp for when the event was raised.
string
The workspace the call belongs to.
data
string | null
The call log entry for this call. Use it to fetch the full record from the portal or the call-log endpoints.
string | null
The agent that handled the call.
string | null
That agent’s name, so you do not have to look it up.
string
web-call, sip-inbound-call, sip-outbound-call, chat-session, or rejected-call.integer | null
Call duration in seconds.
string | null
The caller’s number.
string | null
The number that was called.
string
A human-readable reason the call ended — for example “Caller hung up” or “Maximum duration reached”.
string
When the call started. Note this sits inside
data and is separate from the envelope’s timestamp, which is when the event fired.array | null
The conversation transcript.
object | null
The AI-generated summary and metadata.
null when analysis is off or the agent is privacy-protected.object | null
Data the agent extracted during the call, following the schema you defined on the agent.
string | null
A signed URL for the recording, valid for one hour.
null when recording is disabled.object | null
The cost breakdown for the call.
object | null
Pre-call form values, or the per-contact variables passed for an outbound call.
string | null
Set when the call came from a batch campaign.
Example
call_completed
call_recording_url expires one hour after it is generated. If you need to keep the recording, download it when the webhook arrives rather than storing the URL.Retries
Verifying the signature
When your workspace has a webhook secret, every delivery is signed with HMAC-SHA256 over the raw request body, and the signature is sent inX-VoiceInfra-Signature as a hex string.
To verify:
- Read the raw request body as bytes, before any JSON parsing.
- Compute
HMAC-SHA256(raw_body, your_webhook_secret)and hex-encode it. - Compare with the header using a constant-time comparison.
- Reject with
401if they do not match.
Python
Node.js
Delivery logs
Your Webhooks page lists recent deliveries with the time, the event, the HTTP status your endpoint returned, and the call it relates to. Open any delivery for the full detail, including the error message when a delivery failed. Use the logs to diagnose failures and to check your signature verification — a delivery returning401 is almost always a verification bug on your side, and because 4xx is not retried, those events are gone.