Starship Rewards API
Core Concepts

Order Lifecycle

Every status and sub-status an order can move through, who triggers each transition, and what your integration should do at each stage

Order Lifecycle

An order carries two orthogonal status fields: a primary status (coarse — what bucket is this in?) and a sub-status (fine-grained — what exactly is happening inside that bucket?). Your integration should branch primarily on primary status and use sub-status for observability and reconciliation.

Primary Status — at a glance

PrimaryVouchers?Who can trigger transitions outTerminal?
PLACEDNoStarship (auto)No
PENDINGNoAdmin (usually), inventory pump, auto-retry cron (if enabled)No
DELIVEREDYesYes (can still be refunded)
PARTIALLY_DELIVEREDPartialAdmin, retry on remaining itemsNo (eventually → DELIVERED or CANCELLED)
CANCELLEDNoYes
FAILEDNoYes (rare — usually becomes CANCELLED on admin action)

See the Orders Overview page for the full UPPERCASE enum tables.

The Three Common Paths

Most orders in production take one of three shapes. Your retry and reconciliation logic should be designed around these, not around the full state-machine graph.

Path 1 — Instant Fulfillment (most common)

Path 1 — Instant fulfillment from inventory cache
POST /orders
PLACED · INITIAL
Inventory hit · allocate PENDING · INVENTORY_FULFILLED
Wallet debit · finalize
DELIVERED · COMPLETED
Webhook: order.delivered

Typical turnaround: 200–800 ms end-to-end. The HTTP response carries status: DELIVERED.

Path 2 — Pending → Admin-Driven Vendor Fulfillment

Path 2 — Inventory miss, admin escalates to vendor
POST /orders
PLACED · INITIAL Inventory miss
PENDING · VENDOR_ORDER_PENDING
Webhook: order.pending · client waits
Admin triggers vendor API retry
Vendor returns vouchers?
YES
PENDING · VENDOR_VOUCHER_FETCHED
DELIVERED · COMPLETED
Webhook: order.delivered
NO
PENDING · VENDOR_ORDER_FAILED
CANCELLED Wallet refunded
Webhook: order.cancelled

Turnaround: minutes to hours, depending on admin response time and vendor latency.

Path 3 — Partial Delivery

When a single order covers multiple units and only some fulfill, the order is marked PARTIALLY_DELIVERED with sub-status PARTIAL. The remaining items stay in their item-level pending state. Your integration must read GET /orders/:id and iterate order.items[] to see which units are delivered vs. pending.

A PARTIALLY_DELIVERED order eventually converges to DELIVERED (all items fulfilled) or CANCELLED (remaining items refunded). It does not stay partial indefinitely.

Sub-Status Decoder Ring

When you see an unexpected sub-status on a GET /orders/:id, use this table to figure out where in the pipeline the order is. Sub-statuses are additive over time — new ones may appear on existing primary statuses. Treat any unrecognized sub-status as informational, never as a reason to error out.

Sub-statusTypical primaryWhat it meansYour action
INITIALPLACED / PENDINGOrder just created, nothing has run yetWait.
PROCESSINGPENDINGStarship is mid-pipeline (validation, allocation)Wait — brief.
INVENTORY_FULFILLEDDELIVEREDFilled from cache, no vendor involvedRetrieve vouchers.
VENDOR_ORDER_PENDINGPENDINGAwaiting admin to escalate or awaiting vendor responseWait or poll.
VENDOR_ORDER_CREATEDPENDINGVendor accepted the orderWait — seconds to minutes.
VENDOR_VOUCHER_FETCHEDPENDINGDELIVEREDVouchers returned by vendor, about to finalizeAlmost done.
VENDOR_ORDER_FAILEDPENDING / CANCELLEDVendor rejected or erroredAwait admin action.
PENDING_REPROCESSPENDINGQueued for retry after transient failureWait.
REPROCESS_EXPIREDPENDINGCANCELLEDRetry window closed without successAdmin must intervene; wallet will refund.
PARTIALPARTIALLY_DELIVEREDSome items delivered, others pendingIterate order.items[].
COMPLETEDDELIVEREDAll items fulfilled and reconciledDone.
GV_LINK_PENDINGPENDINGGift-voucher link creation queuedWait.
GV_LINK_CLAIMEDDELIVEREDEnd-user claimed the linkNothing — informational.
GV_LINK_UNCLAIMEDDELIVEREDLink generated but not yet claimedNothing — informational.
BULK_LIMIT_EXCEEDEDCANCELLEDYour request exceeded the bulk capSplit the order; resubmit.
REFUNDEDCANCELLEDWallet refundedReconcile wallet.
NOT_REFUNDEDCANCELLED / FAILEDWallet refund still owedFlag for ops.
RECONCILEDDELIVEREDPost-delivery reconciliation passedNothing — informational.

Refunds & Reversals

DELIVERED is not strictly terminal. If a voucher is found invalid (vendor recall, fraud flag), an admin can mark the order CANCELLED after delivery, which:

  1. Moves the primary status: DELIVEREDCANCELLED
  2. Sets sub-status to REFUNDED (or NOT_REFUNDED if the refund fails)
  3. Fires an order.cancelled webhook
  4. Credits the wallet back

Your integration should handle a late order.cancelled webhook even for an order you already marked "done" in your system.

What You Should Build

Branch on primary status, observe sub-status

Customer-facing logic (show/hide voucher UI, send email) keys off primary status. Reconciliation and ops dashboards should surface sub-status so humans know where stuck orders are parked.

Treat unknown sub-statuses as informational

Sub-statuses evolve. Don't let your client error out on an unrecognized value. Log it and carry on using primary status as truth.

Expect late cancellations

Build idempotent "cancel this order and refund the customer" handlers that don't blow up if the order is already marked delivered in your system.

Poll PENDING orders on a schedule

An order that's been PENDING for more than 24 hours is stuck. Flag it for human review in your ops tool, not in the customer UI.

Next