Webhook Delivery
Configure outbound webhooks to receive real-time shipment status updates. All payloads are HMAC-SHA256 signed for verification.
Endpoint Management
/api/v1/webhook-endpoints/api/v1/webhook-endpoints/events/api/v1/webhook-endpoints/api/v1/webhook-endpoints/:id/api/v1/webhook-endpoints/:idCreate accepts an HTTPS url and a non-empty events array. Accepted subscription names are shipment.created, shipment.status_changed, shipment.delivered, and shipment.exception. The current carrier-update pipeline emits shipment.status_changed; inspect payload.status for delivered or exception.
Secret and Delivery Operations
/api/v1/webhook-endpoints/:id/rotate-secret/api/v1/webhook-endpoints/:id/deliveries/api/v1/webhook-endpoints/:id/deliveries/:deliveryId/retry/api/v1/webhook-endpoints/:id/testCreation and rotation return a whsec_ signing secret once. List and update responses explicitly exclude it. Store it in a secret manager and replace it atomically when rotating. Create, update, delete, rotate, retry, and test require owner/admin role; endpoint and delivery operations verify merchant ownership.
Headers and Payload
X-OT-Timestamp: 1784010721
X-OT-Signature: 7f2a... (lowercase hex HMAC-SHA256)
{
"event": "shipment.status_changed",
"payload": {
"shipmentId": "...",
"externalOrderId": "...",
"orderNumber": "1042",
"trackingNumber": "1Z999AA10123456784",
"carrierCode": "ups",
"status": "out_for_delivery",
"lastEvent": "Out for delivery",
"lastEventTime": "2026-07-14T03:30:00.000Z",
"trackingEvents": []
}
}
Verify the Exact Raw Body
import { createHmac, timingSafeEqual } from 'node:crypto';
const expected = createHmac('sha256', process.env.OT_WEBHOOK_SECRET)
.update(rawBody)
.digest('hex');
const valid = signature.length === expected.length &&
timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
Requests time out after 15 seconds. Jobs use up to five attempts with exponential backoff beginning at 30 seconds. Validate timestamps and reject replayed deliveries in your receiver.