The API
Stock Corrections has one endpoint.
POST /business-units/{businessUnitId}/stock-corrections/{itemId}
| Host | https://stock-corrections-api.retailsvc.com |
| Permission | scr.corrections.create |
| Success | 202 Accepted |
The authoritative technical reference is the OpenAPI specification. This page covers what the specification cannot tell you.
The business unit and the item are both in the path, so each call corrects exactly one item in one store. There is no bulk endpoint — to write off ten items, send ten calls.
The request
{
"quantity": -3,
"reasonCode": "DMG",
"transactionDateTime": "2026-09-23T09:14:02+02:00",
"comment": "crushed in the stockroom",
"serialNumber": "SN-99481",
"batchNumber": "B-2026-14",
"businessPartnerId": "supplier-4471"
}
| Field | Required | Meaning |
|---|---|---|
quantity | yes | How much the stock changes by, signed. Must not be zero. |
reasonCode | yes | The id of a configured reason code — see Reason codes. |
transactionDateTime | no | When the correction happened: an RFC 3339 date-time with an offset, such as 2026-09-23T09:14:02+02:00. Leave it out and the time the service receives the call is used. It may be in the past, but not more than a few minutes in the future. Send it if you might ever retry — see Retrying safely. |
stockType | no | Which stock the correction applies to: SalesStock or Returned. Leave it out for sellable stock. Any other value is rejected. See Which stock a correction moves. |
comment | no | Free text. Carried through to the event unchanged. |
serialNumber | no | For serial-tracked items, the specific unit being corrected. |
batchNumber | no | For batch-tracked items, the batch being corrected. |
businessPartnerId | no | The partner the correction relates to — a supplier whose delivery was short, for example. |
A Correlation-Id header is accepted and is generated for you if you leave it out. It comes back as
the response body and rides along on the published event, which makes it the easiest way to trace one
correction across systems. Together with transactionDateTime, it is also what makes a
retry safe.
🛑 quantity is a change, not a new level
"quantity": -3 means three fewer than before. It does not mean set the stock to −3.
This is the mistake that corrupts data rather than failing loudly, so it is worth being blunt:
- to write off 3 damaged units, send
-3 - to add 3 units found in the stockroom, send
3 - to say "there are 12 on the shelf", do not use this API at all — that is a stock count
Zero is rejected with 400, because a correction that changes nothing has nothing to record.
The Inventory app presents this as an Add / Remove toggle with a positive number, and converts it to a signed value before calling. If you are building your own client, that pairing is worth copying — asking a person to type a minus sign is how sign errors get made.
Decimals
quantity is a number, not an integer, so items sold by weight or length can be corrected in their
own unit of measure — -0.75 kg is a valid correction. Send the quantity in the item's sales unit of
measure; do not convert it to pieces.
Which stock a correction moves
By default, sellable stock. That is the right answer for almost every correction: damage on the shelf, theft, expiry, a mistake made while receiving.
The exception is returned goods. When Hii Retail keeps the stock, a customer return lands in a separate returned stock rather than back on sale, because nothing at the till can tell whether a returned item is fit to sell again. When it turns out not to be, write it off from there:
{ "quantity": -1, "reasonCode": "DMG", "stockType": "Returned" }
🛑 Leave stockType out on a returned item and two figures go wrong at once. Sellable stock drops
by an item that was never on the shelf, and the returned quantity stays standing. Nothing fails and
nothing flags it.
Only SalesStock and Returned are accepted. Stock that is on order, in transit or reserved belongs
to the document that created it (a purchase order, a transfer, a reservation) and is settled when that
document completes, so correcting it by hand would put it out of step with that document.
The reason code does not choose the stock: Damaged on its own still means sellable stock.
What the response tells you
202 Accepted means the correction was accepted and published as an event. It does not mean
stock has moved yet — that happens a moment later, in whichever system
applies it. Any other response means
nothing was published, and nothing will reach stock.
The distinction that matters to a client is between the two kinds of failure:
- a
4xxmeans the request cannot succeed as sent. Fix it — retrying it unchanged fails the same way. - a
5xxmeans the service could not complete it just now. Retry the same request.
Reason codes are checked when the call arrives
The reason code is looked up in the business unit's configuration for every call. A correction is
rejected with 400 when its code:
- does not exist for the business unit — including a misspelling or a difference in case;
- exists, but is not configured for stock corrections;
- is deactivated.
It is also rejected when the business unit has no reason codes configured at all. The message says which of these applies and names the code and the business unit. It is written for the person fixing the configuration — do not parse it.
If Customer Controlled Configuration (CCC) cannot be reached to make the check, the
response is 503, not 400:
nothing is wrong with the request, so retry it.
The check is made once, at the moment of the call. A correction accepted under a code that is later deactivated stays valid, and is still applied if its message is delivered again.
To avoid rejections in the first place, read the tenant's configured codes from CCC and send only codes you found there — see Reason codes.
Retrying safely
A retry is safe when it is the same request. Send your own Correlation-Id and a
transactionDateTime, and on a retry resend both unchanged, with the same body. The service derives
the correction's stockCorrectionId from what you sent, so the retry produces the same id and the
same event. Every consumer, Stock Transaction Processing included, recognises it as a repeat, and the
stock moves once.
So when a call times out, the connection drops or you get a 5xx, retry the same request. You do
not need to find out first whether the first attempt landed — that is the point.
| On a retry you send | The retry is |
|---|---|
your own Correlation-Id and a transactionDateTime, both unchanged | ✅ recognised as the same correction |
no Correlation-Id, or no transactionDateTime | 🛑 a second correction — the stock moves twice |
the same Correlation-Id with a new transactionDateTime | 🛑 a second correction |
"The same request" means the business unit and item in the path, every field in the body, the
Correlation-Id, and the credentials you call with, because the caller's identity is part of the
correction. Change any of them and it is a new correction — which is what you want when someone
corrects their own mistake with a second call.
⚠ Give each correction its own Correlation-Id. Reusing one across a batch run is fine as long as
the corrections differ: a different item, quantity, reason or time keeps them apart. But two
corrections that are identical in every field, sent under the same Correlation-Id and the same
transactionDateTime, cannot be told apart from a retry and are treated as one. If you genuinely mean
two, send them with different Correlation-Ids.
The Inventory app does this for you. A correction is written to a queue on the handset first, and every attempt to send it carries the same Correlation-Id and the moment the correction was made. So a correction captured with no signal can be sent hours later, retried as often as needed, and still land once, at the time it actually happened.
Validation
| Response | When |
|---|---|
202 | Accepted and published |
400 | quantity missing, not a number, or zero; reasonCode missing, or not a code that can be used (see above); transactionDateTime not a date-time with an offset, or more than a few minutes in the future; stockType other than SalesStock or Returned; empty businessUnitId or itemId |
403 | The token lacks scr.corrections.create, or is not valid for this tenant |
503 | CCC could not be reached to check the reason code. Retry the same request. |
other 5xx | The correction was not published. Retry the same request. |
⚠ The item is not validated. A correction for an item that does not exist in the catalogue is
accepted and published. What happens next is the consumer's decision — when Stock Transaction
Processing is master, whether an unknown item is stock handled depends on its own
Use default values if item is unknown
setting. Send catalogue item ids, and treat a rise in corrections for unknown items as a sign that an
integration is sending the wrong identifier.
Who the correction is attributed to
The correction records the user whose access token made the call, taken from the token itself — there is no field for it in the request and you cannot set it.
⚠ The userId on the event is an opaque identifier, not a username or an e-mail address. It
identifies the caller consistently, so you can group corrections by who made them, but it is not a
name and carries no personal data.
Do not build a display around it. For a system integration authenticating as itself, the value
identifies the integration rather than any person, so it will be the same on every correction you
send. If you need to know which of your users triggered a correction, record that on your side and
match it up with the Correlation-Id.