State reference

Cart states

A cart's status field is the single source of truth for whether it can still accept mutations. Only open carts are writable.

State machine

stateDiagram-v2
  [*] --> open: POST /carts
  open --> open: mutations (items / shipping / discount / tax)
  open --> checkout: POST /carts/{id}/checkout (creates draft order)
  checkout --> converted: order is paid (payment.captured)
  open --> abandoned: 24h idle (background sweep)
  checkout --> abandoned: 24h idle OR order cancelled before payment
  abandoned --> [*]
  converted --> [*]
Cart lifecycle from create through conversion.

States

  • open — initial state. Accepts items, discount apply/remove, shipping select, tax recompute, and checkout conversion.
  • checkout — cart has been converted to a draft order (POST /carts/{id}/checkout). The cart is frozen; further mutations return 409 cart_already_checked_out.
  • converted — the linked draft order was paid and an order row was finalised (payment.captured fired). Read- only; archived.
  • abandoned — no activity for 24 hours (or past the cart's expiresAt), OR the linked draft order was cancelled before payment. An hourly background sweep performs the idle transition and emits cart.abandoned. Read-only. See Abandoned-cart recovery below.

Mutation rules

  • Every mutation increments sequence on the cart. Webhook payloads include sequence so subscribers can detect out-of-order delivery.
  • POST /carts/{id}/checkout transitions open checkout. Idempotent within the 24h Idempotency-Key window — same key replays the same draft order id.
  • Direct transitions out of checkout / converted / abandoned via the cart API are not allowed. Cancel the linked order to roll the cart back to abandoned.
Error codes
Mutating a non-open cart returns one of:cart_already_checked_out (409), cart_already_converted (409), or cart_abandoned (410).

Abandoned-cart recovery

An hourly background sweep marks idle carts as abandoned and emits cart.abandoned. A cart is idle when it has had no mutation for 24 hours — every item, shipping, discount, or tax change resets the clock — or when its expiresAt has passed. Empty carts and externally-managed carts are never swept.

  • Webhook (always): every swept cart emits cart.abandoned to your subscribed webhook endpoints, so you can drive recovery from your own systems — including for anonymous carts you track by your own session. The payload carries the line items, totals, customer (a linked customer, or a guest with id: null when only the cart’s customerEmail was captured; null if neither), shippingAddress / billingAddress (if set), and the recoveryUrl.
  • Recovery email (when configured): Throttle sends the customer.cart_abandoned email automatically when the cart has a known customer email and the application has a cartRecoveryUrlTemplate set. The template is a storefront URL containing a {cartId} placeholder, e.g. https://shop.example.com/cart?c={cartId}. The email respects customer email opt-out / unsubscribe; in non-production environments it carries a [TEST] subject banner.
Create the cart at add-to-cart to enable recovery
Throttle can only recover carts it knows about. To get abandoned-cart recovery, create the Throttle cart (POST /carts) when the buyer first adds an item and mutate it on every change — the mirror pattern. If you only create the cart at checkout, abandonment happens before Throttle ever sees it and there is nothing to recover.