Order booked (or payment received) and looking for a driver.
{
"code": "BL3F9A21C0DE",
"externalRef": "WC-10421",
"status": "searching",
"driverId": null,
"trackUrl": "https://blink.example.my/t/9f2c…"
}Quote, book and track deliveries from your store or system. Sandbox, scoped keys, idempotency and signed webhooks.
Blink POSTs { event, data, at } as JSON to every active endpoint whose event filter and mode match. Reply 2xx within 8 s. Otherwise the delivery retries with exponential backoff (30 s, 1 min, 2 min, 4 min … capped at 1 day) and is dead-lettered after the endpoint's maxAttempts (1–20, default 8). Every attempt is logged with request headers, response status, body and latency; replay any delivery (new delivery id, same payload) from the portal or POST /v1/business/webhooks/deliveries/{id}/replay.
Deliveries can repeat or arrive out of order. Sandbox orders carry data.sandbox: true and only go to endpoints in test or all mode.
{
"event": "order.delivered",
"data": {
"code": "BL3F9A21C0DE",
"externalRef": "WC-10421",
"status": "delivered",
"driverId": "0b6c…",
"trackUrl": "https://blink.example.my/t/9f2c…"
},
"at": "2026-10-26T08:15:30.000Z"
}| Header | Value |
|---|---|
X-Blink-Signature | v1: hex HMAC-SHA256(raw body, endpoint secret). Unchanged from v1, always sent. |
X-Blink-Signature-V2 | t=<unix seconds>,v1=<hex HMAC-SHA256("<t>.<raw body>", secret)>. Prefer this; reject timestamps older than 5 min. |
X-Blink-Signature-Version | 2 |
X-Blink-Event | The event name, e.g. order.delivered. |
X-Blink-Delivery | Delivery id. Replays get a new id; dedupe on data.code + event. |
X-Blink-Mode | live or test (sandbox orders). |
Use the raw request body; re-serialised JSON will not match. Compare in constant time.
import { createHmac, timingSafeEqual } from "node:crypto";
// v2: X-Blink-Signature-V2: t=<unix seconds>,v1=<hex HMAC-SHA256("<t>.<raw body>", secret)>
export function verifyBlink(rawBody: string, header: string, secret: string, toleranceS = 300) {
const m = /t=(\d+),v1=([0-9a-f]+)/.exec(header ?? "");
if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > toleranceS) return false;
const expected = createHmac("sha256", secret).update(`${m[1]}.${rawBody}`).digest("hex");
return expected.length === m[2].length && timingSafeEqual(Buffer.from(expected), Buffer.from(m[2]));
}
// express: app.post("/blink/webhook", express.raw({ type: "application/json" }), (req, res) => {
// if (!verifyBlink(req.body.toString("utf8"), req.header("x-blink-signature-v2"), process.env.BLINK_WEBHOOK_SECRET)) return res.sendStatus(401);
// const { event, data } = JSON.parse(req.body); res.sendStatus(200);
// });Order booked (or payment received) and looking for a driver.
{
"code": "BL3F9A21C0DE",
"externalRef": "WC-10421",
"status": "searching",
"driverId": null,
"trackUrl": "https://blink.example.my/t/9f2c…"
}A driver accepted the job.
{
"code": "BL3F9A21C0DE",
"externalRef": "WC-10421",
"status": "assigned",
"driverId": "0b6c…",
"trackUrl": "https://blink.example.my/t/9f2c…"
}Driver arrived at the (first) pickup.
{
"code": "BL3F9A21C0DE",
"externalRef": "WC-10421",
"status": "at_pickup",
"driverId": "0b6c…",
"trackUrl": "https://blink.example.my/t/9f2c…"
}Items collected.
{
"code": "BL3F9A21C0DE",
"externalRef": "WC-10421",
"status": "picked_up",
"driverId": "0b6c…",
"trackUrl": "https://blink.example.my/t/9f2c…"
}On the way to the drop-offs.
{
"code": "BL3F9A21C0DE",
"externalRef": "WC-10421",
"status": "in_transit",
"driverId": "0b6c…",
"trackUrl": "https://blink.example.my/t/9f2c…"
}All stops done; proof of delivery recorded.
{
"code": "BL3F9A21C0DE",
"externalRef": "WC-10421",
"status": "delivered",
"driverId": "0b6c…",
"trackUrl": "https://blink.example.my/t/9f2c…"
}Cancelled by you, ops or no driver found.
{
"code": "BL3F9A21C0DE",
"externalRef": "WC-10421",
"status": "cancelled",
"driverId": "0b6c…",
"trackUrl": "https://blink.example.my/t/9f2c…"
}Delivery could not be completed.
{
"code": "BL3F9A21C0DE",
"externalRef": "WC-10421",
"status": "failed",
"driverId": "0b6c…",
"trackUrl": "https://blink.example.my/t/9f2c…"
}Driver arrived at one stop of a multi-stop order.
{
"code": "BL3F9A21C0DE",
"externalRef": "WC-10421",
"trackUrl": "https://blink.example.my/t/9f2c…",
"stop": {
"seq": 2,
"kind": "dropoff",
"ref": "Aisyah",
"address": "12 Jalan SS2/24, Petaling Jaya"
}
}A stop was completed (items handed over or collected).
{
"code": "BL3F9A21C0DE",
"externalRef": "WC-10421",
"stop": {
"seq": 2,
"kind": "dropoff",
"ref": "Aisyah",
"address": "12 Jalan SS2/24, Petaling Jaya"
},
"failReason": null,
"podRecipient": "Aisyah",
"items": [
{
"name": "Cake box",
"qty": 2
}
]
}A stop could not be completed; undelivered items return.
{
"code": "BL3F9A21C0DE",
"externalRef": "WC-10421",
"stop": {
"seq": 2,
"kind": "dropoff",
"ref": "Aisyah",
"address": "12 Jalan SS2/24, Petaling Jaya"
},
"failReason": "Recipient not reachable",
"podRecipient": null
}A stop was cancelled.
{
"code": "BL3F9A21C0DE",
"externalRef": "WC-10421",
"stop": {
"seq": 2,
"kind": "dropoff",
"ref": "Aisyah",
"address": "12 Jalan SS2/24, Petaling Jaya"
},
"reason": "Customer cancelled"
}A recurring run booked its order (scheduledAt set when booked ahead).
{
"recurringId": "7c0e…",
"name": "Morning bakery run",
"slot": "202610270700",
"code": "BL9A…",
"externalRef": "REC-7c0e…-202610270700",
"trackUrl": "https://blink.example.my/t/…",
"scheduledAt": "2026-10-26T23:00:00.000Z",
"nextRunAt": "2026-10-27T23:00:00.000Z"
}A recurring run could not book (e.g. credit limit).
{
"recurringId": "7c0e…",
"name": "Morning bakery run",
"slot": "202610270700",
"externalRef": "REC-7c0e…-202610270700",
"error": "Credit limit exceeded",
"nextRunAt": "2026-10-27T23:00:00.000Z"
}A customer asked for a delivery through your booking widget.
{
"requestId": "e41b…",
"requester": {
"name": "Aina",
"phone": "0193344556"
},
"to": {
"address": "KLCC",
"lat": 3.1579,
"lng": 101.7116
},
"quoteSen": 1250,
"requireApproval": true
}"Send test event" in the portal or POST /v1/business/webhooks/{id}/test.
{
"message": "Hello from Blink",
"endpointId": "a1f2…"
}