Skip to main content

Tracking & Fulfillment

Once Riverr ships an order, carrier and tracking information becomes available through the API. This guide explains where that data lives, how to poll for it, and how it relates to the shipping options you set when creating the order.

Tracking lives on Shipment, not on the order's own fields

There is no trackingCode field directly on Order or OrderItem. Fulfillment tracking is exposed through the Shipment type. You can read it two ways:

  • getShipment(orderId) — the most recent shipment for one order.
  • Order.shipments — every shipment for an order, returned inline on getOrder / getOrders (best for split shipments, no extra round-trip).
query GetTracking($orderId: ID!) {
getShipment(orderId: $orderId) {
id
orderId
trackingCode
trackingUrl
carrierName
carrierService
cost
currency
platformShipmentId
shippedAt { _seconds _nanoseconds }
}
}

This is the same carrier + tracking record Riverr pushes back to your connected storefront when an order ships, so reading it gives you exactly what your buyer sees. trackingUrl is a ready-to-click carrier link — usually the cleanest value to surface to a buyer.

null until the order ships — poll for it

getShipment returns null while the order has not shipped yet (no shipment record exists). This is a normal, successful response — not an error. Poll the order until you get a non-null Shipment:

{ "data": { "getShipment": null } }

A typical integration polls open orders on an interval (e.g. every few minutes to hourly, depending on volume) and stops once getShipment returns a non-null object with a trackingCode.

Distinguish null from an error

A null payload under data.getShipment means "not shipped yet." If instead the response contains an errors array, that is a real failure (e.g. the order isn't yours, or the order record is missing) — see Errors. Don't treat an error as "not shipped."

Tracking fields can be null even after shipping

The tracking fields are nullable. A shipment record can exist before a label is purchased (for example, a manual shipment created for a non-label-buying enterprise), so trackingCode, carrierName, and carrierService may each be null on a shipment that otherwise exists. Treat them as optional and keep polling until trackingCode is populated if you need the number.

FieldTypeNotes
idID!Shipment record id
orderIdString!The order this shipment belongs to
trackingCodeStringCarrier tracking number. Nullable until a label is assigned.
trackingUrlStringDirect carrier tracking link (AfterShip fallback). Nullable.
carrierNameStringActual carrier used (e.g. "USPS"). Nullable.
carrierServiceStringActual service level used. Nullable.
costFloatShipping cost for this shipment (may be 0 for manual shipments).
currencyStringISO currency code for cost.
platformShipmentIdStringCarrier/platform shipment identifier.
shippedAtTimestampWhen the shipment was created/shipped (alias of createdAt).
createdAtTimestampWhen the shipment record was created.

Orders that ship in multiple parcels (split shipments)

A single order can ship in more than one parcel — Riverr writes one shipment record per partial shipment. getShipment(orderId) returns only the most recent one. To get every parcel for an order in a single call, select shipments on the order:

query GetOrderWithTracking($id: ID!) {
getOrder(id: $id) {
id
status
shipped
shipments {
trackingCode
trackingUrl
carrierName
shippedAt { _seconds _nanoseconds }
}
}
}

Listing shipments across orders with getShipments

getShipments returns shipments scoped to the orders you own. Call it with a shopId (must be one of your shops) or with no argument to span all your shops:

query { getShipments(shopId: "shop_123", limit: 50) { orderId trackingCode trackingUrl carrierName } }

Results are newest-first. limit caps the count; orderIds narrows to specific orders (only those you own are returned).

Reserved arguments

The status and carrier arguments on getShipments are accepted but not yet implemented — they are ignored today. Filter client-side until this notice is removed. See Shipments.

Order status

Alongside tracking, the order carries a coarse, derived status ("production" | "unsynced" | "shipped" | "cancelled") and finer statusTags, so you can poll lifecycle without inspecting individual flags. See Orders for the full status model.

Relation to ShippingOptionsInput

Don't confuse the requested shipping method with the actual carrier that shipped the order:

  • At order creation you may pass shippingOptions (a ShippingOptionsInput) on createOrder / createOrderFromGtin. These are ShipStation-style codes — carrierCode, serviceCode, packageCode — describing the service you'd like used. They are persisted on the order and echoed back as Order.shippingOptions.
  • After the order ships the real carrier is reported on the Shipment as human-readable carrierName / carrierService, plus the trackingCode.

The codes you send (carrierCode/serviceCode) are ShipStation identifiers and are a different namespace from the carrierName/carrierService you later read on the Shipment — match orders to shipments by orderId, not by carrier string.

  1. Create the order (optionally with shippingOptions).
  2. Poll getShipment(orderId) (or getOrder { status shipments { ... } }) on an interval.
  3. While getShipment is null (or status is not shipped), keep polling.
  4. When a Shipment appears, read trackingCode / trackingUrl / carrierName. If trackingCode is still null, the label isn't bought yet — keep polling for it.
  5. Surface the tracking to your buyer (it already matches what Riverr pushed to the connected store).