Webhooks
Webhooks are how Distribu pushes events to your systems in real time. Instead of polling the API every minute asking "did anything new happen?", register a URL once and Distribu fires a POST at it the moment something changes. Twelve events are supported today, spanning orders, customers, products, returns, refunds, and subscription billing — see Event types for the full catalog.
When to use webhooks vs. the API
| If you want to… | Use |
|---|---|
| Keep an ERP in sync with new orders as they come in | Webhooks |
| Notify a Slack channel when orders are confirmed | Webhooks |
| Pull your data for a one-time report or backfill | REST API |
| Show current order status in your own UI | Webhooks (cache locally) + API (fill gaps) |
Webhooks are push; the API is pull. Most production integrations use both — webhooks for real-time updates, the API to bootstrap or backfill when an endpoint's been offline.
Registering a webhook
From Settings → Webhooks, click + Add webhook. The form asks for:
- Payload URL — a URL Distribu will POST to. Must be a valid URL with
http://orhttps://scheme. HTTPS is strongly recommended in production. - Events — one or more events to subscribe to. Check only the ones you care about; unchecked events won't fire at your endpoint.
You can also register and manage webhooks from code — see Webhooks endpoints for the REST API.
On save, Distribu generates a signing secret and shows it once:
Webhook created! Copy the signing secret now — you won't see it again.
Your webhook signing secret:
whsec_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789-abcd
Secrets start with whsec_ and are 24 random bytes, base64url-encoded —
about 37 characters including the prefix. Copy it now — the raw
secret is never shown in the UI again. Lose it and you'll either need
to rotate the secret or
delete and re-create the webhook.
A minimal receiver
The simplest possible Distribu webhook receiver:
// Express + Node
import express from "express";
import crypto from "crypto";
const app = express();
const SECRET = process.env.DISTRIBU_WEBHOOK_SECRET; // whsec_...
app.post("/webhooks/distribu", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-webhook-signature"];
const expected = crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");
if (signature !== expected) return res.status(401).send("Invalid signature");
const { event, timestamp, data } = JSON.parse(req.body.toString());
console.log(`[${timestamp}] ${event}`, data);
res.sendStatus(200);
});
That's the whole thing. Verify, parse, respond. See Signature verification for the HMAC details and receivers in other languages.
Payload envelope
Every webhook POST has this shape:
{
"event": "order.created",
"timestamp": "2026-04-16T14:22:00.000Z",
"data": {
"id": "clxxorder123...",
"status": "SUBMITTED",
"total": 131.25,
"customerId": "clxxcust1...",
"poNumber": "PO-12345",
"itemCount": 2
}
}
event— One of the twelve event names documented in Event types.timestamp— ISO 8601 UTC, set at the moment Distribu composes the payload.data— Event-specific object. See Event types for the schema of each.
HTTP request details
Every delivery is:
- Method:
POST - Content-Type:
application/json - Headers:
X-Webhook-Signature— HMAC-SHA256 hex digest of the raw body, signed with the current secret.X-Webhook-Signature-Old— same digest signed with the previous secret. Only present during a secret rotation grace window.X-Webhook-Event— the event name (redundant with the payload, but handy for routing).
- Timeout: 10 seconds before Distribu aborts the request.
- Body: the JSON envelope above.
Your endpoint should respond with any 2xx status. Anything else (or a timeout / connection error) is recorded as a failed delivery.
Limits
- Up to 10 webhooks per company. Trying to create an 11th returns
Maximum of 10 webhooks per company.— delete unused ones to make room. - Both HTTP and HTTPS URLs are accepted. HTTPS is strongly recommended; webhook payloads contain order totals and customer IDs.
- Any path is fine —
/webhooks/distribu,/api/hooks, the root, doesn't matter. Distribu POSTs to whatever URL you register.
Delivery log
Every delivery (success or failure) is recorded and shown under each
webhook on the Webhooks settings page. You see the most recent 5 attempts
per webhook, with event name, HTTP status, duration in ms, and
timestamp. Useful for debugging — if you see a run of 401s, it's
usually a signature-verification bug on your side.
Failed deliveries are not retried automatically today — see Retries & delivery for the details and the recommended polling fallback.
Disabling and deleting
Each webhook has Disable and Delete links in the dashboard:
- Disable marks the webhook inactive. No events fire. The webhook and its delivery log are preserved so you can re-enable it later.
- Delete permanently removes the webhook. A confirmation prompt reads
Delete this webhook? It will stop receiving events immediately.— there's no undo.
Deleting the webhook also removes its delivery log.
Audit trail
Webhook configuration changes are logged:
WebhookCreated— metadata includesurlandevents.WebhookUpdated— metadata includes the changed fields.WebhookSecretRotated— metadata includes the grace-window end time.WebhookDeleted— metadata includesurl.
Delivery attempts themselves are not in the audit log — they're in the delivery log under each webhook.
What's in this section
- Event types — payload schemas for all twelve events, including the known payload-shape asymmetries by trigger source.
- Signature verification — HMAC-SHA256 verification with Node, Python, Ruby, and Go examples, plus secret rotation.
- Retries & delivery — what happens when your endpoint is down, why there are no automatic retries today, and the polling fallback.
Manage subscriptions from code via Webhooks endpoints.
Next: Event types.
