Skip to content

Conventions

Last updated October 19, 2026

Base URL

https://api.omnishelf.com

Every path in this document is appended to that base — $BASE_URL/v1/ping, $BASE_URL/v1/fulfilment-orders, and so on. The version lives in the path.

Within v1 changes are additive only: new fields and new endpoints may appear, new webhook event types may be added to event_types, but existing fields will not be removed or change meaning. Parse defensively — ignore fields you do not recognise rather than rejecting the payload.


Identifier formats

Every identifier the API exposes is a human-readable display ID, never an internal database UUID. The formats nest: a parcel ID contains its shipment ID, which contains its order ID. That means you can always tell what a parcel belongs to from the string alone.

Identifier Format Example Notes
order_id OMN-{SELLER}-{NNNNN} OMN-KONW-00042 The Omnishelf order ID. The only order identifier on this API.
shipment_id {order_id}-{sequence} OMN-KONW-00042-1 A new order's first shipment is -1. A second shipment on the same order is -2.
parcel_id {shipment_id}-{parcel_number} OMN-KONW-00042-1-2 The second box of the first shipment of order OMN-KONW-00042.

Treat sequence as an ordering key, not a count

A voided or replaced shipment keeps its number, and the next shipment continues from there, so numbers are never reused. Older orders can have -2 as their first visible shipment with no -1, because a placeholder that was later replaced used up the number. Those IDs stay as they are; they are not renumbered.

Path parameters must be URL-encoded if they contain anything unusual, though in practice these formats are already URL-safe.


Timestamps and dates

  • Timestamps are ISO 8601 in UTC with a Z suffix and no sub-second precision: 2026-07-28T14:22:00Z. Fields ending in _at are timestamps.
  • Dates are plain YYYY-MM-DD with no time component: order_date is 2026-07-28. The date filters on the list endpoint take the same format.
  • X-RateLimit-Reset is the one exception: it is a Unix epoch second count, not ISO 8601.

Pagination

List endpoints are cursor-paginated. Every list response carries a pagination object with next_cursor and has_more. When has_more is false, next_cursor is null and you are done.

Cursors are opaque base64 — never construct or modify one; pass back exactly what you received.

# First page — default limit is 50, maximum is 200.
curl -s "$BASE_URL/v1/fulfilment-orders?limit=100" \
  -H "Authorization: Bearer $OMNI_KEY"

# Response tail:
# "pagination": { "next_cursor": "eyJkIjoiMjAyNi0wNy0yOCIsIm8iOiJPTU4tS09OVy0wMDA0MiJ9", "has_more": true }

# Next page — feed next_cursor straight back in.
curl -s "$BASE_URL/v1/fulfilment-orders?limit=100&cursor=eyJkIjoiMjAyNi0wNy0yOCIsIm8iOiJPTU4tS09OVy0wMDA0MiJ9" \
  -H "Authorization: Bearer $OMNI_KEY"

Full drain loop:

cursor=""
while :; do
  url="$BASE_URL/v1/fulfilment-orders?limit=200"
  [ -n "$cursor" ] && url="$url&cursor=$cursor"
  page=$(curl -s "$url" -H "Authorization: Bearer $OMNI_KEY")
  echo "$page" | jq -c '.data[]'
  more=$(echo "$page" | jq -r '.pagination.has_more')
  [ "$more" = "true" ] || break
  cursor=$(echo "$page" | jq -r '.pagination.next_cursor')
done

Results are ordered by order_date ascending, then by order_id ascending, and the cursor encodes that pair. An order whose date changes mid-walk can therefore be missed or repeated — make your ingestion idempotent on order_id.