Docs
Firmly Agentic Commerce
Set theme to dark (⇧+D)

Lifecycle and States

A Firmly purchase moves through several state machines — session, cart, checkout, order — each with its own lifetime and transition rules. This page covers each lifecycle so a destination engineer knows what state exists at any moment and how long it lives.

​​ Session lifecycle

State Meaning Duration
Bootstrap requested POST /browser-session made; awaiting response Sub-second
Active Token issued, device_id assigned ~1 hour from issue
Expired (renewable) Token past exp; prior session state still retained Renewable while session state persists (~7 days from last activity)
Expired (state cleared) Session state garbage-collected Permanent — must bootstrap fresh

Renewal: re-POST to /browser-session with the prior (expired) token in the body. As long as the session state is still retained, Firmly returns a new token with the same device_id. There’s no separate server-enforced renewal window — renewal works while the session state persists (~7 days from last activity), after which a fresh bootstrap is required.

​​ Cart lifecycle

State Meaning Triggers
active Cart exists, items can be added/removed/modified POST /cart/line-items (first item creates the cart implicitly)
item_not_shippable One or more line items can’t ship to the chosen address Set during POST /cart/shipping-info when an item has no valid shipping option
checkout_blocked Cart has validation errors preventing checkout Out-of-stock items, expired promotions, shipping unavailable — see Cart lifecycle
pending Reserved — no current cart operation produces it Treat as an unexpected state; don’t branch checkout logic on it
submitted Order successfully placed at merchant Merchant returned order ID

Cart persistence: cart state is held in Firmly’s session store for ~7 days from the last activity. After 7 days idle, the cart is garbage-collected; the destination must start a fresh cart.

​​ Order placement lifecycle

POST /payment/place-order is the moment the cart transitions to submitted. The response includes:

Field What it represents
cart_status submitted on success. A payment failure returns a 422 CreditCardDeclined error rather than a status value
cart_id Firmly’s stable reference for the cart
urls.thank_you_page Merchant’s own confirmation URL (populated post-success)
platform_order_number Merchant’s native order number — capture for reconciliation

Post-submitted, Firmly’s scope ends. The order is the merchant’s responsibility for fulfillment, tracking, returns, and refunds.

​​ Idempotency window

On the UCP protocol bridge, Idempotency-Key values are cached for 24 hours: the same key within the window returns the cached response, and the operation runs at most once.

The core REST endpoints accept the header but do not deduplicate on it today — a REST retry re-runs the operation. Before retrying a failed place-order over REST, re-read the cart and check cart_status: if it’s already submitted, the order went through. See Errors & Conventions → Idempotency.

​​ State diagram — a typical purchase

​​ What expires when

Token / state Expires after Recovery
Session JWT ~1 hour Renew via re-POST to /browser-session
Cart state 7 days idle Start fresh cart
Idempotency key 24 hours Different key = new call
Payment public key cache Rotated periodically; cadence not public Re-fetch via GET /payment/key on invalid-key encryption error
submitted order Permanent (no Firmly-side TTL) n/a — merchant’s OMS owns post-order

​​ Destination-side state to track

A destination engineer should hold:

State Why
Current session JWT + device_id For subsequent API calls and renewal
Current cart state (last response) Avoids re-fetching for read-only renders
Idempotency key per logical operation Stable for retries within 24h
Order success indicators (cart_status, merchant order ID) For reconciliation and reporting
Attribution params (UTM, click-IDs) Persisted in session for inclusion on place-order