ServicesCart

Cart

Temporary purchase-intent carts and checkout preparation: line items backed by real Item Engine inventory reservations, per-line price locks, guest-cart merge, shareable carts, split-checkout grouping, and a freeze step that commits the holds and hands checkout off to your order flow.

When to reach for it: e-commerce and in-game purchase carts where stock must be held while the buyer decides, guest-to-user merge on login, checkout preparation that must survive stock races.

When not to: the durable inventory itself - definitions, containers, stock - lives in the Item Engine; carts only borrow it through reservations. Actual payment is out of scope platform-wide - checkout-ready ends with a handoff token for your own order and payment layer.

Concepts

  • Carts are ephemeral - each cart carries an expires_at (30 minutes by default), refreshed on every mutation. A background sweeper abandons overdue and inactive carts and releases their holds.
  • Every line holds real stock. Adding an item creates an Item Engine reservation against a seller's container; the line stores its snapshot (id, fencing token, expiry, status). Item Engine remains the stock authority - the cart never keeps its own counters.
  • Price locks are separate - each line locks its unit_amount for a price-lock TTL independent of the reservation TTL.
  • Lifecycle: only active and shared carts accept mutations (else 409 cart_not_active); freezing moves to checkout_ready, a merge leaves its source converted, the sweeper produces abandoned.
  • Add is add-or-update. Adding an item_id already in the cart grows that line; an idempotency_key makes retries safe instead of doubling.

Create a cart and add items

Adding a line names the Item Engine coordinates the stock is reserved from: the domain, the seller's container and its owner, and the definition (SKU).

snug cart create --currency USD
snug cart add docs4-world-pass --qty 2 --cart ryLqjXLpyDPNThWdTNDL \
  --domain docs4-shop --source-container cont_CmREXGyNvQKN \
  --definition HctVYVhHbdBKyrcsYFGF --inventory-owner docs-wave \
  --currency USD --unit-amount 4999
{
  "cart_id": "ryLqjXLpyDPNThWdTNDL",
  "line_id": "QXUhFTjaeQrSwMuBCfNd",
  "quantity": 2,
  "reservation_expires_at": "2026-08-28T13:02:09.141568Z",
  "reservation_status": "held",
  "totals": { "subtotal": 9998, "discount": 0, "estimated_tax": 0, "total": 9998, "currency": "USD" }
}

snug cart get --cart <id> returns the full cart with per-line reservation and price-lock snapshots; snug cart view shows the CLI's current cart (cart add without --cart starts one). Only the owner or a platform admin can touch a cart - anyone else gets 403 insufficient_permissions.

Reservations hold real stock

The hold is not advisory. With 50 units in the container and 2 already held by the cart above, a second cart asking for 49 was refused. Changing a line re-reserves at the new quantity; removing it gives the stock back immediately ("Line removed and reservation released"). Reservations default to strict mode; --reservation-mode soft or availability_check relax the hold.

snug cart add docs4-world-pass --qty 49 --cart RtqjeSPBjYZBpsYerbny ...
# 409 reservation_conflict: "only 48 of HctVYVhHbdBKyrcsYFGF available to reserve"
snug cart update-item --cart ryLqjXLpyDPNThWdTNDL --line-id QXUhFTjaeQrSwMuBCfNd --qty 3
snug cart remove --cart ryLqjXLpyDPNThWdTNDL --line-id KuShdFrFVBfYyQJXscqE

Keeping a cart valid

Reservations and price locks expire on their own clocks (15 minutes each by default), so an open cart goes stale. revalidate reports per-line health without mutating anything; price-lock re-locks prices:

snug cart revalidate --cart ryLqjXLpyDPNThWdTNDL   # per-line reservation_valid / price_lock_valid + "valid": true
snug cart price-lock --cart ryLqjXLpyDPNThWdTNDL   # returns refreshed_line_ids

Merging a guest cart

On login, fold a guest cart into the user's cart. Lines merge by item_id, per-item conflicts come back in a conflicts array with reason codes, and the source cart is drained: its status becomes converted.

snug cart merge --cart ryLqjXLpyDPNThWdTNDL --source-cart RtqjeSPBjYZBpsYerbny
# { "merged_lines": 1, "conflicts": [], "totals": { ... } }

Sharing

share mints a permission-scoped token (view, clone, or contribute) with a TTL, and flips an active cart to shared; --revoke deletes the token and flips it back.

snug cart share --cart ryLqjXLpyDPNThWdTNDL --mode view --ttl-seconds 3600
# { "share": { "token": "share_ATSMbycdFWtZhzuctshc", "permission": "view", ... } }
snug cart share --cart ryLqjXLpyDPNThWdTNDL --revoke

The token is state for your application layer to redeem - no cart endpoint accepts it in this version, so sharing does not by itself open the cart: a second user's direct GET still fails with 403 while sharing is enabled (verified). A TTL above the maximum fails with 400 share_ttl_exceeded.

Checkout

split-checkout is a read-only preview of how the lines partition for downstream orders - group by seller, warehouse, currency, availability, or fulfillment (default inventory_source):

snug cart split-checkout --cart ryLqjXLpyDPNThWdTNDL --group-by currency

checkout-ready is the point of no return: it verifies every reservation is still held, commits them in the Item Engine (stock is now consumed), freezes the cart, and returns a handoff token for your order flow. Raw HTTP, enveloped:

curl -X POST -H "Authorization: Bearer $SNUG_API_TOKEN" \
  http://localhost:4000/api/v1/carts/ryLqjXLpyDPNThWdTNDL/checkout-ready
{
  "status": 200,
  "msg": "OK",
  "data": {
    "cart_id": "ryLqjXLpyDPNThWdTNDL",
    "status": "checkout_ready",
    "checkout_token": "checkout_AQemDHpjrJFRxxDdqLxe",
    "totals": { "subtotal": 20995, "discount": 0, "estimated_tax": 0, "total": 20995, "currency": "USD" }
  }
}

A frozen cart rejects further mutation (409 cart_not_active), and freezing an empty cart or one with a lapsed reservation fails (400 / 409 reservation_conflict) - revalidate first if the cart has been sitting.

Abandonment and recovery

When a cart is abandoned - TTL expiry, inactivity, or a merge draining the source - its holds are released and a recovery record is written with reason (ttl_expired, inactivity, merged, cancelled), item_count, estimated_value, and recovery_eligible, the raw material for win-back flows. Both endpoints require a platform admin token:

snug cart abandoned --since 2026-08-28T00:00:00Z
snug cart sweep-abandoned                          # force a sweep pass now; returns { "swept": n }

The list paginates and sorts (created_at, item_count), with repeatable filter expressions in the shared field:operator:value grammar.

Limits and configuration

100 lines per cart, 999 units per line, 30-minute cart TTL, 15-minute reservation and price-lock windows, 7-day share-token maximum, and the sweeper's cadence are all tunable via the CART_* variables in the Cart CONFIG reference.

Reference

On this page