Lot tracking through the PickFlow pick-and-pack API
When a product has Lot Tracking enabled in Luminous, the PickFlow integration now captures, validates, and surfaces the lot at every stage of the pick-and-pack workflow.
Step 1 — Read the lot assigned to each picklist line
When you fetch a picklist, each item in picklist_items now includes:
| Field | Description |
|---|---|
lot_id | Luminous lot identifier (integer) |
lot_number | Human-readable lot number printed on the label |
production_date | Manufacture date (YYYY-MM-DD) |
expiry_date | Expiry date (YYYY-MM-DD) |
Use lot_number to help the picker match the physical label on the shelf.
Step 2 — Complete picking with a lot
POST /external/api/v1/pickflow/picklists/{id}/complete
Each item in the items array must now include:
{
"picklist_item_id": 456,
"rfq_id": 789,
"location_id": 12,
"picked_quantity": 3,
"lot_id": 101
}
lot_idis required for lot-tracked products and optional for non-lot-tracked products.- The system validates that the lot belongs to the product on that line and that sufficient on-hand quantity exists in the specified bin.
Validation errors come back as HTTP 422 with field-keyed messages:
{
"message": "Cannot complete picklist 99: one or more picked lines are invalid.",
"errors": {
"items.0.lot_id": ["Lot 101 only has 2 on hand in location 12; 3 was requested."]
}
}
Four conditions are refused with 422 (not 500):
- Product or bin does not match the picklist line.
- Lot-tracked product submitted with no
lot_id. lot_idbelongs to a different product.- The lot does not have enough on-hand in the claimed bin.
Retry safety: If the network drops after the server commits, re-submitting the same complete-picking call returns success with already_completed: true and does not move stock a second time.
Step 3 — Read staged lots from the WIP bin (before packing)
After pick-complete, use the new endpoint to see which lots are physically in the tote:
GET /external/api/v1/pickflow/picklists/{id}/wip-lots
Example response:
{
"data": [
{
"picklist_id": 99,
"location_id": 55,
"rfq_id": 789,
"sku": "REN0007",
"lot_tracking": true,
"lot_id": 101,
"lot_number": "LUM-2026-09",
"manufacturer_code": "MFG-A",
"production_date": "2026-01-15",
"expiry_date": "2027-01-15",
"qty_onhand": 3.0
}
]
}
Rows with zero or negative on-hand are excluded so only pickable stock is listed. Non-lot-tracked stock in the same bin is also included (with lot_id: null).
Step 4 — Write a lot when buying a shipping label
Lot allocation for a package isn't a separate "create package" call — it happens on the label-buy endpoint: POST /pickflow/fulfillment-orders/{fulfillmentOrderId}/label. Each entry in the items array accepts:
| Field | Description |
|---|---|
lot_id | The exact lot row (integer FK). Validated against the line's product. |
{
"items": [{
"order_item_id": 456,
"product_id": 789,
"quantity": 3,
"lot_id": 101
}]
}
If lot_id belongs to a different product than the line item, the request is refused with 422.
Step 5 — Write a lot when buying a shipping label
Both the shipment-item and package-item responses now includes lot fields:
| Field | Notes |
| Lot Id | Integer or null |
| Lot number | Falls back to the lot record if only lot Id was stored |
| expiry date | YYYY-MM-DD format |
This allows downstream traceability product than the line item, the request is refused with 422.
Tips
- Always pass lot_id (not just lot_number) when completing picking; lot_id is unambiguous and avoids lookup failures.
- Poll the WIP-lots endpoint after pick-complete to pre-populate the lot field on the pack screen, rather than asking the operator to re-enter it.
- A 422 on complete-picking means the payload needs correction; a 500 indicates a system error — handle these separately in your error flow.