Receiving & stock
Called with warehouse account keys. A key only sees its own account's data. Every example uses the same sample brand, order and item, so you can follow one item from call to call.
http://62.169.23.239:8001/api/v1Auth Bearer token14 endpointsEndpoints
- GET/warehouse/dashboardGet warehouse summary
- GET/warehouse/inboundList inbound deliveries
- POST/warehouse/deliveries/{order_id}/receiveReceive boxes
- POST/warehouse/deliveries/{order_id}/rejectRefuse boxes
- GET/warehouse/boxesList boxes
- PATCH/warehouse/boxes/{box_id}Set a box location
- POST/warehouse/boxes/{box_id}/unpackUnpack a box
- POST/warehouse/boxes/packPack loose units into a box
- GET/warehouse/boxes/{box_id}/qr.svgGet a box QR label
- GET/warehouse/inventoryGet inventory
- GET/warehouse/sellableGet sellable stock
- GET/warehouse/agingList aging stock
- GET/warehouse/recycling-facilitiesList recycling facilities
- POST/warehouse/aging/{ref}/disposeDispose of stock
Get warehouse summary#
Requires scope warehouse_stock:read
Everything held, claimable, forwarded recently, and wanting attention.
Returns
HTTP 200- sealed_box_countinteger
Defaults to
0 - sealed_unit_countinteger
Defaults to
0 - loose_unit_countinteger
Defaults to
0 - total_unit_countinteger
Defaults to
0 - inbound_claimable_boxesinteger
Defaults to
0 - inbound_expected_ordersinteger
Orders a brand has named this warehouse as destination for, but no factory has dispatched yet — advance visibility, not something to claim. Kept apart from inbound_claimable_boxes so the two are never summed under one "unclaimed deliveries" label again (they mean different things).
Defaults to
0 - forwarded_last_7_daysinteger
Defaults to
0 - expiring_soon_countinteger
Defaults to
0 - expired_countinteger
Defaults to
0 - aging_countinteger
Defaults to
0 - open_stock_check_idstring | null
curl http://62.169.23.239:8001/api/v1/warehouse/dashboard \
-H "Authorization: Bearer $SKANNIT_TOKEN"{
"sealed_box_count": 24,
"sealed_unit_count": 24,
"loose_unit_count": 24,
"total_unit_count": 24,
"inbound_claimable_boxes": 24,
"inbound_expected_orders": 1,
"forwarded_last_7_days": 1,
"expiring_soon_count": 24,
"expired_count": 24,
"aging_count": 24,
"open_stock_check_id": "294c6e5c-1e81-5f39-ada2-fa00177041a9"
}List inbound deliveries#
Requires scope warehouse_stock:read
Delivered boxes nobody has claimed yet.
Nothing is assigned to a particular warehouse — as with a shop, the boxes are simply sitting in brand custody, and whoever physically receives them scans them in.
Returns
HTTP 200- itemsarray of objects
The objects on this page.
+Show 17 child attributesHide child attributes
- box_idstringRequired
Id printed on the box label. Scanning the box resolves to this id.
- order_idstringRequired
Skannit's id for the production order. Use it in every /production-orders/{order_id} and QR call.
- order_display_idstring
Order number formatted for display (#PO-YYYY-XXXX).
Defaults to
- product_idstringRequired
Skannit's id for the product.
- product_namestring
Defaults to
— - brand_namestring
Defaults to
— - statusstringRequired
Current state of the object. See the allowed values.
One ofsealedunpackeddispatchedforwardedsoldremovedexpected - unit_countinteger
Defaults to
0 - linesarray of objects
Line items, one per variant.
+Show 4 child attributesHide child attributes
- colorstring | null
Colour name, exactly as on the product's variants.
- sizestring | null
Size name, exactly as on the product's variants.
- batch_skustring | null
SKU printed on the label for this variant in this batch.
- quantityinteger
Number of units.
Defaults to
0
- expiry_datedate | null
- receipt_conditionstring | null
Condition recorded when the box was received.
One ofgooddamagedpartial - received_attimestamp | null
- unpacked_attimestamp | null
- forwarded_attimestamp | null
- forwarded_to_idstring | null
- locationstring | null
Where it sits in the building, e.g. "Aisle 4, Bay B".
- transit_issuesarray of objects
Inbound only: what the carrier reported on the way here.
+Show 6 child attributesHide child attributes
- sourcestringRequiredOne of
damage_reportdispatch_alert - damage_typestring | null
- severitystring | null
- notestring | null
- photo_urlstring | null
- reported_attimestamp | null
- totalinteger
Total count.
Defaults to
0
curl http://62.169.23.239:8001/api/v1/warehouse/inbound \
-H "Authorization: Bearer $SKANNIT_TOKEN"{
"items": [
{
"box_id": "BOX-F4E1A9-0003",
"order_id": "ord_8c2f4e1a9b7d3c60",
"order_display_id": "#PO-2026-7F3A",
"product_id": "f6abe715-90aa-5908-a0a6-12951a3a21cc",
"product_name": "Trail Tee",
"brand_name": "Northwind Apparel",
"status": "sealed",
"unit_count": 24,
"lines": [
{
"color": "White",
"size": "M",
"batch_sku": "TT-100-WHT-M",
"quantity": 1
}
],
"expiry_date": "2026-11-20",
"receipt_condition": "good",
"received_at": null,
"unpacked_at": null,
"forwarded_at": null,
"forwarded_to_id": "a03218a3-a2d6-5112-8c31-778970bde949",
"location": "Aisle 4, Bay B",
"transit_issues": [
{
"source": "web_shop",
"damage_type": "crushed",
"severity": "high",
"note": "Corner of box dented",
"photo_url": "https://files.skannit.example/photo.png",
"reported_at": null
}
]
}
],
"total": 1
}Receive boxes#
Requires scope warehouse_stock:write
Take scanned boxes into warehouse custody, sealed.
The boxes are stored unopened — one row per box, not per unit — and their contents are known from the manifest. Pass expected_units to have the server confirm what arrived against what the manifest says; a mismatch is reported in discrepancies rather than rejecting goods already on the dock.
Parameters
- order_idstringpathRequired
Skannit's id for the production order. Use it in every /production-orders/{order_id} and QR call.
Request body
- box_idsarray of strings
Ids of the boxes this call acts on.
- expiry_datedate | null
- conditionstring | nullOne of
gooddamagedpartial - notesstring | null
- expected_unitsinteger | null
- locationstring | null
Where the received boxes are put away, e.g. "Aisle 4, Bay B".
Returns
HTTP 200- order_idstringRequired
Skannit's id for the production order. Use it in every /production-orders/{order_id} and QR call.
- received_boxesinteger
Defaults to
0 - already_received_boxesinteger
Defaults to
0 - received_unitsinteger
Defaults to
0 - boxesarray of objects
+Show 17 child attributesHide child attributes
- box_idstringRequired
Id printed on the box label. Scanning the box resolves to this id.
- order_idstringRequired
Skannit's id for the production order. Use it in every /production-orders/{order_id} and QR call.
- order_display_idstring
Order number formatted for display (#PO-YYYY-XXXX).
Defaults to
- product_idstringRequired
Skannit's id for the product.
- product_namestring
Defaults to
— - brand_namestring
Defaults to
— - statusstringRequired
Current state of the object. See the allowed values.
One ofsealedunpackeddispatchedforwardedsoldremovedexpected - unit_countinteger
Defaults to
0 - linesarray of objects
Line items, one per variant.
+Show 4 child attributesHide child attributes
- colorstring | null
Colour name, exactly as on the product's variants.
- sizestring | null
Size name, exactly as on the product's variants.
- batch_skustring | null
SKU printed on the label for this variant in this batch.
- quantityinteger
Number of units.
Defaults to
0
- expiry_datedate | null
- receipt_conditionstring | null
Condition recorded when the box was received.
One ofgooddamagedpartial - received_attimestamp | null
- unpacked_attimestamp | null
- forwarded_attimestamp | null
- forwarded_to_idstring | null
- locationstring | null
Where it sits in the building, e.g. "Aisle 4, Bay B".
- transit_issuesarray of objects
Inbound only: what the carrier reported on the way here.
+Show 6 child attributesHide child attributes
- sourcestringRequiredOne of
damage_reportdispatch_alert - damage_typestring | null
- severitystring | null
- notestring | null
- photo_urlstring | null
- reported_attimestamp | null
- discrepanciesarray of objects
Non-empty when the count the worker expected differs from the manifest. Receiving still succeeds — the discrepancy is recorded for the manager, because refusing the goods that are physically on the dock helps nobody.
+Show 4 child attributesHide child attributes
- box_idstringRequired
Id printed on the box label. Scanning the box resolves to this id.
- expected_unitsinteger
Defaults to
0 - manifest_unitsinteger
Defaults to
0 - detailstringRequired
- custody_tx_queuedinteger
True when the hand-over was queued for on-chain recording.
Defaults to
0
curl -X POST http://62.169.23.239:8001/api/v1/warehouse/deliveries/ord_8c2f4e1a9b7d3c60/receive \
-H "Authorization: Bearer $SKANNIT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"box_ids": [
"BOX-F4E1A9-0003"
],
"condition": "good",
"expected_units": 48,
"location": "Aisle 4, Bay B"
}'{
"order_id": "ord_8c2f4e1a9b7d3c60",
"received_boxes": 24,
"already_received_boxes": 24,
"received_units": 24,
"boxes": [
{
"box_id": "BOX-F4E1A9-0003",
"order_id": "ord_8c2f4e1a9b7d3c60",
"order_display_id": "#PO-2026-7F3A",
"product_id": "f6abe715-90aa-5908-a0a6-12951a3a21cc",
"product_name": "Trail Tee",
"brand_name": "Northwind Apparel",
"status": "sealed",
"unit_count": 24,
"lines": [
{
"color": "White",
"size": "M",
"batch_sku": "TT-100-WHT-M",
"quantity": 1
}
],
"expiry_date": "2026-11-20",
"receipt_condition": "good",
"received_at": null,
"unpacked_at": null,
"forwarded_at": null,
"forwarded_to_id": "a03218a3-a2d6-5112-8c31-778970bde949",
"location": "Aisle 4, Bay B",
"transit_issues": [
{
"source": "web_shop",
"damage_type": "crushed",
"severity": "high",
"note": "Corner of box dented",
"photo_url": "https://files.skannit.example/photo.png",
"reported_at": null
}
]
}
],
"discrepancies": [
{
"box_id": "BOX-F4E1A9-0003",
"expected_units": 24,
"manifest_units": 24,
"detail": "Box BOX-F4E1A9-0003 manifest says 48 units"
}
],
"custody_tx_queued": 1
}Refuse boxes#
Requires scope warehouse_stock:write
Refuse delivered boxes instead of accepting them — e.g. the carrier reported damage (see transit_issues on GET /inbound) or the goods are wrong. Nothing moves: the boxes stay in the brand's custody, the refusal and reason are recorded, the brand is notified and its systems receive a custody.exception webhook. To accept only some boxes, receive those and reject the rest.
Parameters
- order_idstringpathRequired
Skannit's id for the production order. Use it in every /production-orders/{order_id} and QR call.
Request body
- box_idsarray of strings
Ids of the boxes this call acts on.
- reasonstringRequired
Returns
HTTP 200- order_idstringRequired
Skannit's id for the production order. Use it in every /production-orders/{order_id} and QR call.
- rejected_box_idsarray of strings
- already_rejected_box_idsarray of strings
- reasonstringRequired
curl -X POST http://62.169.23.239:8001/api/v1/warehouse/deliveries/ord_8c2f4e1a9b7d3c60/reject \
-H "Authorization: Bearer $SKANNIT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"box_ids": [
"BOX-F4E1A9-0004"
],
"reason": "Box crushed, seal broken"
}'{
"order_id": "ord_8c2f4e1a9b7d3c60",
"rejected_box_ids": [
"e3a18da1-ad26-5640-bf3d-714fec94bc30"
],
"already_rejected_box_ids": [
"e12f686f-8b49-536b-ad7e-81b0555c90dd"
],
"reason": "Customer changed their mind"
}List boxes#
Requires scope warehouse_stock:read
Boxes held by this warehouse, with what each one contains.
Parameters
- statusstringquery
Current state of the object. See the allowed values.
Returns
HTTP 200- itemsarray of objects
The objects on this page.
+Show 17 child attributesHide child attributes
- box_idstringRequired
Id printed on the box label. Scanning the box resolves to this id.
- order_idstringRequired
Skannit's id for the production order. Use it in every /production-orders/{order_id} and QR call.
- order_display_idstring
Order number formatted for display (#PO-YYYY-XXXX).
Defaults to
- product_idstringRequired
Skannit's id for the product.
- product_namestring
Defaults to
— - brand_namestring
Defaults to
— - statusstringRequired
Current state of the object. See the allowed values.
One ofsealedunpackeddispatchedforwardedsoldremovedexpected - unit_countinteger
Defaults to
0 - linesarray of objects
Line items, one per variant.
+Show 4 child attributesHide child attributes
- colorstring | null
Colour name, exactly as on the product's variants.
- sizestring | null
Size name, exactly as on the product's variants.
- batch_skustring | null
SKU printed on the label for this variant in this batch.
- quantityinteger
Number of units.
Defaults to
0
- expiry_datedate | null
- receipt_conditionstring | null
Condition recorded when the box was received.
One ofgooddamagedpartial - received_attimestamp | null
- unpacked_attimestamp | null
- forwarded_attimestamp | null
- forwarded_to_idstring | null
- locationstring | null
Where it sits in the building, e.g. "Aisle 4, Bay B".
- transit_issuesarray of objects
Inbound only: what the carrier reported on the way here.
+Show 6 child attributesHide child attributes
- sourcestringRequiredOne of
damage_reportdispatch_alert - damage_typestring | null
- severitystring | null
- notestring | null
- photo_urlstring | null
- reported_attimestamp | null
- totalinteger
Total count.
Defaults to
0
curl http://62.169.23.239:8001/api/v1/warehouse/boxes \
-H "Authorization: Bearer $SKANNIT_TOKEN"{
"items": [
{
"box_id": "BOX-F4E1A9-0003",
"order_id": "ord_8c2f4e1a9b7d3c60",
"order_display_id": "#PO-2026-7F3A",
"product_id": "f6abe715-90aa-5908-a0a6-12951a3a21cc",
"product_name": "Trail Tee",
"brand_name": "Northwind Apparel",
"status": "sealed",
"unit_count": 24,
"lines": [
{
"color": "White",
"size": "M",
"batch_sku": "TT-100-WHT-M",
"quantity": 1
}
],
"expiry_date": "2026-11-20",
"receipt_condition": "good",
"received_at": null,
"unpacked_at": null,
"forwarded_at": null,
"forwarded_to_id": "a03218a3-a2d6-5112-8c31-778970bde949",
"location": "Aisle 4, Bay B",
"transit_issues": [
{
"source": "web_shop",
"damage_type": "crushed",
"severity": "high",
"note": "Corner of box dented",
"photo_url": "https://files.skannit.example/photo.png",
"reported_at": null
}
]
}
],
"total": 1
}Set a box location#
Requires scope warehouse_stock:write
Record where a box sits in the building, e.g. "Aisle 4, Bay B" (send null to clear). Units unpacked from it later inherit the location.
Parameters
- box_idstringpathRequired
Id printed on the box label. Scanning the box resolves to this id.
Request body
- locationstring | null
Free-text place in your warehouse, for example "Aisle 4, Bay B".
Returns
HTTP 200- box_idstringRequired
Id printed on the box label. Scanning the box resolves to this id.
- order_idstringRequired
Skannit's id for the production order. Use it in every /production-orders/{order_id} and QR call.
- order_display_idstring
Order number formatted for display (#PO-YYYY-XXXX).
Defaults to
- product_idstringRequired
Skannit's id for the product.
- product_namestring
Defaults to
— - brand_namestring
Defaults to
— - statusstringRequired
Current state of the object. See the allowed values.
One ofsealedunpackeddispatchedforwardedsoldremovedexpected - unit_countinteger
Defaults to
0 - linesarray of objects
Line items, one per variant.
+Show 4 child attributesHide child attributes
- colorstring | null
Colour name, exactly as on the product's variants.
- sizestring | null
Size name, exactly as on the product's variants.
- batch_skustring | null
SKU printed on the label for this variant in this batch.
- quantityinteger
Number of units.
Defaults to
0
- expiry_datedate | null
- receipt_conditionstring | null
Condition recorded when the box was received.
One ofgooddamagedpartial - received_attimestamp | null
- unpacked_attimestamp | null
- forwarded_attimestamp | null
- forwarded_to_idstring | null
- locationstring | null
Where it sits in the building, e.g. "Aisle 4, Bay B".
- transit_issuesarray of objects
Inbound only: what the carrier reported on the way here.
+Show 6 child attributesHide child attributes
- sourcestringRequiredOne of
damage_reportdispatch_alert - damage_typestring | null
- severitystring | null
- notestring | null
- photo_urlstring | null
- reported_attimestamp | null
curl -X PATCH http://62.169.23.239:8001/api/v1/warehouse/boxes/BOX-F4E1A9-0003 \
-H "Authorization: Bearer $SKANNIT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"location": "Aisle 4, Bay B"
}'{
"box_id": "BOX-F4E1A9-0003",
"order_id": "ord_8c2f4e1a9b7d3c60",
"order_display_id": "#PO-2026-7F3A",
"product_id": "f6abe715-90aa-5908-a0a6-12951a3a21cc",
"product_name": "Trail Tee",
"brand_name": "Northwind Apparel",
"status": "sealed",
"unit_count": 24,
"lines": [
{
"color": "White",
"size": "M",
"batch_sku": "TT-100-WHT-M",
"quantity": 1
}
],
"expiry_date": "2026-11-20",
"receipt_condition": "good",
"received_at": null,
"unpacked_at": null,
"forwarded_at": null,
"forwarded_to_id": "a03218a3-a2d6-5112-8c31-778970bde949",
"location": "Aisle 4, Bay B",
"transit_issues": [
{
"source": "web_shop",
"damage_type": "crushed",
"severity": "high",
"note": "Corner of box dented",
"photo_url": "https://files.skannit.example/photo.png",
"reported_at": null
}
]
}Unpack a box#
Requires scope warehouse_stock:write
Open a box into individually trackable units.
Optional — a warehouse that only ships whole boxes never needs this. Afterwards the units are ordinary inventory rows: sellable one by one, or packed into a new box (POST /boxes/pack) for a shop. Nothing changes hands, so no custody event is recorded.
Parameters
- box_idstringpathRequired
Id printed on the box label. Scanning the box resolves to this id.
Returns
HTTP 200- box_idstringRequired
Id printed on the box label. Scanning the box resolves to this id.
- units_createdinteger
Defaults to
0 - already_unpacked_unitsinteger
Defaults to
0 - statusstringRequired
Current state of the object. See the allowed values.
One ofsealedunpackeddispatchedforwardedsoldremovedexpected
curl -X POST http://62.169.23.239:8001/api/v1/warehouse/boxes/BOX-F4E1A9-0003/unpack \
-H "Authorization: Bearer $SKANNIT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)"{
"box_id": "BOX-F4E1A9-0003",
"units_created": 24,
"already_unpacked_units": 24,
"status": "sealed"
}Pack loose units into a box#
Requires scope warehouse_stock:write
Seal N loose units of one SKU into a brand-new box.
The reverse of unpack — for an order smaller than any box already sealed, most often exactly one unit. The resulting box is dispatchable the same way any other sealed box is (see POST /warehouse/transport/dispatches).
Request body
- skustringRequired
Stock-keeping unit of the variant.
- colorstring | null
Colour name, exactly as on the product's variants.
- sizestring | null
Size name, exactly as on the product's variants.
- quantityintegerRequired
Number of units.
- notesstring | null
Returns
HTTP 200- box_idstringRequired
Id printed on the box label. Scanning the box resolves to this id.
- packed_unitsinteger
Defaults to
0
curl -X POST http://62.169.23.239:8001/api/v1/warehouse/boxes/pack \
-H "Authorization: Bearer $SKANNIT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"sku": "TT-100-WHT-M",
"color": "White",
"size": "M",
"quantity": 1,
"notes": "Received at dock 3"
}'{
"box_id": "BOX-F4E1A9-0003",
"packed_units": 24
}Get a box QR label#
Requires scope warehouse_stock:read
QR label for a box this warehouse holds. Print it on the box so the transporter can scan it at pickup (it resolves via GET /scan/box/{box_id}).
Parameters
- box_idstringpathRequired
Id printed on the box label. Scanning the box resolves to this id.
Returns
HTTP 200No body.
curl http://62.169.23.239:8001/api/v1/warehouse/boxes/BOX-F4E1A9-0003/qr.svg \
-H "Authorization: Bearer $SKANNIT_TOKEN"// 200 — no bodyGet inventory#
Requires scope warehouse_stock:read
Stock at both granularities.
Sealed and loose are reported separately on purpose: a sealed box can be shipped whole but cannot be picked from, so summing them into one availability number would overstate what is actually sellable.
Returns
HTTP 200- sealedarray of objects
+Show 6 child attributesHide child attributes
- product_idstringRequired
Skannit's id for the product.
- product_namestring
Defaults to
— - brand_namestring
Defaults to
— - box_countinteger
Defaults to
0 - unit_countinteger
Defaults to
0 - locationsarray of strings
- loosearray of objects
+Show 9 child attributesHide child attributes
- item_idstringRequired
- product_idstringRequired
Skannit's id for the product.
- product_namestring
Defaults to
— - brand_namestring
Defaults to
— - colorstring | null
Colour name, exactly as on the product's variants.
- sizestring | null
Size name, exactly as on the product's variants.
- batch_skustring | null
SKU printed on the label for this variant in this batch.
- current_qtyinteger
Defaults to
0 - locationsarray of strings
Where these units sit, e.g. ["Aisle 4, Bay B"]; empty when not recorded.
- sealed_box_countinteger
Defaults to
0 - sealed_unit_countinteger
Defaults to
0 - loose_unit_countinteger
Defaults to
0 - total_unit_countinteger
Defaults to
0 - reservedarray of objects
Held for waiting retailer orders until a dispatch ships them. Can exceed what is on hand — the excess is still waiting on production.
+Show 8 child attributesHide child attributes
- product_idstringRequired
Skannit's id for the product.
- product_namestring
Defaults to
— - skustringRequired
Stock-keeping unit of the variant.
- colorstring | null
Colour name, exactly as on the product's variants.
- sizestring | null
Size name, exactly as on the product's variants.
- quantityinteger
Number of units.
Defaults to
0 - held_from_stockinteger
Part of `quantity` covered by stock on hand; the rest waits on production.
Defaults to
0 - order_countinteger
Defaults to
0
- reserved_unit_countinteger
Defaults to
0 - available_unit_countinteger
total_unit_count minus the reserved units actually on hand.
Defaults to
0
curl http://62.169.23.239:8001/api/v1/warehouse/inventory \
-H "Authorization: Bearer $SKANNIT_TOKEN"{
"sealed": [
{
"product_id": "f6abe715-90aa-5908-a0a6-12951a3a21cc",
"product_name": "Trail Tee",
"brand_name": "Northwind Apparel",
"box_count": 24,
"unit_count": 24,
"locations": [
"Aisle 4, Bay B"
]
}
],
"loose": [
{
"item_id": "Qm7xK2pL9vR4tY8wZ1nB3c",
"product_id": "f6abe715-90aa-5908-a0a6-12951a3a21cc",
"product_name": "Trail Tee",
"brand_name": "Northwind Apparel",
"color": "White",
"size": "M",
"batch_sku": "TT-100-WHT-M",
"current_qty": 24,
"locations": [
"Aisle 4, Bay B"
]
}
],
"sealed_box_count": 24,
"sealed_unit_count": 24,
"loose_unit_count": 24,
"total_unit_count": 24,
"reserved": [
{
"product_id": "f6abe715-90aa-5908-a0a6-12951a3a21cc",
"product_name": "Trail Tee",
"sku": "TT-100-WHT-M",
"color": "White",
"size": "M",
"quantity": 1,
"held_from_stock": 1,
"order_count": 24
}
],
"reserved_unit_count": 24,
"available_unit_count": 24
}Get sellable stock#
Requires scope warehouse_stock:read
What can go on a sale right now — sealed boxes and loose units.
The two are returned as separate lists, not one merged catalogue, because they are not interchangeable: a sealed box sells whole, loose units sell by the piece. Only sealed boxes appear under boxes — once a box is unpacked its units are offered under units instead, so nothing is counted twice.
Returns
HTTP 200- boxesarray of objects
+Show 10 child attributesHide child attributes
- box_idstringRequired
Id printed on the box label. Scanning the box resolves to this id.
- product_idstringRequired
Skannit's id for the product.
- product_namestring
Defaults to
— - brand_namestring
Defaults to
— - unit_countinteger
Defaults to
0 - linesarray of objects
Line items, one per variant.
+Show 4 child attributesHide child attributes
- colorstring | null
Colour name, exactly as on the product's variants.
- sizestring | null
Size name, exactly as on the product's variants.
- batch_skustring | null
SKU printed on the label for this variant in this batch.
- quantityinteger
Number of units.
Defaults to
0
- expiry_datedate | null
- received_attimestamp | null
- unit_price_eurnumber
Price of one unit in euro, including VAT.
Defaults to
0 - suggested_price_eurnumber
unit_price_eur * unit_count — the whole-box asking price.
Defaults to
0
- unitsarray of objects
+Show 9 child attributesHide child attributes
- item_idstringRequired
- product_idstringRequired
Skannit's id for the product.
- product_namestring
Defaults to
— - colorstring | null
Colour name, exactly as on the product's variants.
- sizestring | null
Size name, exactly as on the product's variants.
- batch_skustring | null
SKU printed on the label for this variant in this batch.
- available_qtyinteger
Defaults to
0 - unit_price_eurnumber
Price of one unit in euro, including VAT.
Defaults to
0 - unitsarray of objects
Individual units behind available_qty, oldest first. Capped per group so a warehouse holding thousands of one SKU does not turn the picker payload into a multi-megabyte response; available_qty remains the true count.
+Show 2 child attributesHide child attributes
- unit_tokenstringRequired
The item's own token. It is what the item's QR code carries, and the id every item event uses.
- received_attimestamp | null
- sellable_box_countinteger
Defaults to
0 - sellable_unit_countinteger
Defaults to
0
curl http://62.169.23.239:8001/api/v1/warehouse/sellable \
-H "Authorization: Bearer $SKANNIT_TOKEN"{
"boxes": [
{
"box_id": "BOX-F4E1A9-0003",
"product_id": "f6abe715-90aa-5908-a0a6-12951a3a21cc",
"product_name": "Trail Tee",
"brand_name": "Northwind Apparel",
"unit_count": 24,
"lines": [
{
"color": "White",
"size": "M",
"batch_sku": "TT-100-WHT-M",
"quantity": 1
}
],
"expiry_date": "2026-11-20",
"received_at": null,
"unit_price_eur": 29,
"suggested_price_eur": 29
}
],
"units": [
{
"item_id": "Qm7xK2pL9vR4tY8wZ1nB3c",
"product_id": "f6abe715-90aa-5908-a0a6-12951a3a21cc",
"product_name": "Trail Tee",
"color": "White",
"size": "M",
"batch_sku": "TT-100-WHT-M",
"available_qty": 24,
"unit_price_eur": 29,
"units": [
{
"unit_token": "Qm7xK2pL9vR4tY8wZ1nB3c",
"received_at": null
}
]
}
],
"sellable_box_count": 24,
"sellable_unit_count": 24
}List aging stock#
Requires scope warehouse_stock:read
Stock wanting a decision — expired, expiring, or sitting too long.
Reports only; nothing here changes stock state. Acting on a row is the separate, explicit POST below, because whether to write stock off or send it to a recycler is the manager's call rather than a consequence of looking.
Parameters
- include_healthybooleanquery
Also list stock that needs no attention yet, so the expiry date of the whole holding is visible in one place. Those rows carry reason=null and are never counted in the three totals.
Defaults to
false
Returns
HTTP 200- itemsarray of objects
The objects on this page.
+Show 13 child attributesHide child attributes
- refstringRequired
- ref_kindstringRequiredOne of
boxunit - product_namestring
Defaults to
— - brand_namestring
Defaults to
— - colorstring | null
A sealed box has no single colour/size (a mixed carton); a loose unit always does. Both None on a box is correct, not a missing-data bug.
- sizestring | null
Size name, exactly as on the product's variants.
- locationstring | null
Where to find it, e.g. "Aisle 4, Bay B" — None when never recorded.
- reasonstring | null
None means the row needs no attention yet — it is healthy stock, listed only so the expiry date of everything held is visible in one place. The three counters on the screen count reasons, so they ignore these rows.
One ofexpiringexpiredaging - expiry_datedate | null
- received_attimestamp | null
- days_in_stockinteger | null
- days_to_expiryinteger | null
- unit_countinteger
Defaults to
1
- totalinteger
Total count.
Defaults to
0 - expired_countinteger
Counted server-side over the whole holding, so the cards stay correct regardless of which filter the client is showing.
Defaults to
0 - expiring_countinteger
Defaults to
0 - aging_countinteger
Defaults to
0
curl http://62.169.23.239:8001/api/v1/warehouse/aging \
-H "Authorization: Bearer $SKANNIT_TOKEN"{
"items": [
{
"ref": "BOX-F4E1A9-0003",
"ref_kind": "box",
"product_name": "Trail Tee",
"brand_name": "Northwind Apparel",
"color": "White",
"size": "M",
"location": "Aisle 4, Bay B",
"reason": "Customer changed their mind",
"expiry_date": "2026-11-20",
"received_at": null,
"days_in_stock": 1,
"days_to_expiry": 1,
"unit_count": 1
}
],
"total": 1,
"expired_count": 24,
"expiring_count": 24,
"aging_count": 24
}List recycling facilities#
Requires scope warehouse_stock:read
Recyclers this warehouse can hand stock to, nearest first.
Everything listed here is eligible to take custody, so anything the picker offers the dispose call below will accept. Licence details are included because they belong in the choice — the stock is leaving for good.
Returns
HTTP 200- itemsarray of objects
The objects on this page.
+Show 8 child attributesHide child attributes
- facility_idstringRequired
- facility_namestring
Defaults to
— - facility_typestring | null
- citystring | null
- countrystring | null
- registration_idstring | null
Licence fields travel with the listing on purpose: the warehouse is being asked to hand goods over for permanent disposal, so who is licensed to receive them — and whether anyone has checked — belongs in the choice rather than behind a second call. 'unverified' is self-asserted, never accreditation.
- licence_authoritystring | null
- licence_statusstring | null
- totalinteger
Total count.
Defaults to
0
curl http://62.169.23.239:8001/api/v1/warehouse/recycling-facilities \
-H "Authorization: Bearer $SKANNIT_TOKEN"{
"items": [
{
"facility_id": "be5df03e-bc08-5a20-8773-8f8f63bffeaf",
"facility_name": "ReLoop Recycling",
"facility_type": "textile",
"city": "Rotterdam",
"country": "NL",
"registration_id": "e7367686-035b-5200-824a-93091ffaa355",
"licence_authority": "Rijkswaterstaat",
"licence_status": "valid"
}
],
"total": 1
}Dispose of stock#
Requires scope warehouse_stock:write
Take aged-out stock out of inventory: a sealed box or a loose unit.
REMOVE drops it from stock and says nothing about where it went. RECYCLE hands it to a licensed recycler and records the custody handoff, so the goods have a documented exit from the market. ref is a box_id or a unit_token; ref_kind says which, as everywhere else in this module.
Disposing of a sealed box disposes of the units inside it too — they cannot outlive the carton they were sealed in.
Parameters
- refstringpathRequired
Request body
- ref_kindstringRequiredOne of
boxunit - actionstringOne of
REMOVERECYCLEDefaults to
REMOVE - reasonstringOne of
expireddamagedrecalledotherDefaults to
expired - facility_idstring | null
RECYCLE only: pick a specific facility instead of the nearest active one.
Returns
HTTP 200- refstringRequired
- ref_kindstringRequiredOne of
boxunit - statusstringRequired
Current state of the object. See the allowed values.
One ofremovedrecycled - units_removedinteger
How many units left stock — 1 for a loose unit, the whole carton for a box.
Defaults to
0 - recycling_request_idsarray of integers
Populated for RECYCLE so the caller can follow the stock to its certificate.
- facility_idstring | null
- facility_namestring | null
- custody_recordedboolean
Defaults to
false
curl -X POST http://62.169.23.239:8001/api/v1/warehouse/aging/BOX-F4E1A9-0003/dispose \
-H "Authorization: Bearer $SKANNIT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"ref_kind": "box",
"reason": "Customer changed their mind",
"facility_id": "be5df03e-bc08-5a20-8773-8f8f63bffeaf"
}'{
"ref": "BOX-F4E1A9-0003",
"ref_kind": "box",
"status": "removed",
"units_removed": 24,
"recycling_request_ids": [
1
],
"facility_id": "be5df03e-bc08-5a20-8773-8f8f63bffeaf",
"facility_name": "ReLoop Recycling",
"custody_recorded": false
}