# Platform webhooks

Register an HTTPS callback with your API key:

```
POST https://themassprint.com/v1/webhook-endpoints
Authorization: Bearer mp_live_...
{ "url": "https://yourplatform.com/hooks/massprint" }
→ { "id": "whe_...", "secret": "whsec_..." }   // save the secret; shown once
```

On every order status change we POST, with `event` set to `order.<status>` (`order.confirmed`, `order.printing`, `order.ready_for_dispatch`, `order.pickup_scheduled`, `order.shipped`, `order.out_for_delivery`, `order.delivered`, `order.returning`, `order.canceled`, ...). Ignore events you do not use; new statuses may be added. `id` is `<order_id>:<status>`; an order that enters a status again (out for delivery a second time after a failed attempt) gets `:2`, `:3` on the end, so dedupe on `id` as it is:

```json
{ "id": "ord_...:shipped", "event": "order.shipped", "order": { "order_id": "ord_...", "status": "shipped", "tracking": { "carrier": "bluedart", "awb": "AWB123", "url": "https://track.example.com/AWB123" }, ... }, "sent_at": "..." }
```

## Verifying signatures

Header `X-Massprint-Signature` is hex HMAC-SHA256 of the **raw request body** with your endpoint secret (the same scheme Razorpay uses to sign to us):

```js
const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
const valid = timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
```

Delivery is **at-least-once** and retries on failure. Make handlers idempotent by
deduping on the stable `id` field (`<order_id>:<event>`, constant across retries) —
not `sent_at`, which changes on every attempt.
