Skip to content

Webhooks — Overview & Events

Last updated October 20, 2026

Webhooks are the push half of the API. Omnishelf POSTs a signed JSON envelope to a URL you host when an order changes state. Registration is done by Omnishelf staff: give them the callback URL and the event types you want. Each event type is registered as its own webhook, and every webhook gets its own signing secret: a delivery verifies only with the secret of the webhook it was sent for.


Event catalogue

Three event types exist today, all driven by a single database trigger on the order. Read the "from" column carefully — cancelled and on_hold only fire for an order you were already told about.

Event Fires when status changes to From What it means for you
fulfilment_order.created its first visible status Order creation The order now exists and is readable on the API. Sent once per order, as soon as you can see it, usually before any label exists. Where orders are routed, that is when it is routed to you.
fulfilment_order.cancelled cancelled any announced status Stop. Do not ship. Destroy or void any label already printed. Includes previous_status.
fulfilment_order.on_hold on_hold any announced status Pause. Hold the boxes. No event is sent when the hold is released, so poll GET /v1/fulfilment-orders/{order_id} until its status moves on. Includes previous_status.

You are only sent event types you registered for, and only for the orders you fulfil, once they are routed to you. Read the live list from event_types on GET /v1/ping rather than hard-coding it.

GET /v1/ping lists every event type you can register, eight in all. Besides the three above, five belong to the order lifecycle: fulfilment_order.hold_released, .in_transit, .delivered, .undelivered and .status_changed. They are sent only once the order lifecycle is switched on for your account. Until then, the three events above are the only ones you receive.


When nothing is sent

The single largest source of integration surprises. Every case below is a moment where an integrator reasonably expects an event and does not get one:

Situation Why no event
The order ships (processing → shipped) There is no fulfilment_order.shipped event. Poll GET /v1/orders/{order_id}/tracking instead.
The order is delivered (shipped → delivered) No event emitted. Same workaround.
A parcel hits an exception Carrier problems move status to exception silently. Only tracking reads reveal it.
The order becomes actionable (awaiting_shipment → processing) You were already told about the order when it was created. Poll the order or its labels to see when a label appears.
The order has no order_id yet Nothing is sent until the order is assigned its Omnishelf order identifier; the created event follows immediately afterwards.
Anything other than status changes A changed address, an added or removed line item, a new tracking number, a regenerated label — none of these emit an event.
The status is re-set to the same value A write of processing over processing is not a change and is ignored.
The order has no seller assigned Nobody to notify, so nothing is sent.
You have no active registration for that event type A registration that is inactive, or missing for that specific event type, is a silent no-op.

Webhooks start the work; polling finishes it

The dependable pattern is: react to fulfilment_order.created to begin fulfilment, then poll tracking for the orders you have in flight until each reaches delivered, exception, or cancelled. Do not wait for a completion event — there isn't one.