Skip to content

Receiving webhooks

Delivr posts an event to your HTTPS endpoint whenever one of your orders changes. Webhooks are how your order system stays in step without polling.

Delivr registers your endpoint URL and gives you its signing secret (whsec_…). Then you manage it through the API:

Call Does
GET /v1/merchant/webhook Your endpoint, the subscribed events and the secret’s prefix.
PUT /v1/merchant/webhook/events Choose the events you want. An empty list means all.
POST /v1/merchant/webhooks/test Send a webhook.test event.
GET /v1/merchant/webhooks/deliveries The delivery log: every attempt, its status code and timing, for 30 days.
POST /v1/merchant/webhooks/events/{id}/replay Send an event again, with the same id.
POST /hooks/delivr HTTP/1.1
Content-Type: application/json
X-Delivr-Event: order.picked_up
X-Delivr-Event-Id: 6b0f4f1e-2b7a-4d4e-9a6b-1f3e5d2c7a90
X-Delivr-Signature: t=1762175400,v1=5f2b…
Delivr-Api-Version: 2026-11-01
{
"id": "6b0f4f1e-2b7a-4d4e-9a6b-1f3e5d2c7a90",
"type": "order.picked_up",
"apiVersion": "2026-11-01",
"createdAt": "2026-11-03T13:30:00Z",
"data": {
"orderId": "3f0e9a52-8a7e-4f8e-9a51-2c0f3c9d1b7a",
"reference": "DD-482913",
"externalRef": "order-1001",
"type": "on_demand",
"status": "picked_up",
"version": 9
}
}

data is the order’s state when the event was queued. It carries the order id, reference, externalRef, type, status, version and, when they apply, reason, readyAt, window, previousWindow, predecessorId, successorId, choiceDeadline, fallbackReason, proofOfDelivery and sale. Door codes and driver notes are never included.

  1. Verify the signature before you trust anything. See Verifying signatures.
  2. Answer 2xx within 15 seconds. Queue the work and answer first. Redirects are not followed.
  3. Deduplicate on id. An event keeps its id across retries and replays. Process each id once.
  4. Expect any order. Events for one order can arrive late or out of order, for example order.driver_assigned before order.held.
  5. Apply data only if it is newer. Store each order’s version. Apply data as state only when its version is higher than yours, but still act on every new event id: some events share a version, such as order.window_changed with its order.placed.
  6. Create orders on order.placed only. Never on order.held or a status event. A status event for an id you do not know, or with sale.status pending or abandoned, is an assisted checkout hold: ignore it or read the order.
  7. Ignore unknown event types. New ones may be added.

When in doubt, GET /v1/merchant/orders/{id} is the source of truth.

// Express, with the raw body kept for the signature.
app.post("/hooks/delivr", express.raw({ type: "application/json" }), async (req, res) => {
if (!verify(process.env.DELIVR_WEBHOOK_SECRET, req.get("X-Delivr-Signature"), req.body)) {
return res.sendStatus(400);
}
const event = JSON.parse(req.body);
if (await seen(event.id)) return res.sendStatus(200);
await queue.add("delivr-event", event); // do the work after answering
res.sendStatus(200);
});

Any answer other than 2xx, no answer within 15 seconds, or a redirect is a failure. Delivr retries after 30 s, 2 m, 10 m, 30 m, 1 h, 3 h, 6 h, 6 h and 6 h: 10 attempts over about 23 hours. Then it gives up, and you can still replay the event for 30 days.

Replays send the event as it was first queued, to your endpoint as it is now, whatever your subscription. One replay per event per minute.