Skip to content

Tracking

Last updated October 20, 2026

Base URL

All examples assume BASE_URL=https://api.omnishelf.com and OMNI_KEY=omni_sk_YOUR_KEY.

Two endpoints submit tracking and three read it back:

Endpoint Use it to
POST /v1/fulfilment-orders/{order_id}/tracking Submit tracking when Omnishelf has no shipment records yet.
POST /v1/shipments/{shipment_id}/tracking Attach tracking numbers to parcels Omnishelf already created.
GET /v1/orders/{order_id}/tracking Read the whole order: every shipment and every parcel.
GET /v1/shipments/{shipment_id}/tracking Read one shipment and its parcels.
GET /v1/parcels/{parcel_id}/tracking Read one parcel.

Both submission endpoints push to the fulfilment system before writing anything locally, so a failed push leaves Omnishelf untouched and you can retry the identical request. Both are idempotent: resubmitting a tracking number Omnishelf already holds is a no-op that returns "result": "unchanged". Neither sets order status — Omnishelf recomputes it.


POST /v1/fulfilment-orders/{order_id}/tracking

Submit tracking for an order when Omnishelf has no shipment records yet — Omnishelf creates the shipments and parcels for you, one parcel per tracking number, grouped by carrier.

Path parameters

Parameter Example Notes
order_id OMN-KONW-00042 Omnishelf order ID.

Request body

Field Type Required Notes
parcels Array Required Non-empty, at most 50 entries.
parcels[].tracking_number String Required Must be unique within the request (case-insensitive).
parcels[].carrier String Required Carrier name or code, e.g. ups, fedex, stamps_com.
parcels[].shipping_cost Decimal Optional Must be ≥ 0. Requires currency when supplied.
parcels[].currency String Conditional Three-letter uppercase ISO 4217, e.g. USD. Required with shipping_cost.
parcels[].weight Decimal Optional Must be > 0. Requires weight_unit when supplied.
parcels[].weight_unit String Conditional One of lb, kg. Required with weight.
parcels[].dimensions Object Optional All-or-nothing: length, width, height (all > 0) and unit.
parcels[].dimensions.unit String Conditional One of in, cm.

Request

curl -s -X POST "$BASE_URL/v1/fulfilment-orders/OMN-KONW-00042/tracking" \
  -H "Authorization: Bearer $OMNI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "parcels": [
      {
        "tracking_number": "1Z0K49Y40312446341",
        "carrier": "ups",
        "shipping_cost": 14.32,
        "currency": "USD",
        "weight": 11.0,
        "weight_unit": "lb",
        "dimensions": { "length": 17.0, "width": 13.0, "height": 5.0, "unit": "in" }
      },
      {
        "tracking_number": "1Z0K49Y40301020133",
        "carrier": "ups"
      }
    ]
  }'

Response — 200

{
  "data": {
    "order_id": "OMN-KONW-00042",
    "order_status": "processing",
    "previous_order_status": "awaiting_shipment",
    "omnishelf_oms_synced": true,
    "parcels": [
      { "tracking_number": "1Z0K49Y40312446341", "carrier": "ups", "result": "created" },
      { "tracking_number": "1Z0K49Y40301020133", "carrier": "ups", "result": "created" }
    ]
  }
}

A fully idempotent resubmission — every tracking number already on the order — returns 200 with omnishelf_oms_synced: false and every parcel marked "result": "unchanged". No writes happen at all.

Only the first new tracking number reaches the fulfilment system

The downstream fulfilment system accepts one tracking number per order on this path, so only the first new parcel's number is pushed through. Every parcel you submit is still recorded in Omnishelf and appears on the tracking endpoints.

Errors

  • 400 validation_error — empty or oversized parcels, malformed JSON, a missing tracking number or carrier, a duplicate tracking number, or a bad unit/currency. Includes an errors array keyed by tracking_number (see When the key field is missing).
  • 404 not_found — unknown order, or another seller's order.
  • 409 omnishelf_oms_order_missing — the order is not linked to the fulfilment system yet.
  • 409 request_pending — a cancellation or SKU change request is open on the order. Nothing is saved or sent to the fulfilment system. Wait for Omnishelf to decide the request.
  • 503 service_unavailable — Omnishelf couldn't check whether the order may take tracking. Nothing is saved or sent. Retry shortly.
  • 502 omnishelf_oms_push_failed — nothing was saved. Retry the same request.

POST /v1/shipments/{shipment_id}/tracking

Attach tracking numbers to parcels Omnishelf has already created — the case where you were given parcel IDs up front and are filling in the numbers you got at the counter.

Path parameters

Parameter Example Notes
shipment_id OMN-KONW-00042-1 Omnishelf shipment ID.

