POST requests to your server when things
happen during a call — an inbound call starts, a call ends, a grading
run completes, an alert fires, and so on. There are two delivery
models:
Webhook endpoints (recommended)
Multiple URLs, per-endpoint secrets, per-endpoint event filters,
and automatic retries.
Manage via
GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints.Single-URL legacy webhook
One URL per org. Carries the call-lifecycle events, including the
blocking configuration exchanges. Managed at
GET/PUT /v1/webhook.telephony.incoming, telephony.complete, telephony.tool,
web.incoming, web.complete, web.tool) are also sent to the
legacy single-URL webhook — if you have both a legacy URL and a
matching endpoint, you receive the event on both paths. Blocking
behavior (the telephony.incoming / web.incoming configuration
exchange and webhook-mode
tool dispatch) lives
exclusively on the legacy path; every endpoint delivery is a
fire-and-forget notification.
Payload format
Endpoint deliveries are a JSON object withdata, event_id, and
type:
event_id is unique per emitted event. It is identical across retries
and across every endpoint that receives the event — dedup on it.
The legacy single-URL webhook sends the same type and data but
without event_id:
Signature verification
Every request carries an HMAC-SHA256 signature over the raw request body in theX-ThunderPhone-Signature header. The signing key is the
endpoint’s secret (or your org-level webhook secret for legacy
deliveries).
Steps
- Read the raw request body before any parsing.
- Compute
hmac_sha256(secret, body).hexdigest(). - Compare in constant time to the
X-ThunderPhone-Signatureheader.
Delivery semantics
These semantics apply to endpoint deliveries. The legacy single-URL webhook is a single synchronous attempt with no retries.Retries
Retries
Each event is attempted once immediately. Any
2xx response
acknowledges the delivery. On any other outcome (non-2xx,
connection error, timeout) we retry at 1 m, 5 m, 30 m, 2 h, 6 h,
12 h, and 24 h after the first attempt — 8 attempts spanning
24 hours. If every attempt fails, delivery stops and the endpoint
is marked status="failing" in
webhook endpoints. Return 2xx as soon as
the payload is durably accepted; process asynchronously.Ordering
Ordering
Delivery ordering is best-effort. In practice we deliver in the
order events are emitted, but retries can reorder on failure.
Always dedup and reconcile by
call_id / object id.Duplicates
Duplicates
Delivery is at-least-once: a retry after a response we never
saw can duplicate an event. Every retry carries the same
event_id, so store processed ids and skip repeats. event_id is
also shared across endpoints — two endpoints subscribed to the
same event receive the same event_id.Timeouts
Timeouts
Endpoint deliveries have a 30 s timeout per attempt. On the
legacy path, blocking requests that drive live call behavior — the
telephony.incoming / web.incoming
configuration exchange — time out after 10 s, but a slow
response delays call pickup, so aim to answer within a couple of
seconds. Webhook-mode tool dispatch allows 20 s.Source IPs
Source IPs
Outbound webhooks originate from ThunderPhone’s cloud IP range.
If your firewall requires an allowlist, contact support and we’ll
share the current ranges.
Choosing between legacy and endpoint-based webhooks
New integrations should consume events through endpoint-based
webhooks. Keep (or add) a legacy URL only if you configure calls
dynamically at pickup time or use webhook-mode tool dispatch — those
request/response exchanges only run on the legacy path.
Related
Events catalog
All event types and their payloads.
Webhook endpoints
Manage multiple endpoints, event filters, and secrets.
telephony.incoming / web.incoming
The blocking request your server must answer to configure calls.
telephony.complete / web.complete
Post-call payload with transcript, recording, and metrics.