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_01HQT4VX3KQK4Z
Verify (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.entered

Wire a webhook.

Create a subscription in mock mode and watch deliveries against a local tunnel.