Webhooks overview
DialCru delivers events to HTTPS endpoints you configure. Deliveries follow the Standard Webhooks specification, so a generic verification library will work — and the signature algorithm is documented in full so you can implement it in twenty lines if you would rather not add a dependency.
- Events — what you can subscribe to, and what each payload carries
- Signatures — verifying a delivery is genuinely from us
- Delivery — headers, retries, timeouts, idempotency
- Templates — reshaping the body to match your intake
The model
An endpoint is a URL you own, plus the configuration around it. You create one in the dashboard under Manage → Webhooks, or through the API — see webhook endpoints.
An endpoint subscribes to exactly one event type. To receive two event types, create two endpoints. This keeps the payload shape, the template and the filter one-to-one with the event — your handler never has to branch on event.type to know what fields exist.
Within that event type you may narrow further with a subtype filter — for example, only call.completed events whose status is missed, or only call_outcome.submitted events whose disposition is interested. See Events.
When something happens, one event fans out to a delivery per matching endpoint. Each delivery is attempted, retried on its own schedule, and recorded in the endpoint's delivery log.
What your endpoint must do
Respond 2xx quickly. You have 10 seconds per attempt. Anything else — a 4xx, a 5xx, a timeout, a connection failure — is a failure and will be retried.
Do the work afterwards. Acknowledge the delivery, then process it out of band. A handler that writes to your CRM synchronously will eventually exceed 10 seconds and force a retry of work you already did.
Verify the signature before trusting the body. See Signatures. The URL is the only secret otherwise, and a URL is not a secret.
Deduplicate. Delivery is at-least-once. See Delivery.
Do not assume ordering. Events are not ordered. Use the x-event-time header, or event.timestamp in the body, to order a stream yourself.
URL requirements
- HTTPS. Plain HTTP is refused.
- Publicly resolvable. The hostname must resolve to a public address.
localhost, loopback, link-local, and private ranges are refused at configuration time — this is an SSRF control, not a convenience check. Use a tunnelling service to develop against a local receiver. - At most 2 048 characters.
Secrets
Each endpoint gets its own signing secret: 32 random bytes, base64, prefixed whsec_. The prefix makes a leaked secret greppable in your codebase and recognisable to secret scanners.
Rotating a secret starts a 24-hour grace period during which deliveries are signed with both the old and the new secret, so you can redeploy when it suits you. See Signatures.
When we stop sending
An endpoint is switched off automatically in two cases, and the delivery log records which:
| Reason | Meaning |
|---|---|
gone | Your endpoint answered 410 Gone. We take that as intentional. |
failures | 20 consecutive failures — roughly two full retry ladders. |
manual | Somebody turned it off in the dashboard. |
Any success resets the failure counter. The counter is per endpoint, not per event, so it measures "this URL is broken" rather than "this event is unlucky".
Answer 410 Gone when you decommission a receiver. It is the polite way to stop the stream immediately instead of burning two ladders' worth of retries.
Disabling is not pausing. A disabled endpoint's queued deliveries are dropped, not held. Re-enabling starts from new events only.
Payload size
A delivery body is capped at 64 KB. Exceeding it is our bug, not yours — it means an event carried something it should not — and the delivery fails rather than sending a truncated body.
Reference: WEBHOOKS in src/lib/const/webhook.const.ts.