Cart Lifecycle
A Commerce cart progresses through distinct stages: creation, mutation, rendering, and completion.
States
| State | Description |
|---|---|
open | Active cart, can be modified |
completed | Cart has been completed (order created) |
rejected | Not actively used. Carts are generally retained for a limited time before automatically removed |
Creating a Cart
Carts are created via the Create Cart endpoint with a profile key and locale:
POST /ingress/commerce/carts
Content-Type: application/json
{
"profileKey": "default",
"locale": "sv_SE"
}The profile key determines the cart's currency, default country, and allowed shipping countries. The locale must be configured in the system registry.
The response contains the new cartId — all subsequent operations use this ID.
Captured client context
At creation time, Commerce captures request context from standard headers and stores it on the cart. These are forwarded onto the resulting order on completion (as cart:* dynamic fields) for conversion tracking and auditing:
| Header | Cart field | Order field |
|---|---|---|
User-Agent | client:userAgent | cart:client:userAgent |
X-Hantera-Client-IP | client:ip | cart:client:ip |
Origin / Referer | storefrontUrl | cart:storefrontUrl |
The client IP is the resolved shopper IP (see HTTP Ingress Parameters → Client IP Address). It is deliberately captured at cart creation rather than completion, because completion may be triggered by a PSP from a different remote address. The order exposes it as the queryable graph field clientIp, so orders can be filtered by originating shopper IP.
Mutating a Cart
Cart mutations are performed via the Commerce ingresses. Most mutations return the rendered cart.
Adding and Managing Items
# Add an item
POST /ingress/commerce/carts/{cartId}/add-item
{ "productNumber": "TSHIRT-001", "quantity": 2 }
# Change quantity
POST /ingress/commerce/carts/{cartId}/set-quantity
{ "cartItemId": "...", "quantity": 3 }
# Remove an item
POST /ingress/commerce/carts/{cartId}/remove-item
{ "cartItemId": "..." }Setting Customer Details
# Set email
POST /ingress/commerce/carts/{cartId}/email
{ "email": "[email protected]" }
# Set shipping address
POST /ingress/commerce/carts/{cartId}/address
{ "address": { "countryCode": "SE", "city": "Stockholm", ... } }Custom Fields
Custom fields allow PSP apps and other integrations to store metadata on the cart:
# Set a custom field
POST /ingress/commerce/carts/{cartId}/set-field/paymentProvider
{ "value": "stripe" }
# Remove a custom field
POST /ingress/commerce/carts/{cartId}/remove-field/paymentProviderCustom fields appear in the rendered cart under the fields property, and are projected onto the created order as cart:<key>. Apps store their own state on the cart the same way, using key prefixes to control what reaches the order — see Cart Dynamic Fields.
Rendering
Every mutation endpoint returns the rendered cart — a normalized structure with computed prices, totals, and tax. The rendering pipeline:
- Sends a
completemessage to the cart actor in preview mode (no persistent changes) - This triggers all order creation rules (including price lookup, discounts, shipping)
- Queries the preview result for order totals and line items
- Maps the data into the rendered cart structure
The rendered cart includes:
orderTotal,orderTaxTotal,shippingTotal,productTotal— computed totalsitems[]— each item withunitPrice,total,tax,discountfields— custom fields set via theset-fieldendpointcartState— current state (open,completed,rejected)
After rendering, the OnCartMutation hook fires, enabling other apps to react. See Hooks.
Real-Time Updates
Subscribe to the Cart Events SSE endpoint to receive the rendered cart in real-time whenever the cart state changes:
const eventSource = new EventSource(
`${baseUrl}/ingress/commerce/carts/${cartId}/events`
)
eventSource.onmessage = (event) => {
const cart = JSON.parse(event.data)
updateUI(cart)
}This is essential for checkout flows where the cart may be modified by background processes (e.g., PSP synchronization).
Completing a Cart
Cart completion is typically triggered by a PSP app after payment authorization. The PSP app:
- Creates a Payment actor with an authorization
- Links the payment to the cart via a
createRelationcommand - Sends a
completemessage to the cart ticket actor
When a cart is completed:
- The
cart-to-orderrule fires, creating a real order from the cart's preview, and projecting the cart's prefixed dynamic fields onto the order and its delivery - The cart state changes to
completed - The
orderIdandorderNumberbecome available in the rendered cart - SSE subscribers receive the final cart state
See PSP Integration for the recommended completion pattern.
Cart Profiles
Cart profiles are configured in the Commerce app registry under apps/commerce/profiles/{key}. Each profile defines:
| Setting | Description |
|---|---|
currencyCode | Currency for the cart (e.g., SEK, EUR) |
defaultCountryCode | Default shipping country |
allowedCountries | List of allowed shipping country codes |
channelKey | Sales channel key for the order |
Retrieve available profiles via the Get Cart Profiles endpoint.