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 --> [*] 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 409cart_already_checked_out. -
converted— the linked draft order was paid and an order row was finalised (payment.capturedfired). Read- only; archived. -
abandoned— no activity for 24 hours (or past the cart'sexpiresAt), OR the linked draft order was cancelled before payment. An hourly background sweep performs the idle transition and emitscart.abandoned. Read-only. See Abandoned-cart recovery below.
Mutation rules
-
Every mutation increments
sequenceon the cart. Webhook payloads include sequence so subscribers can detect out-of-order delivery. -
POST
/carts/{id}/checkouttransitionsopen→checkout. Idempotent within the 24h Idempotency-Key window — same key replays the same draft order id. -
Direct transitions out of
checkout/converted/abandonedvia the cart API are not allowed. Cancel the linked order to roll the cart back toabandoned.
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.abandonedto 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 withid: nullwhen only the cart’scustomerEmailwas captured;nullif neither),shippingAddress/billingAddress(if set), and therecoveryUrl. - Recovery email: Throttle sends the
customer.cart_abandonedemail automatically whenever the cart has a known customer email. The email respects customer opt-out / unsubscribe; in non-production environments it carries a[TEST]subject banner.
Where the recovery link points
The link in the recovery email resolves through three tiers, first match wins:
-
cartRecoveryUrlTemplate— a URL on your own site containing a{cartId}placeholder, e.g.https://shop.example.com/cart?c={cartId}. Use this when you want the buyer to land on a specific page you control. -
checkoutLinkBaseUrl— your own host, e.g.https://shop.example.com. Throttle appends/r/<cartSessionToken>, so serve that route yourself with the cart SDK. - Throttle-hosted — when neither is set, the link points at Throttle’s own checkout. Nothing to configure.
Both settings live on the application, per environment, and are editable
from the dashboard’s Abandoned carts page. checkoutLinkBaseUrl also governs where quote links point,
so a merchant hosting their own buyer-facing pages sets it once.
A recovery link is single-use. Opening it converts the underlying cart session into a checkout session; a second click lands on the expired page rather than reopening a checkout the buyer may already have paid.
/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.