Skip to content

Cart preview & price locks

Scent orders are always created from the caller’s own cart — order lines are never part of the create request itself. The flow is: build a cart, preview it, optionally lock the preview’s prices, then create the order.

Terminal window
POST /api/v1/scent/cart/items
{ "variantId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "quantity": 2 }

Pass ?customerRef= on every cart call if you’re tracking separate in-progress carts per one of your own end customers (max 128 characters); omit it for a single, bare partner-level cart.

2. Preview before you ever create an order

Section titled “2. Preview before you ever create an order”
Terminal window
GET /api/v1/scent/cart/checkout-preview
{
"state": "SUCCESS",
"payload": {
"cartId": "...",
"lines": [
{ "variantId": "...", "quantity": 2, "ok": true, "issue": null, "listPrice": 249.0, "...": "..." }
],
"canCheckout": true,
"cartIssue": null,
"pricedAt": "2026-08-02T10:00:00Z",
"lockId": null,
"lockExpiresAt": null,
"chargedWithVat": 498.0
}
}

Always call this before POST /api/v1/orders/scent. It’s the only way to find out, ahead of time, whether every line in the cart can actually be checked out. canCheckout is true only when every line reports ok: true, the cart is non-empty, and there’s no already-pending order on it. Per-line issue is one of:

Issue Meaning
UNAVAILABLE The variant isn’t currently sellable.
INSUFFICIENT_STOCK Available stock is less than the requested quantity.
PRICE_BELOW_FLOOR The line’s margin would fall below the platform’s required minimum.
NOT_PRICED No active price exists for this variant/partner combination.

A cart-level cartIssue: "CART_HAS_PENDING_ORDER" means an order already exists against this exact cart and hasn’t reached a terminal state yet.

Prices can move between when you preview and when you actually create the order — a vendor cost change, a margin-floor recalculation, a promotional price update. If your checkout has any delay between “show the customer a price” and “charge them” (a payment step, a confirmation screen), lock the preview:

Terminal window
GET /api/v1/scent/cart/checkout-preview?lock=true

This only succeeds in taking a lock when the preview itself reports canCheckout: true — a broken basket is never locked. The response adds:

{
"lockId": "b8f1c2a0-...",
"lockExpiresAt": "2026-08-02T10:15:00Z"
}

The lock is valid for 15 minutes from the moment it’s taken. Any cart mutation (add, change quantity, remove, clear) invalidates the active lock immediately — a lock always reflects the exact cart contents at the moment it was taken, never a moving target. Only one lock can be active per cart at a time; taking a new one supersedes the old.

Terminal window
POST /api/v1/orders/scent
{
"partnerId": "...",
"externalReference": "po-2026-04-1029",
"geoScopeId": "...",
"sagaId": "...",
"shippingAddress": { "recipient": "...", "line1": "...", "city": "...", "country": "SA", "phone": "..." },
"lockId": "b8f1c2a0-..."
}

If you omit lockId, the order is priced against the CURRENT live prices at the moment of creation (re-running the exact same checks the preview ran). If you pass a lockId, the order is charged exactly the locked per-line prices — but the margin-floor check still re-runs against those locked figures, so a lock does not bypass the floor; it only protects you from routine price drift, not from a floor violation.

A lockId that’s unknown, doesn’t belong to your own cart, has expired, or no longer matches the cart’s current version (because the cart was mutated after the lock was taken) is rejected as 400 VALIDATION_PRICE_LOCK_INVALID or 400 VALIDATION_PRICE_LOCK_EXPIRED — both collapse to a generic “the lock isn’t usable” signal by design, so treat either the same way: re-preview (optionally re-lock) and retry.

Don’t skip the preview call “to save a round trip.” Order-create will still enforce every check the preview reports, so skipping it doesn’t make checkout faster — it only means you find out about a PRICE_BELOW_FLOOR or INSUFFICIENT_STOCK issue from a failed order-create instead of a cheap read, and you lose the ability to show the customer accurate pricing before you charge them.