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.
Set up
Section titled “Set up”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. |
The event
Section titled “The event”POST /hooks/delivr HTTP/1.1Content-Type: application/jsonX-Delivr-Event: order.picked_upX-Delivr-Event-Id: 6b0f4f1e-2b7a-4d4e-9a6b-1f3e5d2c7a90X-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.
Handling events correctly
Section titled “Handling events correctly”- Verify the signature before you trust anything. See Verifying signatures.
- Answer
2xxwithin 15 seconds. Queue the work and answer first. Redirects are not followed. - Deduplicate on
id. An event keeps itsidacross retries and replays. Process each id once. - Expect any order. Events for one order can arrive late or out of order, for example
order.driver_assignedbeforeorder.held. - Apply
dataonly if it is newer. Store each order’sversion. Applydataas state only when itsversionis higher than yours, but still act on every new event id: some events share a version, such asorder.window_changedwith itsorder.placed. - Create orders on
order.placedonly. Never onorder.heldor a status event. A status event for an id you do not know, or withsale.statuspendingorabandoned, is an assisted checkout hold: ignore it or read the order. - 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);});Retries
Section titled “Retries”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.