Webhooks
Signed events.
Delivered straight.
Dials emits every meaningful event over HMAC-signed HTTP. Subscribe to a topic, verify the signature, and respond 2xx — or watch retries land on a dead-letter queue.
At a glance
What every webhook carries.
Signature
HMAC-SHA256
Per-subscription secret. Headers include timestamp and signature.
Replay
300s window
Reject deliveries with a timestamp drift over five minutes.
Retries
Exponential
30s, 2m, 10m, 1h, 6h, 24h. DLQ on final failure.
DLQ
Inspectable
Per-subscription DLQ readable via API and exportable as JSON.
Verify
Five lines.
Verified delivery.
Build the signed string from the timestamp and the body. Compare against the header. The SDK ships with a `verify(req, secret)` that does it for you, but the math is intentionally simple.
- Per-subscription secret
- Timestamp + body signed
- Constant-time compare
- Per-event idempotency key
Headers
Dials-Signature: t=1716301200,v1=2cb0e3b4d3a5…
Dials-Event: calls.dial.ok
Dials-Delivery: whe_01HQT4VX3KQK4Z
Dials-Idemp: evt_01HQT4VX3KQK4ZVerify (TypeScript)
import { verifyWebhook } from "@dials/sdk";
export async function POST(req: Request) {
const event = await verifyWebhook(req, process.env.DIALS_WEBHOOK_SECRET!);
switch (event.type) {
case "calls.dial.ok":
// record
break;
case "messages.send.ok":
// record
break;
}
return new Response("ok");
}Topics
Subscribe to
only what you care about.
Subscribe per topic, per seat, per number, per region. Topics are stable. New event versions ship under additive fields and an explicit `version` key — never a silent shape change.
Topics
calls.dial.ok
calls.dial.failed
calls.inbound.answered
calls.recording.stored
messages.send.ok
messages.send.failed
messages.opt_out
numbers.searched
numbers.held
seats.created
seats.updated
routing.policy.changed
usage.event
webhooks.dlq.enteredWire a webhook.
Create a subscription in mock mode and watch deliveries against a local tunnel.