Skip to content

Webhooks — Payload Shape & Headers

Last updated October 19, 2026


Payload shape

Every event is the same four-field envelope. The data object is byte-for-byte the same shape as the object returned by GET /v1/fulfilment-orders/{order_id} — it is built by the same code path and passed through the same serializer, so the two cannot drift apart. Write one parser and use it for both.

{
  "event": "fulfilment_order.created",
  "event_id": "evt_9f2c1b4e-6d31-4f7a-9c0e-8b2a5d41e770",
  "occurred_at": "2026-07-29T09:10:04Z",
  "data": {
    "order_id": "OMN-KONW-00042",
    "status": "processing",
    "order_date": "2026-07-28",
    "created_at": "2026-07-28T14:22:00Z",
    "updated_at": "2026-07-29T09:10:00Z",
    "parcel_count": 3,
    "warehouse": { "code": "TETRIS", "name": "Tetris Prep" },
    "shipping_address": {
      "name": "Jordan Ellis",
      "line1": "1420 Sycamore Ave",
      "line2": null,
      "city": "Baltimore",
      "state": "MD",
      "postal_code": "21201",
      "country": "US"
    },
    "items": [
      { "sku": "YT-FL-RV-A-028-YE-1", "product_name": "31000 Lumen LED Work Lights", "quantity": 5 }
    ],
    "shipments": [
      {
        "shipment_id": "OMN-KONW-00042-1",
        "sequence": 1,
        "carrier": "ups",
        "parcels": [
          {
            "parcel_id": "OMN-KONW-00042-1-1",
            "parcel_number": 1,
            "tracking_number": "1Z0K49Y40312446341",
            "carrier": "ups",
            "has_label": true,
            "weight": 11.0,
            "weight_unit": "lb",
            "dimensions": { "length": 17.0, "width": 13.0, "height": 5.0, "unit": "in" }
          }
        ]
      }
    ]
  }
}
Field Type Notes
event String One of the event types in the catalogue.
event_id String Unique per event, prefixed evt_. Use it as your idempotency key.
occurred_at Timestamp ISO 8601 UTC with a Z suffix.
data Object The full fulfilment order. Same shape as the GET response.

On fulfilment_order.cancelled and fulfilment_order.on_hold, data carries one extra field, previous_status, holding the status the order left:

{
  "event": "fulfilment_order.cancelled",
  "event_id": "evt_31c8a0d5-77bd-4a2e-8f19-1cf3f0b6a942",
  "occurred_at": "2026-07-30T16:48:11Z",
  "data": {
    "order_id": "OMN-KONW-00042",
    "status": "cancelled",
    "previous_status": "processing",
    ...
  }
}

Delivery headers

Every delivery carries Content-Type: application/json plus four Omnishelf headers:

Header Example What it is for
X-Omnishelf-Event-Id evt_9f2c1b4e-... Same value as event_id in the body. Deduplicate on this without parsing the payload.
X-Omnishelf-Event-Type fulfilment_order.created Route to the right handler before parsing.
X-Omnishelf-Signature sha256=8f14e45f... HMAC-SHA256 of the raw body, hex-encoded, with a sha256= prefix.
X-Omnishelf-Timestamp 1785488503 Unix epoch seconds at send time. Useful for rejecting very stale deliveries.
POST /omnishelf/webhooks HTTP/1.1
Host: api.your-company.com
Content-Type: application/json
X-Omnishelf-Event-Id: evt_9f2c1b4e-6d31-4f7a-9c0e-8b2a5d41e770
X-Omnishelf-Event-Type: fulfilment_order.created
X-Omnishelf-Signature: sha256=8f14e45fceea167a5a36dedd4bea2543a1b4e2c9d0f7361b8e4c2a9d13f5e074
X-Omnishelf-Timestamp: 1785488503

The signature is not timestamped into the signed string: it is a plain HMAC over the body alone. The timestamp header is informational, so use it for staleness checks but do not include it in the HMAC input.