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
Zsuffix and no sub-second precision:2026-07-28T14:22:00Z. Fields ending in_atare timestamps. - Dates are plain
YYYY-MM-DDwith no time component:order_dateis2026-07-28. The date filters on the list endpoint take the same format. X-RateLimit-Resetis 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.