Request body

Same shape as the order-level endpoint with one addition: each entry must name an existing parcel_id that belongs to this shipment.

Field Type Required Notes
parcels Array Required Non-empty, at most 50 entries.
parcels[].parcel_id String Required Must already exist on this shipment. Unique within the request.
parcels[].tracking_number String Required —
parcels[].carrier String Required —
parcels[].shipping_cost Decimal Optional ≥ 0, requires currency.
parcels[].currency String Conditional ISO 4217 uppercase.
parcels[].weight Decimal Optional > 0, requires weight_unit.
parcels[].weight_unit String Conditional lb or kg.
parcels[].dimensions Object Optional length, width, height (> 0) and unit (in or cm).

Request

curl -s -X POST "$BASE_URL/v1/shipments/OMN-KONW-00042-1/tracking" \
  -H "Authorization: Bearer $OMNI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "parcels": [
      { "parcel_id": "OMN-KONW-00042-1-1", "tracking_number": "1Z0K49Y40312446341", "carrier": "ups", "weight": 11.0, "weight_unit": "lb" },
      { "parcel_id": "OMN-KONW-00042-1-2", "tracking_number": "1Z0K49Y40301020133", "carrier": "ups" }
    ]
  }'

Response — 200

{
  "data": {
    "shipment_id": "OMN-KONW-00042-1",
    "order_id": "OMN-KONW-00042",
    "order_status": "processing",
    "previous_order_status": "awaiting_shipment",
    "omnishelf_oms_synced": true,
    "parcels": [
      { "parcel_id": "OMN-KONW-00042-1-1", "tracking_number": "1Z0K49Y40312446341", "carrier": "ups", "result": "updated" },
      { "parcel_id": "OMN-KONW-00042-1-2", "tracking_number": "1Z0K49Y40301020133", "carrier": "ups", "result": "unchanged" }
    ]
  }
}

Errors

  • 400 validation_error — as above, plus "parcel_id does not belong to this shipment". Entries are keyed by parcel_id.
  • 404 not_found — unknown shipment, or another seller's shipment.
  • 409 omnishelf_oms_order_missing — the parent order is not linked to the fulfilment system.
  • 409 request_pending — a cancellation or SKU change request is open on the order. Nothing is saved or sent to the fulfilment system. Wait for Omnishelf to decide the request.
  • 503 service_unavailable — Omnishelf couldn't check whether the order may take tracking. Nothing is saved or sent. Retry shortly.
  • 502 omnishelf_oms_push_failed — nothing was saved. Retry.

When the key field is missing

Each entry in errors is keyed by tracking_number on the order-level endpoint and by parcel_id on the shipment-level endpoint. When the key field is the one that is missing, the key is null and the message names the entry by its index in parcels:

{
  "error": "validation_error",
  "message": "One or more parcel updates are invalid.",
  "errors": [
    { "tracking_number": null, "field": "tracking_number", "message": "tracking_number is required (parcels[0])" }
  ]
}

Map such an entry back to your request by the index in the message, not by the key.


What happens after you submit tracking

Submitting a tracking number never sets the order to shipped by itself — status is always derived from carrier data.

Step When Effect on order status
1. Tracking accepted Synchronously, in your POST Parcels store the number and carrier; the order is recomputed to processing.
2. Tracker registered Synchronously, right after the write (best effort) The carrier's current milestone is fetched and applied. An already in-transit parcel becomes in_transit and the order becomes shipped in the same response.
3. Live carrier updates Whenever the carrier scans, via webhook Parcel tracking_status updates and the order rolls up to shipped, exception or delivered.
4. Safety net Every 30 minutes A background sync registers any tracking number that step 2 could not register and reconciles statuses.

order_status in the response reflects the status after step 2, so a parcel already in the carrier network can come back as shipped immediately. If the provider is slow or unreachable, you get processing and the status corrects itself on the next carrier event or within 30 minutes.


GET /v1/orders/{order_id}/tracking

Different path prefix

Reading an order's tracking uses /v1/orders/..., while everything else order-related uses /v1/fulfilment-orders/.... Submitting tracking is POST /v1/fulfilment-orders/{id}/tracking; reading it back is GET /v1/orders/{id}/tracking. Same order ID, different prefix. The shipment and parcel reads have no such mismatch: GET /v1/shipments/{id}/tracking and GET /v1/parcels/{id}/tracking use the same prefix as their other endpoints.

The whole tracking picture for one order: the stored order status, then every shipment with its computed rollup status, then every parcel with its raw carrier status.

Path parameters

Parameter Example Notes
order_id OMN-KONW-00042 Omnishelf order ID.

