source: "webhook", with webhook_id + delivery_id in the job metadata).
This surface is inbound-only. Outbound webhooks — workflow tasks calling external URLs — are a separate future REST catalog action, not receiver machinery.
The webhook object
| Field | Type | Description |
|---|---|---|
id | string | 128-bit opaque hex identifier — the receive URL is unguessable by construction. |
definition_id | string | Definition to trigger. Must belong to your org and be re-validated as active at dispatch time. |
org_id | string | Owning organization. |
description | string | Human-readable description (1–200 chars). |
mode | string | trigger (each delivery starts a new job). resume is reserved and rejected with 422. Immutable after create. |
provider | string | Signature-verification adapter. Only standard (Standard Webhooks HMAC-SHA256) is implemented; stripe/five9/custom are rejected with 422. Immutable after create. |
input_template | object | null | Optional {{ }}-expression mapping from the normalized payload context to the definition’s input_data. Omit to pass the whole normalized payload through. |
dispatch_retry | object | null | Admin-authored dispatch retry policy, default OFF (one attempt), e.g. {"max_attempts": 3, "backoff": 5}. Never read from the sender’s body. |
rate_limit | integer | null | Per-webhook deliveries/minute cap. Default 1000. |
enabled | boolean | false makes the receive URL answer 404. |
signing_secret | string | Show-once. Returned only on create and rotate — never retrievable from any GET. Store it in the sending system immediately. |
receive_url | string | Show-once relative path. Prepend https://api.getdialed.ai/flows before handing it to a sender. |
The receive URL
The receiver path isPOST /webhooks/{slug}/{webhook_id}. In production the Flows API mounts under the /flows ingress prefix, so the URL external senders must call is:
receive_url returned at create time is a relative path (no host, no /flows prefix) — prepend https://api.getdialed.ai/flows when configuring your sender. The {slug} identifies your tenant; the 128-bit {webhook_id} is unguessable.
Signature verification
Senders include three Standard Webhooks headers on every delivery:| Header | Description |
|---|---|
webhook-id | Sender-chosen unique delivery ID. This is the idempotency key — re-sending the same value is safe. |
webhook-timestamp | Unix seconds. Deliveries outside the 5-minute replay window are rejected with 401. |
webhook-signature | v1,<base64 HMAC-SHA256> computed over {webhook-id}.{webhook-timestamp}.{raw body} with the signing secret. |
401 message is deliberately generic — a missing header, an expired timestamp, a tampered body, and a wrong secret all return the same {"detail": "Signature verification failed"}, revealing nothing to a probing caller. Existence failures (unknown slug, unknown webhook_id, or a disabled webhook) always return 404, never 401.
Delivery semantics
- 202-async. The receiver acknowledges within ~50ms with
202 Accepted; job creation and dispatch happen asynchronously after the ack. Check the deliveries view for the outcome. - Deduplication. Exactly one job per
webhook-id. A replayed delivery returns202but does not dispatch again — sender retry storms collapse to one job. - Rate limit. Each webhook accepts up to
rate_limitdeliveries/minute (default 1000/min), counted perwebhook_id(not per source IP, so senders sharing egress IPs don’t throttle each other). Over-limit deliveries get429. - Body format.
Content-Typedrives normalization — JSON is parsed, form-urlencoded becomes a dict, XML is parsed with a hardened parser, anything else is wrapped as{"raw": ..., "content_type": ...}. The receiver never rejects on format.
Managing webhooks
All management endpoints require authentication and operate only on your organization’s webhooks. Mutating endpoints require an admin-role caller (anX-API-Key is admin-equivalent).
| Method | Endpoint | Purpose |
|---|---|---|
POST | /webhooks | Create a config and mint its signing secret. Returns 201 with the show-once signing_secret + receive_url. |
GET | /webhooks | List your org’s webhooks with cursor pagination. The secret never appears on the read model. |
GET | /webhooks/{webhook_id} | Fetch one config (no secret, no receive URL). |
PATCH | /webhooks/{webhook_id} | Partial update — rebind definition_id, edit description/input_template/dispatch_retry/rate_limit/enabled. mode/provider are immutable. |
POST | /webhooks/{webhook_id}/rotate-secret | Rotate the signing secret with a 24-hour previous-secret overlap. Returns the new secret once. |
DELETE | /webhooks/{webhook_id} | Delete the config and scrub its stored signing secret. Returns 204. |
GET | /webhooks/{webhook_id}/deliveries | Recent deliveries — the debugging view for “did my sender’s POST arrive and start a job?”. |
POST | /webhooks/{slug}/{webhook_id} | The receiver — called by your external system, HMAC-signed, no API key. Returns 202. |
Next steps
Receive webhooks
A step-by-step walkthrough: create a webhook, sign a delivery, and confirm the job.
Event subscriptions
Trigger definitions from internal business events instead of external POSTs.