Skip to main content

The basket model

Two halves: what you sent, and what it costs

The single most useful thing to understand about UBM is that a basket has two distinct halves, and the API exposes both:

HalfWhat it isWho writes it
The input basketWhat you asked for: items and quantities, coupons applied, the loyalty membership, the customer, fulfillments, tendersYou, through the endpoints
The calculated resultWhat it costs: priced lines, unit discounts, the promotions that fired, coupon outcomes, loyalty outcomes, and tax groupsThe calculation engine, whenever a price-affecting part of the basket changes

🛑 Tax groups are empty on business units configured for the PROMOTION engine — it returns price without tax. See How pricing works.

You never write the calculated result, and you should never derive one yourself. If a total looks wrong, the question is which engine produced it and from what input — not whether UBM added it up correctly.

The calculated result carries the promotions and discounts that actually applied. A coupon present in the input basket but absent from the calculated result was not honoured — that is the signal to look for, rather than assuming a coupon in the basket means a discount was given.

Status

StatusMeaning
ACTIVEThe basket can be read and changed. Every basket starts here
COMPLETEDThe basket has been checked out and a customer order exists

🛑 A COMPLETED basket is locked, not merely discouraged. Every modifying operation — items, coupons, customer, loyalty, fulfillments, tenders, a further checkout — returns 400 Bad Request with "Cannot modify a completed basket". Reads still work, and delete still works, so a completed basket can be cleaned up but never revived. To sell the same goods again, create a new basket.

There is no cancelled or expired status. A basket you no longer want is deleted.

How long a basket lives

A basket is not automatically expired or cleaned up. There is no abandoned-cart sweep and no tenant setting that removes baskets, so an unfinished basket stays ACTIVE and fully usable until you delete it.

Storage is reclaimed only after 18 months without modification, and that period is sliding — most calls restart it. A cart a shopper returns to every few weeks therefore persists indefinitely, and a long-lived webshop basket never needs recreating.

Treat 18 months as the retention period today, not a guarantee to build against. It may change. If your integration depends on baskets surviving a particular length of time, tell us rather than inferring it from this number.

What restarts each clock

Two independent clocks run on a basket, and the same call does not necessarily restart both. Retention restarts when a call writes the basket record itself; the two-hour price clock restarts when a call produces a new calculated price. Creating or replacing the basket restarts retention; beyond that:

CallRestarts 18-month retentionResets the two-hour price clock
Recalculateyesyes
Add, change or remove an itemyesyes
Add a couponyesyes
Remove a couponnoyes
Add or change loyaltyyesyes
Remove loyaltynono
Set or clear the customeryesno
Any fulfillment callnono
Any tender callnono

The rows that restart neither are narrow: a basket would have to sit untouched for 18 months except for those calls before retention were affected. For a cart being built or revisited, every call a storefront normally makes restarts it.

🛑 A long-lived basket still meets the two-hour price clock. The basket survives; its calculated price does not. A shopper returning to an old cart can read it, but checkout fails until you recalculate — and the total can legitimately have changed in the meantime. See Checkout.

Sub-resources

Each of these is separately addressable. Only items, coupons and loyalty reprice the basket — fulfillments, customer and tenders are stored without calling an engine, because none of them changes what the goods cost.

Items

Lines carry the item, quantity, unit of measure, and — after calculation — the resolved name, identifier, regular and actual unit prices, tax group, and any deposit or fee rules that apply. Quantity is fractional, so weight- and measure-based items work without a separate mechanism.

There are two ways to write lines, and they behave very differently:

  • One line at a timePUT …/items/{itemId} changes that line and leaves the rest alone.
  • The whole set at oncePUT …/items takes the complete item set the basket should hold.

🛑 The bulk call is a replace, not an append. The array you send is the basket afterwards: an item you leave out is deleted, and an empty array clears the basket. It is the desired end state, not a list of changes.

Prefer it when you hold the whole cart anyway — a storefront usually does. It is also markedly faster, because it works out what actually changed and only touches those lines: re-sending a 20-line cart with one quantity edited costs a single engine call, not twenty.

Quantity is replaced, never accumulated — on both endpoints. Sending quantity: 3 for a line holding 5 leaves it at 3, not 8. Worth pausing on, because "add to cart" in a storefront usually means increment.

The basket-level PUT still carries no lines. See Integrating with UBM.

Resolution of names, identifiers and prices against the catalogue is described in How pricing works. The short version: what you send is used, and the catalogue fills in what you omit — so send the price your storefront displayed.

Coupons

Applied and removed individually, and each reprices. Whether a coupon actually discounts anything is decided by the engine and visible in the calculated result, not in the input basket.

Loyalty

The loyalty membership the basket is priced for. Registering loyalty can change prices, so it reprices; it is not merely an attribute recorded for later.

Customer

Who is buying. Held separately from loyalty, because a basket can have a customer without a membership and a membership without full customer details. Does not reprice — customer details do not affect price; loyalty membership is what can.

Fulfillments

How the basket reaches the customer. A basket can have several fulfillments — for example part collected and part delivered — and each can carry its own destination business unit and its own line items. Does not reprice. Fulfillments matter at checkout, where tenders are allocated to them.

Tenders

The means of payment, with authorization data where the tender type needs it (card and gift-card authorizations are held separately). Does not reprice. Tenders are validated at checkout against the business unit's active tender configurations, and where the basket also has fulfillments their total must match the order total — see Checkout.

External identifiers

A basket and its fulfillments can carry external identifiers — your own references, kept alongside UBM's ids so you can correlate a basket with an order in your own systems. UBM stores and returns them without interpreting them.

Additional properties

Baskets accept additional properties for data the model does not have a field for. They are carried through and returned, not interpreted, and they do not influence pricing.