Request

curl -s "$BASE_URL/v1/orders/OMN-KONW-00042/tracking" \
  -H "Authorization: Bearer $OMNI_KEY"

Response — 200

{
  "data": {
    "order_id": "OMN-KONW-00042",
    "status": "shipped",
    "parcel_count": 3,
    "shipments": [
      {
        "shipment_id": "OMN-KONW-00042-1",
        "sequence": 1,
        "status": "shipped",
        "carrier": "ups",
        "parcel_count": 2,
        "parcels": [
          {
            "parcel_id": "OMN-KONW-00042-1-1",
            "parcel_number": 1,
            "tracking_number": "1Z0K49Y40312446341",
            "carrier": "ups",
            "tracking_status": "out_for_delivery",
            "tracking_message": "Out for delivery",
            "tracking_updated_at": "2026-07-31T12:04:00Z"
          },
          {
            "parcel_id": "OMN-KONW-00042-1-2",
            "parcel_number": 2,
            "tracking_number": "1Z0K49Y40301020133",
            "carrier": "ups",
            "tracking_status": "in_transit",
            "tracking_message": "Arrived at facility",
            "tracking_updated_at": "2026-07-31T04:41:00Z"
          }
        ]
      },
      {
        "shipment_id": "OMN-KONW-00042-2",
        "sequence": 2,
        "status": "processing",
        "carrier": "stamps_com",
        "parcel_count": 1,
        "parcels": [
          {
            "parcel_id": "OMN-KONW-00042-2-1",
            "parcel_number": 1,
            "tracking_number": "9405511899223197428431",
            "carrier": "stamps_com",
            "tracking_status": "pending",
            "tracking_message": null,
            "tracking_updated_at": null
          }
        ]
      }
    ]
  }
}

Note shipment 2: its only parcel is pending, so the shipment rolls up to processing, not shipped. Nested parcels omit shipment_id and order_id because the surrounding structure already states them.

Errors

  • 404 not_found — unknown order, or another seller's order.

GET /v1/shipments/{shipment_id}/tracking

One shipment: its computed rollup status, then every parcel with its raw carrier status. It is the same shipment object as in the order-level read, with order_id added so you can walk back up.

Path parameters

Parameter Example Notes
shipment_id OMN-KONW-00042-1 Omnishelf shipment ID.

Request

curl -s "$BASE_URL/v1/shipments/OMN-KONW-00042-1/tracking" \
  -H "Authorization: Bearer $OMNI_KEY"

Response — 200

{
  "data": {
    "shipment_id": "OMN-KONW-00042-1",
    "order_id": "OMN-KONW-00042",
    "sequence": 1,
    "status": "shipped",
    "carrier": "ups",
    "parcel_count": 2,
    "parcels": [
      {
        "parcel_id": "OMN-KONW-00042-1-1",
        "parcel_number": 1,
        "tracking_number": "1Z0K49Y40312446341",
        "carrier": "ups",
        "tracking_status": "out_for_delivery",
        "tracking_message": "Out for delivery",
        "tracking_updated_at": "2026-07-31T12:04:00Z"
      },
      {
        "parcel_id": "OMN-KONW-00042-1-2",
        "parcel_number": 2,
        "tracking_number": "1Z0K49Y40301020133",
        "carrier": "ups",
        "tracking_status": "in_transit",
        "tracking_message": "Arrived at facility",
        "tracking_updated_at": "2026-07-31T04:41:00Z"
      }
    ]
  }
}

status is the shipment rollup, computed from this shipment's parcels only. The order may have a different status if it has other shipments.

Errors

  • 404 not_found — "The requested shipment does not exist." Unknown shipment, or another seller's shipment.

GET /v1/parcels/{parcel_id}/tracking

One box, parcel facts only — with its shipment and order IDs so you can walk back up.

Path parameters

Parameter Example Notes
parcel_id OMN-KONW-00042-1-1 Omnishelf parcel ID.

Request

curl -s "$BASE_URL/v1/parcels/OMN-KONW-00042-1-1/tracking" \
  -H "Authorization: Bearer $OMNI_KEY"

Response — 200

{
  "data": {
    "parcel_id": "OMN-KONW-00042-1-1",
    "parcel_number": 1,
    "shipment_id": "OMN-KONW-00042-1",
    "order_id": "OMN-KONW-00042",
    "tracking_number": "1Z0K49Y40312446341",
    "carrier": "ups",
    "tracking_status": "out_for_delivery",
    "tracking_message": "Out for delivery",
    "tracking_updated_at": "2026-07-31T12:04:00Z"
  }
}

Errors

  • 404 not_found — unknown parcel, or another seller's parcel.