Generic Webhooks
Dispatch works with any service that can send or receive HTTP webhooks — no specific integration required.
Receiving Webhooks (Source)
Any service that supports outgoing webhooks can send events to Dispatch. Create a source endpoint and point the external service at your webhook URL:
POST https://your-api.dispatch.tech/hooks/<slug>
Authentication
Choose one of two methods when configuring the external service:
HMAC signature — most webhook senders support signing their requests. Set the signing secret on both the external service and your Dispatch endpoint. Dispatch accepts signatures in either of these headers:
X-Hub-Signature-256: sha256=<hex-hmac>
X-Dispatch-Signature: sha256=<hex-hmac>
API key — if the external service can set custom headers, use a project API key:
Authorization: Bearer dsp_your-api-key
Event Type Extraction
If the sending service doesn't include an event type header, set the Event Type Path on the endpoint to a dot-notation path into the JSON body (e.g., action, event.type, or data.object). Dispatch will extract the event type from that field automatically.
Example
BODY='{"action":"opened","issue":{"title":"Bug report"}}'
SIG=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "your-signing-secret" | cut -d' ' -f2)
curl -X POST https://your-api.dispatch.tech/hooks/my-slug \
-H "Content-Type: application/json" \
-H "X-Dispatch-Signature: sha256=$SIG" \
-d "$BODY"
Dispatch responds with 202 Accepted and an event_id once the event is queued.
Forwarding Webhooks (Destination)
Create a Webhook destination to forward events to any HTTP endpoint. Dispatch will POST the event payload to the target URL.
Creating a Webhook Destination
- Navigate to Destinations → Create Destination
- Enter a name
- Select Webhook as the type
- Enter the target URL
- Optionally filter by event types
- Click Create
What Gets Sent
The raw event payload is forwarded as a JSON POST request. If a transform is configured on the link, the transformed payload is sent instead. Templates are not applied to webhook destinations — the payload (original or transformed) is always forwarded as-is.
Dispatch also injects headers identifying the event:
X-Dispatch-Event-ID: <event-uuid>
X-Dispatch-Endpoint-ID: <endpoint-uuid>
The prefix (dispatch) can be customized per project via the Header Prefix setting.
Signing Outbound Deliveries
Webhook destinations can sign every outbound POST with HMAC-SHA256 so your receiver can verify Dispatch as the origin (the same model Dispatch uses to verify inbound webhooks from GitHub, Stripe, Slack, etc.).
When a signing secret is set on a webhook destination, every delivery carries two additional headers:
X-Dispatch-Timestamp: <unix-seconds>
X-Dispatch-Signature: sha256=<hex>
The signature is computed as:
HMAC-SHA256(secret, "<timestamp>.<raw-body>")
The timestamp is included inside the HMAC so receivers can also reject signatures whose timestamp is outside a tolerance window — defending against replay attacks.
Verifying a Delivery
import crypto from "node:crypto";
function verify(secret, timestamp, body, signatureHeader) {
// Reject stale timestamps (5 minute window).
const skew = Math.abs(Date.now() / 1000 - Number(timestamp));
if (skew > 5 * 60) return false;
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${body}`)
.digest("hex");
// Constant-time compare to avoid timing leaks.
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected),
);
}
Managing the Signing Secret
Pass signing_secret when creating a webhook destination via the API:
curl -X POST https://your-api.dispatch.tech/v1/destinations \
-H "Authorization: Bearer dsp_your-api-key" \
-H "Content-Type: application/json" \
-d '{"name":"my-app","type":"webhook","webhook_url":"https://example.com/hook","signing_secret":"<32+ random bytes>"}'
Rotate or clear the secret later:
# Rotate
curl -X POST https://your-api.dispatch.tech/v1/destinations/<id>/signing-secret \
-H "Authorization: Bearer dsp_your-api-key" \
-H "Content-Type: application/json" \
-d '{"signing_secret":"<new secret>"}'
# Clear
curl -X DELETE https://your-api.dispatch.tech/v1/destinations/<id>/signing-secret \
-H "Authorization: Bearer dsp_your-api-key"
The secret is encrypted at rest (AES-256-GCM) and never returned in responses.
Rotating without dropping in-flight deliveries
When you rotate the secret, the previous secret is kept active alongside the new one for an overlap window. During the overlap, every delivery carries both signatures so your receiver can accept either while you roll the verification key:
X-Dispatch-Signature: sha256=<hex computed with NEW secret>
X-Dispatch-Signature-Previous: sha256=<hex computed with OLD secret>
Pass overlap_seconds on the rotate call to control how long the old secret stays valid (default 86400 = 24 hours, max 604800 = 7 days; 0 disables the overlap and cuts over immediately):
curl -X POST https://your-api.dispatch.tech/v1/destinations/<id>/signing-secret \
-H "Authorization: Bearer dsp_your-api-key" \
-H "Content-Type: application/json" \
-d '{"signing_secret":"<new secret>","overlap_seconds":86400}'
Receivers should treat a request as valid if either signature header verifies. Once your fleet is fully cut over to the new secret, the old one can be ignored; Dispatch automatically stops emitting X-Dispatch-Signature-Previous once the overlap expires.
Retry Behavior
Failed deliveries are retried automatically using the destination's retry policy (default: 7 attempts with exponential backoff). See Destinations for details.