Batch & Best-Before-Date Management on Order Lines
Audience: developers integrating a merchant's order/ERP system with Logitrail's Order API, who need to control exactly which physical stock (which incoming batch, or stock with a specific best-before date) is used to fulfill an order line.
This guide explains how to restrict an order line to a specific batch number and/or best-before date, why you'd want to, and what to expect once you do. It assumes you already have Logitrail API credentials — see Authentication if you don't.
1. Base URL and versioning
| Environment | Base URL |
|---|---|
| Test / sandbox | https://api-1.test.logitrail.com |
| Production | https://api-1.logitrail.com |
All endpoints in this guide are under the /v1/orders path.
2. Authentication
Every request needs the standard two headers (OAuth 2.0 client-credentials bearer token + merchant id):
| Header | Value |
|---|---|
Authorization | Bearer <access_token> |
X-Logitrail-Merchant-Id | Your Logitrail merchant id |
Reading an order requires the orders:read scope; setting or clearing a batch/best-before restriction requires orders:manage. See Authentication for how to obtain a token.
3. What this feature is for
By default, Logitrail's warehouse fulfills an order line by picking whichever eligible stock is oldest by expiry (FEFO — first-expiry-first-out). Most merchants never need to override this.
Sometimes you do need control over exactly which physical stock ships, for example:
- Traceability / recall compliance — pinning an order to a specific vendor batch/LOT for food, pharma, or other recall-traceable goods.
- Known-good stock — you've identified a specific incoming batch (e.g. from a particular inbound shipment) that must be the one shipped, overriding the default oldest-first pick.
You do this per order line, by setting best_before_date, batch_numbers, or both. Setting either turns off default FEFO picking for that line and constrains the warehouse to stock matching your restriction.
This only affects lines fulfilled by Logitrail's own warehouse (fulfillment_by: "logitrail"). It has no effect on lines the merchant fulfills themselves.
4. Key concepts
| Term | Meaning |
|---|---|
| Batch / LOT number | The vendor's manufacturing/production batch identifier, captured when goods are shelved. A product can have stock from several different incoming batches on the shelf at once, each with its own batch number and best-before date. |
| Best-before date | The expiry date recorded against a specific incoming batch when it was shelved. |
Two fields on an order line carry the restriction:
| Field | Type | Notes |
|---|---|---|
best_before_date | string, ISO 8601 date (YYYY-MM-DD) | The warehouse must pick stock whose best-before date exactly equals this date — not "on or before". |
batch_numbers | array of strings, minimum one entry | The warehouse may pick stock from any batch in this list. |
The best-before match is exact, not a range. Despite the name,
best_before_datedoes not mean "anything expiring on or before this date" — it means "only stock whose recorded best-before date is exactly this date." If you don't know the exact date of the batch you want, usebatch_numbersinstead (or in addition).
If you set both fields on the same line, both must match on the same physical stock — the warehouse picks stock that has one of the listed batch numbers and the exact best-before date. This is normally what you want when a batch number alone isn't unique enough to identify the stock you mean.
5. Setting a restriction
5.1 At order creation
Include best_before_date and/or batch_numbers on the line item when creating the order:
POST /v1/orders
Scope: orders:manage
{
"order": {
"...": "other order fields",
"items": [
{
"product": { "id": "64b8f0c2e1b2c3d4e5f67891" },
"quantity": 10,
"batch_numbers": ["LOT-2026-0142"],
"best_before_date": "2026-11-30"
}
]
}
}
Both fields are optional — omit them for normal FEFO picking.
5.2 On an existing order line
PATCH /v1/orders/{orderId}/items/{itemId}
Scope: orders:manage
This is a dedicated per-line patch endpoint — it only ever touches best_before_date and batch_numbers, leaving the rest of the order line untouched. Each field independently supports three states:
| You send | Effect |
|---|---|
| Field omitted from the body | Left unchanged |
| Field set to a value | Restriction set to that value |
Field set to null | Restriction cleared |
Set both fields:
{
"best_before_date": "2026-11-30",
"batch_numbers": ["LOT-2026-0142"]
}
Set only the batch restriction, leaving any existing best-before restriction untouched:
{
"batch_numbers": ["LOT-2026-0142", "LOT-2026-0143"]
}
Clear both restrictions (back to normal FEFO picking):
{
"best_before_date": null,
"batch_numbers": null
}
Response — 200 OK, body { "order": { "...": "the full updated order" } }.
5.3 Reading the current restriction
best_before_date and batch_numbers are returned on every order line by GET /v1/orders/{orderId} and GET /v1/orders (scope orders:read) — null/omitted when no restriction is set.
6. Validation and errors
PATCH /v1/orders/{orderId}/items/{itemId} returns:
| Status | Cause |
|---|---|
400 Bad Request | Malformed orderId/itemId, malformed best_before_date (must be YYYY-MM-DD), or an empty batch_numbers array. To clear batch_numbers, send null — not []. |
404 Not Found | The order doesn't exist for your merchant account, or the item id isn't on the order. |
7. What happens after you set a restriction
Applying a restriction doesn't validate it against current stock at write time — you can set a batch number or date for which no stock currently exists on the shelf. What happens next:
- If matching stock exists, the warehouse reserves it for the line (replacing any previously reserved stock that no longer matches, as long as that stock hasn't already been physically staged for picking on this order).
- If no matching stock exists yet, the line is held the same way any out-of-stock line is — it will fulfil once stock matching your restriction arrives and is shelved.
- Re-evaluation happens asynchronously after your call — expect a short delay (typically seconds) before the order's reservation reflects a change you just made.
Because there's no write-time check against real stock, a batch number with a typo or a best-before date that doesn't match anything on the shelf won't be rejected — it will simply never find matching stock. Double-check the value against what you know was shelved before setting it, particularly for best_before_date given the exact-match behaviour in §4.
8. Merchant portal
The Logitrail merchant portal exposes these fields when creating an order (a best-before date picker and a comma-separated batch-numbers field per line) and displays them read-only once the order exists. To change a restriction on an order that's already been created, use the PATCH endpoint described in §5.2 — the portal does not currently offer an edit-in-place control for this.
Questions
For API credentials or anything not covered here, contact your Logitrail integration contact.