Skip to main content

Substitutions

When a picker cannot find what the customer ordered, they can offer a replacement rather than simply reporting the item unavailable.

Click and Collect runs an open substitution policy: the picker decides. We do not maintain a fixed table of permitted swaps, because the person holding the product in a real store on a real day is better placed to judge than a rule written in advance. What we do instead is make the decision faster — the service tracks which replacements have been chosen before, at customer, store and tenant level, and offers those first.

For those suggestions to be any good, your items need to be in the system. The minimum item information can be imported through the Item Input API.

Adding a replacement​

POST /api/v1/business-units/{businessUnitId}/orders/{orderId}/lines/{lineId}/substitutions
{
"lines": [
{
"itemId": "item-987",
"description": "Own Brand Baked Beans 400g",
"quantity": 2,
"unitOfMeasure": "EA"
}
]
}

itemId, description, quantity and unitOfMeasure are required; itemIdentifier and imageUrl are optional. More than one replacement can stand in for a single line — a customer who ordered one large jar can be given two small ones.

You do not send a price. Click and Collect looks up the replacement's current regular sales price and tax group in the store that is picking, from the product catalogue, and stores them with the replacement. A price in the request is ignored. The customer order uses them to price the substitute, and a substitute never costs the customer more than the item they ordered. See What a substitute costs.

ResponseWhen
422 Unprocessable Entitythe item has no current sales price in this store. It cannot be sold here, so choose another replacement
503 Service Unavailablethe product catalogue could not be reached. Nothing was stored; retry

Removing one takes the item id in the path:

DELETE /api/v1/business-units/{businessUnitId}/orders/{orderId}/lines/{lineId}/substitutions/{itemId}

When the customer does not allow a substitute​

Each order line carries the customer's choice as isSubstitutionAllowed, set when the order was placed. See customer choices.

isSubstitutionAllowed on the lineWhat the picker can do
true, or not setoffer a replacement
falsepick the item or mark it unavailable; no replacement is offered

🛑 A line with false refuses a replacement. The picker's app does not offer one, and a direct POST …/substitutions for that line is answered with 409 Conflict. Nothing is stored or published.

The customer's choice per line decides whether a replacement can be offered. When isSubstitutionAllowed is true or not set, the picker's line screen offers ADD and SCAN for a replacement. When it is false, the app shows no substitution option, and a direct POST to the substitutions endpoint for that line is answered 409 Conflict, with nothing stored or published.

The choice can change while the order is being picked. If an update to the order switches a line from allowed to not allowed after picking has started, it arrives as a late change, and the picker sees what changed. Replacements already added are not removed automatically. The picker decides what to do with them.

🛑 What a substitution does to the line​

The original line does not disappear, and it does not become a different line. It stays, and its status changes to record that a replacement is involved:

What happenedLine status
a replacement offered, none of the original foundSUBSTITUTED
a replacement offered and some of the original foundPARTIALLY_SUBSTITUTED

The substitution outranks the picked quantity. Once a replacement is on the line, the status describes the substitution rather than how much of the original was picked — so a substituted line is never reported as PICKED or UNAVAILABLE.

Removing the last replacement puts the line back where its picked quantity alone would have put it — PENDING, PICKED or UNAVAILABLE. Substituting is not a one-way door.

See order statuses for how these combine with the picking statuses.

What this means if you are consuming the events​

An ERP or OMS reads substitutions from Customer Order's events, where each substitute is a line of its own (see below). The points before that apply to a system that consumes Click and Collect's events directly.

🛑 A substituted line still counts as fulfilled. SUBSTITUTED is a resolved state — the order can be completed with it, and the picker is not expected to do anything further. If you are reconciling what the customer ordered against what they received, do not treat a substituted line as a shortfall; the customer is getting something, it is simply not the item on the original line.

The original item and the replacement are both on the line, so an integration that only reads the ordered item will miss what the customer is actually receiving. Read the substitutions, not just the line.

A substitution stays on the original line. Anna ordered Socks 3-pack grey ×2 and the shelf had one, so the picker picked one grey pack and added one black pack as a replacement. The line keeps the grey item with pickedQuantity 1, carries the black pack in its substitutions, and its status is PARTIALLY_SUBSTITUTED. Read the substitutions as well as the line to see what Anna receives.

In Customer Order the replacement becomes a line of its own. When a picked customer order reaches Customer Order, each substitute is added to the order as a new line, linked to the original by substituteOfLineItemId, and the original line's quantity drops by the substituted amount. Anything that consumes Customer Order's events, such as an OMS, sees the order gain a line. See Substitutions add order lines.

A substitute becomes a line of its own. Anna ordered socks grey ×2, and the shelf had one. The picker took one black pack instead. On the order, the grey line drops to quantity 1 with cancelledQuantity 1 and a new black line has quantity 1. On the fulfillment, the grey line keeps orderedQuantity 2 and quantity 1 picked, and the black line has orderedQuantity 0, quantity 1 and substituteOfLineItemId pointing at the grey line.