Skip to main content

API

Goods Received exposes twelve endpoints. One of them is for external systems; the rest exist to support a user interface. Knowing which is which saves a lot of wasted integration work.

For request and response schemas, see the API reference. This page is about what each endpoint is for.

Which endpoints are for you

If you areUse
An ERP, OMS, WMS or supplier portal sending deliveries inPOST /imports/deliveries/gs1 — and nothing else
A system that needs to know what was acceptedNot an endpoint — subscribe to External Events
Building a receiving UI or back office screenEverything below

🛑 Do not build an external integration on the business-unit endpoints. They are shaped around screens and a signed-in user: they take one action at a time, they are business-unit-addressed, and they will change as the app changes. GS1 import is the stable, documented, document-oriented contract for feeding deliveries in, and events are the contract for reading results out.

⚠ Every endpoint that writes returns 202 Accepted. See Asynchronous writes — a client that writes and immediately reads will sometimes see the old value.

Sending deliveries in

POST /imports/deliveries/gs1Import a GS1 despatch advice as a file upload

Tenant-addressed, not business-unit-addressed: the receiving store comes from the document. Fully covered in GS1 import.

Finding a delivery

GET /business-units/{businessUnitId}/deliveriesList the store's deliveries
GET /business-units/{businessUnitId}/deliveries/{deliveryId}One delivery, with its totals

The list endpoint is the service's search. It answers most operational questions directly, filtered by any combination of:

FilterAnswers
deliveryNoteId"Did this delivery note arrive?" — the one to use after an import
status"What is still waiting to be received?"
shipperId"What has this supplier sent us?"
orderId"Which deliveries covered this purchase order?"
itemId"Which deliveries contained this item?"
containerId"Which delivery is this pallet part of?"
orderByDeliveryDatetimeOldest or newest first

Each filter takes several values. Results are paged with take (10 by default) and skip, and the response carries total and hasMore so a caller knows whether to continue.

A single delivery also reports its rolled-up totals — expected and approved quantities, expected and approved amounts, the number of items and containers — so a summary screen does not have to fetch every line to show progress.

Working with lines

GET /business-units/{businessUnitId}/deliveries/{deliveryId}/linesList the delivery's lines
POST /business-units/{businessUnitId}/deliveries/{deliveryId}/linesAdd lines to a delivery

Lines can be filtered by gtin, itemId, status, orderId and containerId, and are paged the same way. Filtering by containerId is what a container-by-container receiving screen is built on; filtering by status shows what is left to count.

Each line reports its expected, approved and adjusted quantities, its adjustments with their reason codes, its item name, and who approved it and when.

Creating a delivery

POST /business-units/{businessUnitId}/deliveriesCreate a delivery note, then add lines separately
POST /business-units/{businessUnitId}/deliveries/ad-hocCreate a delivery and its lines in one call
PUT /business-units/{businessUnitId}/deliveries/{deliveryId}Change the header — date, comment, transport and customs amounts

The two creation endpoints differ in shape, not in which business situation they serve — either can record goods that arrived unannounced or goods with a paper note.

Create-then-add-lines is the two-call route: the header first, then lines, added as they are read off a document or off the goods themselves. This is what the Hii Retail store app uses, and the only path where the delivery date must be within the last 14 days; see Rules that are not configurable.

Ad-hoc is a one-call alternative that takes the header and every line together — the better fit for a caller that already holds the complete delivery, and it carries no 14-day date limit. The shipper is always recorded as an external business partner on this path, so it cannot express an inter-store movement.

Both record the shipper as a business partner, so neither can create a delivery from another store. Inter-store movements arrive on their own, from a completed store transfer — see Data sources.

The header update endpoint deliberately does not touch lines or quantities. Transport and customs amounts are delivery level costs, recorded here rather than spread across lines.

Could an external system create deliveries this way instead of importing GS1?

It is a fair question — these are ordinary permissioned endpoints, and a JSON body is easier to produce than a GS1 document. An ERP or OMS can call them. But it gives up four things, and the first two are usually decisive:

GS1 importAd-hoc / header-first
Purchase order lineRecorded per lineNot expressible — a line carries an order id, but no order line id
Retry safetyA repeat of the same document is ignoredA repeat of the same delivery note number is ignored
ShipperAn external partner or another storeAn external partner only
Addressing the storeA GLN or a business unit idA Hii Retail business unit id only
Scanning a container to approve itEvery logistic unit, at any nesting depthOne container per delivery, supplied by the caller

🛑 The purchase order line is the one that cannot be worked around. Goods Received publishes orderLineId on every line, and only a GS1 import can populate it. A system reconciling receipts against purchase order lines — matching a three-way invoice, or closing individual order lines — will find that field permanently empty on anything it created through the JSON endpoints. There is no second route to it.

Retries are safe on both. A delivery is identified by the receiving store, the delivery note number and the shipper, so re-sending the same call is recognised and ignored — exactly as a re-sent GS1 document is. If a request times out and you do not know whether it landed, send it again.

The delivery note number is yours to choose, and it must be distinct per shipper. When a supplier brought no reference at all — a local baker or butcher dropping goods off daily — generate one, and reuse that same value on any retry of that request. Reusing it across different deliveries from the same shipper would make the second look like a repeat of the first.

Where containers are concerned, both endpoints accept one container id for the delivery, which staff can then scan to find and approve it. Nested logistic units are a GS1 import feature — the JSON endpoints have no way to express a pallet holding cages.

Scanning to find a delivery only works if the id you supply is the one on the label. Receiving by scanning a container — read the label, see what should be inside, approve the lot — matches the scanned code against the container id recorded on the delivery. A GS1 import gets that right by construction, because the ids are the document's SSCCs. On the JSON endpoints the id is whatever you send: put the SSCC there and scanning works, put an internal box number there and the label will never match. Nothing validates it, because an internal reference is a legitimate thing to record — it simply is not scannable.

So use GS1 import for machine-to-machine delivery notes. Reach for the JSON endpoints when you are building a user interface, or for a one-off correction where no purchase order is involved and a human is watching the result.

Recording what arrived

PUT /business-units/{businessUnitId}/deliveries/{deliveryId}/lines/{lineId}/adjustmentsSet a line's adjustment and reason codes

This is where a difference between expected and delivered is recorded. It replaces the line's adjustments rather than adding to them, so a correction sends the new complete set — the arithmetic and the reason for the replace-rather- than-append behaviour are in Quantities and adjustments.

The request is rejected if the approved quantity does not equal expected plus adjusted, if a non-zero adjustment has no reason codes, if the reason codes do not sum to the adjustment, or if a reason code is not configured for that store. All four are immediate 400s, not asynchronous failures.

Approving

POST /business-units/{businessUnitId}/deliveries/{deliveryId}/submitApprove the whole delivery
POST /business-units/{businessUnitId}/deliveries/{deliveryId}/containers/{containerId}/submitApprove one container
POST /business-units/{businessUnitId}/deliveries/{deliveryId}/lines/{lineId}/submitApprove one line

Approval is what publishes data. All three take no request body — what is approved is already recorded on the lines. Which one to use, and what each publishes, is in Approving a delivery.

Approving something already approved is not an error and does not re-publish it, so a retry after a timeout is safe.

Conventions

AuthenticationBearer access token. Permissions per endpoint are listed in Configuration
TenantFrom the token, except on GS1 import which takes a Tenant-Id header
Correlation-IdAccepted on every endpoint. Supply your own to trace a request through the system; generated if omitted
Pagingtake (default 10) and skip, with total and hasMore in the response
Writes202 Accepted, no body
Reads200 OK