Carts API
A cart is a long-lived (~30 days) shopping list. It's the lightweight half of the cart→checkout split (Q1 design decision).
A cart has:
token— opaque URL handle (returned at create)market_id— pinned currency and localecustomer_id— optional (NULL for anonymous)line_items— variant + qty + custom_attributesexpires_at— auto-extended on every PATCH
The cart never reserves inventory by default. Reservation happens
at checkout creation (default commerce.checkout.reserve_at = checkout_enter).
Create a cart
curl -X POST "$SF/carts.json" \
-H "X-Storefront-Key: $SK" \
-H "Content-Type: application/json" \
-d '{
"market_id": 308...,
"customer_id": null,
"utm": {"source":"newsletter","campaign":"may2026"},
"referrer": "https://google.com"
}'
{
"cart": {
"id": "308...",
"token": "8H3xa9...long-opaque",
"currency": "CZK",
"status": "active",
"line_items": [],
"totals": {
"currency": "CZK",
"subtotal_with_tax_cents": "0",
"total_cents": "0",
"line_count": 0,
"item_count": 0
},
"expires_at": "2026-05-31T..."
}
}
The token is your handle for all subsequent calls. Store it in
localStorage / a cookie so the basket survives page reloads.
Add a line
curl -X POST "$SF/carts/{token}/lines.json" \
-H "X-Storefront-Key: $SK" -H "Content-Type: application/json" \
-d '{
"variant_id": 308...,
"quantity": 2,
"custom_attributes": {"gift_wrap": true, "message": "Happy birthday"}
}'
The cart service:
- Resolves
variantandvariant_pricing(variant, cart.market_id) - Rejects if pricing currency doesn't match cart currency (
409) - Tries to merge with an existing line (same variant + same
custom_attributes_hash) — quantity adds up - Otherwise creates a new
cart_line_itemsrow with snapshot prices - Recomputes totals
Update qty
curl -X PATCH "$SF/carts/{token}/lines/{line_id}.json" \
-H "X-Storefront-Key: $SK" -H "Content-Type: application/json" \
-d '{"quantity": 5}'
Refreshes the price snapshot from the current variant_pricing
row — the customer always sees today's prices, not whatever was
stored when they first added the item.
Remove a line
curl -X DELETE "$SF/carts/{token}/lines/{line_id}.json" \
-H "X-Storefront-Key: $SK"
Pricing recomputation
Cart totals are computed live on every read — no stored total_cents
on the cart row. The cart-level totals are subtotal-only (no shipping,
no order-level discounts; those are checkout-phase concerns).
{
"totals": {
"currency": "CZK",
"subtotal_with_tax_cents": "24200",
"subtotal_without_tax_cents": "20000",
"tax_cents": "4200",
"total_cents": "24200",
"line_count": 1,
"item_count": 2
}
}
Customer association
A cart can attach a customer at create time, or be promoted later when
the user logs in. When two carts meet (anonymous browser cart +
existing customer cart), use merge_into (service-level) — the
source cart becomes status=merged, lines move to the target.
Endpoints
POST /storefront/2026-01/carts.json
GET /storefront/2026-01/carts/{token}.json
POST /storefront/2026-01/carts/{token}/lines.json
PATCH /storefront/2026-01/carts/{token}/lines/{line_id}.json
DELETE /storefront/2026-01/carts/{token}/lines/{line_id}.json
Next: Checkouts API.