Tie each unit to its B2B order
By the end of this page, you will know how to give your distributors a territory, have your logistics provider scan each unit at picking, and receive an alert naming the order and the distributor when a unit is scanned outside its territory.
On this page
Your logistics provider picks your B2B orders. Each order goes to a distributor with an exclusive contractual territory. If a unit of that order is later scanned by a consumer outside that territory, you want to know which unit, from which order, and which distributor it was meant for.
For that, each unit is scanned once when it goes into the box. That scan ties it to the order. The flow has four steps.
This flow needs the Compliance + Identity offer, with a QR code or a chip per
unit. On the Compliance offer, creating an order and every scan are refused
with the code OFFER_EXCLUDES_IDENTITY.
#1. Give each distributor a territory
In the console, Distribution screen, "Retailers" tab, create the distributor
with the type "Distributor". The "Territory" field takes its countries, as
two-letter ISO codes separated by commas, for instance DE, AT. The retailer
code, for instance DIST-DE, is the one your logistics provider will send.
A distributor without a territory is not an error: its units are then checked against your brand's authorized countries, as before.
Every creation or change leaves one line in the audit log, with the old and the new territory.
#2. Open the order
An order carries your order reference, the one from your ERP or your logistics provider, and the distributor. The same reference sent twice gives the same order: a provider that replays a call creates no duplicate.
Three ways to open it, your choice.
- Through the API, with
POST /partner/dispatch/shipments. - In the console, "Shipments" tab, "New shipment" button.
- Through the CSV import of step 3, which creates the orders it names.
#3. Scan each unit at picking
Each scanned unit goes into the order. A unit can only be in one active order at a time.
#Through the API, from the provider's system
The provider sends units in batches of 1000 at most. Each unit gets its own
result, and an error on one unit does not keep the others out. The results are
detailed in
POST /partner/dispatch/shipments/{shipment_id}/units.
The API key carries the dispatch:write scope. Create it in the console,
Settings, Developers, and tick that scope only: the key can do nothing else.
# 1. Open the order
curl -X POST "https://api.sealtrust.io/v1/partner/dispatch/shipments" \
-H "Authorization: Bearer st_live_0000000000000000000000000000000000000000000000" \
-H "Content-Type: application/json" \
-d '{"order_ref": "SO-2026-1042", "distributor_code": "DIST-DE"}'
# 2. Send the units scanned into the box, up to 1000 per call
curl -X POST "https://api.sealtrust.io/v1/partner/dispatch/shipments/318/units" \
-H "Authorization: Bearer st_live_0000000000000000000000000000000000000000000000" \
-H "Content-Type: application/json" \
-d '{"units": ["H897RFWJ4972", "https://sealtrust.io/p/Y5T2VGGF2NP9"]}'
# 3. The order left the warehouse
curl -X POST "https://api.sealtrust.io/v1/partner/dispatch/shipments/318/close" \
-H "Authorization: Bearer st_live_0000000000000000000000000000000000000000000000"Units are read in the forms the label carries: the 12-character serial, the
link https://sealtrust.io/p/{serial}, the GS1 Digital Link or the signed QR
link.
#Through the console picking page
For a small volume, the picker opens the console on a phone, "Shipments" tab, "Picking page" button. They pick the order, then scan:
- with the phone camera;
- with a handheld scanner, which types the code into the field then presses Enter;
- or by typing the serial.
Every scan answers at once: green and a high beep when the unit is in the box, red and a low double beep otherwise, with the reason. The count of units in the box updates with every scan.
#Through a CSV file
If your provider exports files, import them in the "Shipments" tab, "Import a CSV" button. The file has three columns, in any order, separated by commas or semicolons, one line per unit:
order_ref,distributor_code,serial
SO-2026-1042,DIST-DE,H897RFWJ4972
SO-2026-1042,DIST-DE,Y5T2VGGF2NP9
SO-2026-1043,DIST-BE,K2M8Q0R4T6V1| Column | Content |
|---|---|
order_ref | your order reference. A missing order is created. |
distributor_code | the distributor's code. One order names one distributor. |
serial | the unit's serial, or its link. |
The file weighs 2 MB at most and has 20000 lines at most. We read UTF-8 as well
as the CSV format of French Excel (Windows-1252, semicolons, a first sep=;
line accepted). The result lists each refused line with its number and its
reason.
Each line is judged on its own. A refused line does not stop the others:
| Reason | What causes it |
|---|---|
missing_value | one of the three columns is empty. |
invalid_value | an order reference longer than 100 characters, a control character or an unreadable byte, or a reference starting with =, +, - or @, which a spreadsheet would run as a formula. |
database_error | the order could not be recorded. The orders of the file already processed stay recorded, the next ones are tried. |
Orders are recorded one by one, not as a block: a file of 2000 orders with one problem records the 1999 others. Every import leaves one line in the audit log, even when it stops half way.
#A unit already in another order
It is refused with already_allocated, and the answer names the other order.
Two ways to fix it: remove it from the other order, or move it through the API
with reallocate and a reason. The old row is never erased: it is closed with
the date, the author and the reason, and the audit log keeps the move.
#4. Receive the alert
When a consumer scans a unit tied to an order, through the QR code or the chip,
we compare the country of the scan with the distributor's territory. Outside the
territory, you receive a gray-market alert through the same channels as before:
the console in real time, Slack or Teams, and the product.gray_market webhook.
The Slack or Teams message names the order, the distributor and its territory. The webhook keeps all its former fields and gets new ones.
{
"product_id": 4096,
"product_name": "Eau de parfum 50 ml",
"country": "FR",
"city": "Lyon",
"authorized_countries": ["FR", "DE", "AT", "BE"],
"source": "qr",
"nfc_auth_log_id": 88213,
"retailer_id": null,
"shipment_id": 318,
"order_ref": "SO-2026-1042",
"ship_date": "2026-10-09",
"distributor": {"id": 77, "code": "DIST-DE", "name": "Acme Distribution GmbH"},
"expected_territory": ["DE", "AT"],
"territory_source": "distributor",
"scan_country": "FR",
"country_source": "ip"
}| Field | What it says |
|---|---|
order_ref, shipment_id, ship_date | the unit's order. null when the unit is in no order. |
distributor | the distributor, with its code and its name. null outside an order. |
expected_territory | the countries where the unit was expected. |
authorized_countries | your brand's authorized countries, or its model's, as before orders were added. For a unit of an order, the zone checked is expected_territory, not this one. |
territory_source | distributor for the distributor's territory, authorized_countries for your brand's countries. |
scan_country | the country of the scan. |
country_source | where that country comes from: gps (the phone), ip (the network), relay (the iCloud relay region), declared (a sale declared by a retailer). |
A country read from the network is an approximation: a travelling consumer, or
one behind a private network, may appear elsewhere. Read country_source before
calling a distributor.
The order's page, in the console, shows for each unit its latest consumer scan and whether it is in the territory. Each product's page shows its order and its distributor.
#5. See what was never scanned
The order's page tells how many of its units a consumer scanned since they were picked, and how many never were ("Never scanned"). A scan made before picking, in the warehouse for instance, does not count: it says nothing about where the unit was sold. Only a successful verification counts, never a failure nor the telemetry of a chip read.
The "See on the scan map" link opens the scan map filtered on this order: each scanned unit is placed there, with its card (product, lot, distributor, territory, scan country, inside or outside the zone, where the place comes from). The map's "Coverage" panel gives the same figures per distributor, per order and per lot.
Was this page helpful?
Your answer opens a pre-filled email in your mail app, addressed to contact@sealtrust.io. You read it over before sending it.