Webhooks
A webhook subscription delivers your account’s events to an HTTPS endpoint you control. Register a URL and the event types you care about, and Dial POSTs each matching event to that URL — signed, retried, and idempotency-keyed.
Webhooks are the at-least-once, off-machine counterpart to the presence-based
event stream: instead of holding a connection open with
dial wait-for, your server receives events as they happen.
Create a subscription
Create one from the dashboard Webhooks page, or via the API:
targetUrlmust be HTTPS and must not point at a private, loopback, or link-local address.eventTypesis either["*"](all events) or an explicit list of event types, e.g.["message.received", "call.ended"].
The response includes a signing secret (whsec_…). The dashboard masks it and lets you
copy it on demand; over the API the full secret is returned at creation and via
GET /api/v1/webhooks/{id}/secret.
Send different numbers to different endpoints
By default a webhook receives events for every number on your account. To send some
numbers’ events to a different endpoint, limit a webhook to specific numbers with
phoneNumberIds, using the id of each number from GET /api/v1/numbers:
Change the numbers later with PATCH /api/v1/webhooks/{id}. You can send any of
targetUrl, eventTypes, and phoneNumberIds; the signing secret stays the same:
Three rules to know:
- Omitted means all numbers. Leave
phoneNumberIdsout (or sendnull) and the webhook covers every number on the account, including numbers you add later. On update,nullswitches a limited webhook back to all numbers, and leaving the field out keeps it as it is. - An empty list is rejected.
"phoneNumberIds": []returns400, as does an id that isn’t one of your active numbers. To cover every number, omit the field instead. - Released numbers never widen a webhook. If you release every number a webhook was
limited to, it stays limited: it receives nothing, and
phoneNumberIdsreads back as[], until you edit it. It does not fall back to all numbers.
Delivery format
Each delivery is an HTTP POST to your targetUrl with the event JSON as the body and these headers:
The body is the same event envelope you’d get from the
event stream, plus a top-level id field equal to X-Dial-Event-ID.
Verify the signature
The signature protects against forged and replayed deliveries. Recompute the HMAC over
{timestamp}.{raw_request_body} with your subscription secret and compare:
Retries
Dial considers a delivery successful on any HTTP 2xx. Anything else — a non-2xx
status, a connection error, or a timeout (10s per attempt) — is retried with exponential
backoff, up to 6 attempts total (1 initial + 5 retries). Because deliveries retry,
your endpoint must be idempotent: dedupe on X-Dial-Event-ID.
Test with a ping
The dashboard’s Fire ping button (and POST /api/v1/webhooks/{id}/ping) sends a
webhook.ping event to that subscription, bypassing
its eventTypes filter. When the ping is delivered and your endpoint returns 2xx, the
dashboard records the time as Last ping. Use it to confirm a new endpoint is reachable
and verifying signatures correctly.