docs/developer/core-concepts/orders.mdx
An order is the central model connecting a customer to their purchase. It collects line items, addresses, shipments, payments, and adjustments into a single transaction that flows through a checkout state machine from cart to completion.
erDiagram
Order ||--o{ LineItem : "has many"
Order ||--o{ Shipment : "has many"
Order ||--o{ Payment : "has many"
Order ||--o{ Adjustment : "has many"
Order }o--|| Address : "ship_address"
Order }o--|| Address : "bill_address"
Order }o--|| User : "belongs to"
Order }o--|| Store : "belongs to"
LineItem }o--|| Variant : "belongs to"
Shipment }o--|| StockLocation : "ships from"
Payment }o--|| PaymentMethod : "belongs to"
Order {
string number
string state
string email
string currency
decimal total
string payment_state
string shipment_state
}
LineItem {
integer quantity
decimal price
}
Shipment {
string number
string state
string tracking
}
Payment {
decimal amount
string state
}
Key relationships:
The API returns these key fields on every order:
| Attribute | Description |
|---|---|
number | Unique order number (e.g., R123456789), shown to customers |
state | Current checkout state (cart, address, delivery, payment, confirm, complete) |
email | Customer's email address |
currency | Order currency (e.g., USD) |
item_count | Total number of items |
item_total / display_item_total | Sum of line item prices |
ship_total / display_ship_total | Shipping cost |
tax_total / display_tax_total | Total tax |
promo_total / display_promo_total | Total discount from promotions |
adjustment_total / display_adjustment_total | Sum of all adjustments (tax + shipping + promos) |
total / display_total | Final order total |
payment_state | Payment status (balance_due, paid, credit_owed, failed, void) |
shipment_state | Shipment status (pending, ready, partial, shipped, backorder) |
completed_at | Timestamp when the order was placed |
The display_* fields return formatted strings with currency symbols (e.g., "$15.99").
A cart is simply an order in the cart state. Guest carts are identified by a cart token; authenticated users' carts are linked to their account.
// Create a cart
const cart = await client.carts.create()
// cart.token => "abc123" (save this for guest checkout)
// Get existing cart
const cart = await client.carts.get(cartId, { spreeToken: 'xxx' })
// Add an item
const updatedOrder = await client.carts.items.create(cart.id, {
variant_id: 'var_xxx',
quantity: 2,
})
// Update quantity
await client.carts.items.update(cart.id, 'li_xxx', {
quantity: 3,
})
// Remove an item
await client.carts.items.delete(cart.id, 'li_xxx')
# Create a cart
curl -X POST 'https://api.mystore.com/api/v3/store/carts' \
-H 'Authorization: Bearer pk_xxx'
# Get existing cart
curl 'https://api.mystore.com/api/v3/store/carts/cart_xxx' \
-H 'Authorization: Bearer pk_xxx' \
-H 'X-Spree-Token: abc123'
# Add an item
curl -X POST 'https://api.mystore.com/api/v3/store/carts/cart_xxx/items' \
-H 'Authorization: Bearer pk_xxx' \
-H 'X-Spree-Token: abc123' \
-H 'Content-Type: application/json' \
-d '{ "variant_id": "var_xxx", "quantity": 2 }'
# Update quantity
curl -X PATCH 'https://api.mystore.com/api/v3/store/carts/cart_xxx/items/li_xxx' \
-H 'Authorization: Bearer pk_xxx' \
-H 'X-Spree-Token: abc123' \
-H 'Content-Type: application/json' \
-d '{ "quantity": 3 }'
# Remove an item
curl -X DELETE 'https://api.mystore.com/api/v3/store/carts/cart_xxx/items/li_xxx' \
-H 'Authorization: Bearer pk_xxx' \
-H 'X-Spree-Token: abc123'
Every item mutation returns the full updated order with recalculated totals.
The checkout is a state machine that advances the order through a series of steps. Each step collects required information before allowing the order to proceed.
<Steps> <Step title="cart"> Customer has items in their cart. This is the starting state. </Step> <Step title="address"> Customer provides shipping and billing addresses. </Step> <Step title="delivery"> Customer selects a shipping rate for each shipment. </Step> <Step title="payment"> Customer provides payment. Skipped if the order is fully covered by store credit. </Step> <Step title="confirm"> Customer reviews and confirms the order. </Step> <Step title="complete"> Order is placed. `completed_at` is set and fulfillment begins. </Step> </Steps>If the order doesn't meet the requirements for the next state (e.g., missing address), the API returns an error.
<CodeGroup>// Set addresses
await client.carts.update(cartId, {
email: '[email protected]',
ship_address: {
firstname: 'John', lastname: 'Doe',
address1: '123 Main St', city: 'Los Angeles',
country_iso: 'US', state_abbr: 'CA', zipcode: '90001',
phone: '555-0100',
},
})
// Get shipments and select a shipping rate
const { data: shipments } = await client.carts.shipments.list(cartId)
await client.carts.shipments.update(cartId, shipments[0].id, {
selected_shipping_rate_id: 'sr_xxx',
})
// Create a payment session (e.g., Stripe)
const session = await client.carts.paymentSessions.create(cartId, {
payment_method_id: 'pm_xxx',
})
// session.external_data.client_secret => use with Stripe.js
// Complete the payment session after provider confirmation
await client.carts.paymentSessions.complete(cartId, session.id)
// Complete the order
await client.carts.complete(cartId)
# Set addresses
curl -X PATCH 'https://api.mystore.com/api/v3/store/carts/cart_xxx' \
-H 'Authorization: Bearer pk_xxx' \
-H 'X-Spree-Token: abc123' \
-H 'Content-Type: application/json' \
-d '{
"email": "[email protected]",
"ship_address": {
"firstname": "John", "lastname": "Doe",
"address1": "123 Main St", "city": "Los Angeles",
"country_iso": "US", "state_abbr": "CA", "zipcode": "90001",
"phone": "555-0100"
}
}'
# Select a shipping rate
curl -X PATCH 'https://api.mystore.com/api/v3/store/carts/cart_xxx/shipments/shp_xxx' \
-H 'Authorization: Bearer pk_xxx' \
-H 'X-Spree-Token: abc123' \
-H 'Content-Type: application/json' \
-d '{ "selected_shipping_rate_id": "sr_xxx" }'
# Complete the order
curl -X POST 'https://api.mystore.com/api/v3/store/carts/cart_xxx/complete' \
-H 'Authorization: Bearer pk_xxx' \
-H 'X-Spree-Token: abc123'
Apply or remove promotional coupon codes during checkout:
<CodeGroup>// Apply a discount code
await client.carts.discountCodes.apply(cartId, 'SAVE20')
// Remove a discount code
await client.carts.discountCodes.remove(cartId, 'SAVE20')
# Apply a discount code
curl -X POST 'https://api.mystore.com/api/v3/store/carts/cart_xxx/discount_codes' \
-H 'Authorization: Bearer pk_xxx' \
-H 'X-Spree-Token: abc123' \
-H 'Content-Type: application/json' \
-d '{ "code": "SAVE20" }'
# Remove a discount code
curl -X DELETE 'https://api.mystore.com/api/v3/store/carts/cart_xxx/discount_codes/SAVE20' \
-H 'Authorization: Bearer pk_xxx' \
-H 'X-Spree-Token: abc123'
Authenticated customers can view their past orders:
<CodeGroup>// List past orders
const { data: orders } = await client.customer.orders.list()
// Get a specific order with details
const order = await client.orders.get('or_xxx', {
expand: ['items', 'shipments', 'payments'],
})
# List past orders
curl 'https://api.mystore.com/api/v3/store/customer/orders' \
-H 'Authorization: Bearer <jwt_token>'
# Get a specific order
curl 'https://api.mystore.com/api/v3/store/orders/or_xxx?expand=items,shipments,payments' \
-H 'Authorization: Bearer <jwt_token>'
Line items represent individual products in an order. Each line item links to a Variant and tracks the quantity and price at the time of purchase.
When a variant is added to an order, the price is locked on the line item. If the variant's price changes later, existing orders are unaffected.
Adjustments modify an order's total — promotions decrease it, taxes and shipping increase it. Adjustments can be applied at the order level, the line item level, or the shipment level.
| State | Description |
|---|---|
balance_due | Payment total is less than the order total |
paid | Payment total equals the order total |
credit_owed | Payment total exceeds the order total (refund pending) |
failed | Most recent payment attempt failed |
void | Order was canceled and payments voided |
| State | Description |
|---|---|
pending | All shipments are pending |
ready | All shipments are ready to ship |
partial | At least one shipment is shipped, others are not |
shipped | All shipments have been shipped |
backorder | Some inventory is on backorder |
For more details, see Shipments and Payments.
order.completed)