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.
1. Build the cart
Section titled “1. Build the cart”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”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.
3. (Optional) Lock the previewed prices
Section titled “3. (Optional) Lock the previewed prices”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:
GET /api/v1/scent/cart/checkout-preview?lock=trueThis 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.
4. Create the order
Section titled “4. Create the order”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.
Common mistake to avoid
Section titled “Common mistake to avoid”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.