API reference
Webhooks
Receive order and PUDO events as they happen. Your Starmile organization configures one or more webhook endpoints, each subscribed to the events you care about; we POST a signed JSON envelope to your URL and retry on failure.
How deliveries reach you
POST a JSON envelope to your endpoint. A delivery is a success on any 2xx response; anything else (or a timeout) is retried 3 times, 5 minutes apart, after which the delivery is marked failed. Respond 2xx quickly and process asynchronously.The request we send
Each delivery is a single event. Verify the signature, acknowledge with a 2xx, and de-duplicate on X-Webhook-Id (the same id is re-sent on every retry).
POST https://your-app.example/webhooks
Content-Type: application/json
X-Webhook-Id: 4821
X-Webhook-Event: order.delivered
X-Webhook-Signature: sha256=9f86d0818...c2c9
{
"id": 4821,
"event": "order.delivered",
"occurred_at": "2026-06-22T14:05:11+00:00",
"data": {
"order_id": 12345,
"tracking_number": "STM-12345",
"external_parent_id": "EXT-1",
"status": "delivered",
"occurred_at": "2026-06-22T14:05:10+00:00"
}
}Headers
X-Webhook-Idstringrequired- The delivery id — stable across retries of the same delivery. Use it to de-duplicate.
X-Webhook-Eventstringrequired- The event name (e.g. order.delivered), so you can route without parsing the body.
X-Webhook-Signaturestringoptional- Present when the endpoint has a signing secret: sha256=<HMAC-SHA256 of the raw body>.
Body
idintegerrequired- The delivery id (matches X-Webhook-Id).
eventstringrequired- The event name.
occurred_atstring (ISO-8601)required- When the delivery was created.
dataobjectrequired- The event payload — fields depend on the event (see below).
Verifying the signature
When your endpoint has a signing secret, every delivery carries X-Webhook-Signature: the lowercase hex HMAC-SHA256 of the raw request body using your secret, prefixed with sha256=. Compute the same and compare in constant time — reject the delivery if it does not match.
// Verify the signature before trusting a delivery (Node.js)
import crypto from "node:crypto";
function verify(rawBody, signatureHeader, secret) {
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
// constant-time compare
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected),
);
}Events
Subscribe each endpoint to any of the events below. The data object carries the listed fields.
Order events (OMS)
| Event | Sets status | data fields | Description |
|---|---|---|---|
order.created | order_created | order_idtracking_numberexternal_parent_id | An order entered the system. |
order.status_updated | (varies) | order_idstatusstagestep | The order advanced to a new status. |
order.consolidated | consolidated | order_idretained | Child parcels were consolidated under a master order. |
order.delivered | delivered | order_idtracking_number | The order was delivered to the recipient. |
order.delivery_failed | delivery_failed | order_idreason | A delivery attempt failed. |
order.cancelled | cancelled | order_id | The order was cancelled. |
order.customs_hold | customs_hold | order_id | The order was held at customs. |
PUDO events
| Event | Sets status | data fields | Description |
|---|---|---|---|
pudo.received | received_at_pudo | parcel_idtracking_numberpudo_point_id | A parcel was received at a PUDO point. |
pudo.accepted | accepted_at_pudo | parcel_idpudo_point_id | A parcel was accepted (checked in) at a PUDO point. |
pudo.delivered | delivered | parcel_idpudo_point_id | A customer collected the parcel from the PUDO point. |
pudo.undelivered | undelivered | parcel_idpudo_point_id | The parcel went uncollected past the PUDO hold window. |
Retries & failures
- A delivery succeeds on any
2xx. - On a non-
2xxor a timeout, it is retried 3 times, 5 minutes apart. - After the final attempt it is marked failed and logged; your Starmile operator can review every delivery (success or failure) in the console and re-send a test event.