Skip to main content

Product Inventory Service (2026-10-17.2958151882)

Download OpenAPI specification:Download

Public per-product stock-rows endpoint. The path lives under /v1/products even though the implementing service is product-inventory-service.

List per-product stock rows

Returns the in-warehouse stock of one of your products, aggregated into rows keyed on (arrival_batch_id, best_before_date, batch_number) — extended with the reserving order id when include_order_breakdown=true. Each row carries available_quantity / reserved_quantity / total_quantity, the earliest on-shelf timestamp, and references to the inbound shipment and (optionally) the reserving order. Articles that have already shipped are excluded.

Authorizations:
idp
path Parameters
productId
required
string^[a-f0-9]{24}$

Logitrail's internal technical ID of the product (24-char hex).

query Parameters
offset
integer [ 0 .. 9007199254740991 ]
Default: 0

Pagination offset into the post-aggregation row list.

limit
integer [ 1 .. 500 ]
Default: 100

Pagination page size (1–500). Default 100. Caps the post-aggregation row list.

include_order_breakdown
boolean
Default: false

When true, split rows by the reserving order id (order.id). When false (default), reserved articles are summed across all reserving orders and order is null on every row.

header Parameters
x-logitrail-merchant-id
required
string non-empty

Logitrail's Merchant ID.

Responses

Response samples

Content type
application/json
{
  • "stock_rows": [
    ]
}

List articles for one of your inbound shipments

Returns the units of one of your inbound shipments, aggregated into rows keyed on (product, in_warehouse_status, batch_number, best_before_date). in_warehouse_status tells you where each unit is in its lifecycle (inbound = expected, arrived = received but not yet shelved, on_shelf, picked, …). Each row carries the total_quantity and the product (id, sku, name). Filter with in_warehouse_status; paginated with offset/limit (up to 500 rows per page). Inbound shipments that do not belong to you return an empty list.

Authorizations:
idp
query Parameters
inbound_shipment_id
required
string^[a-f0-9]{24}$

Inbound shipment ID (24-char hex) whose articles to list. Required. Shipments that do not belong to you return an empty list.

string or Array of strings

Optional in-warehouse status filter — a single value or an array. When omitted, every status present on the shipment is returned. Allowed values: inbound, arrived, on_shelf, picked, packed, sent_out, dropshipped, incident.

offset
integer [ 0 .. 9007199254740991 ]
Default: 0

Pagination offset into the post-aggregation row list.

limit
integer [ 1 .. 500 ]
Default: 100

Pagination page size (1–500). Default 100. Caps the post-aggregation row list.

header Parameters
x-logitrail-merchant-id
required
string non-empty

Logitrail's Merchant ID.

Responses

Response samples

Content type
application/json
{
  • "article_rows": [
    ]
}

List inventory movement history for a product

Returns the chronological inventory movement history for one of your products. Each entry records a timestamp, movement type (e.g. inbound_shipment.to_shelf, order_pickup.picked), article count, and references to the related inbound shipment or order when applicable. Supports time-range and type filters; paginated with offset/limit (up to 500 rows per page). Products that do not belong to your merchant return an empty list.

Authorizations:
idp
path Parameters
productId
required
string^[a-f0-9]{24}$

Logitrail's internal technical ID of the product (24-char hex).

query Parameters
offset
integer [ 0 .. 9007199254740991 ]
Default: 0

Pagination offset into the chronological movement list.

limit
integer [ 1 .. 500 ]
Default: 100

Pagination page size (1–500). Default 100.

ts_min
string <date-time> ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[...

Optional inclusive lower bound on movement ts (ISO 8601). Omit for "all history".

ts_max
string <date-time> ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[...

Optional inclusive upper bound on movement ts (ISO 8601). Omit for "up to now".

string or Array of strings

Optional movement-type filter. Repeat the parameter or supply a comma-separated value to filter to multiple types.

order
string
Default: "asc"
Enum: "asc" "desc"

Chronological order. asc (default) returns oldest first.

header Parameters
x-logitrail-merchant-id
required
string non-empty

Logitrail's Merchant ID.

Responses

Response samples

Content type
application/json
{
  • "movements": [
    ]
}

List idle on-shelf inventory for your products

Returns your products that are in on_shelf state but have not shipped in the last minDaysIdle days (default 30). Aggregated per product: qty_on_shelf sums the on-shelf article count across every shelf position, last_outbound_at is the MAX shipped-timestamp across those positions, and days_idle is derived from last_outbound_at (or the most recent shelving when that is null). Minimum-age guard excludes products whose most recent shelving is more recent than the cutoff, so fresh inbound stock doesn't show as idle. Sorted oldest-idle first, paginated with offset/limit (up to 500 rows per page).

Authorizations:
idp
query Parameters
minDaysIdle
integer [ 0 .. 9007199254740991 ]
Default: 30

Products with no outbound movement from any of their shelf positions in the last minDaysIdle days (default 30). Also acts as a minimum age — products whose most recent shelving is more recent than the cutoff are excluded so genuinely fresh stock does not appear as "idle".

offset
integer [ 0 .. 9007199254740991 ]
Default: 0

Pagination offset into the result list.

limit
integer [ 1 .. 500 ]
Default: 100

Pagination page size (1–500). Default 100.

header Parameters
x-logitrail-merchant-id
required
string non-empty

Logitrail's Merchant ID.

Responses

Response samples

Content type
application/json
{
  • "rows": [
    ]
}

Get warehouse stock value for your products

Returns a point-in-time aggregate of stock quantity × purchase value across your in-warehouse ProductArticles (on_shelf or picked, not yet shipped). Optionally group by batch_number, best_before_date, or purchase_value_currency; defaults to a single grand-total row when group_by is omitted. Returns both per-group rows and grand totals for the requested KPIs (stock_value, article_count; both computed by default).

Authorizations:
idp
query Parameters
group_by
string

Comma-separated dimensions to group by. Allowed: batch_number, best_before_date, purchase_value_currency, product_id. Omit for a single grand-total row.

kpis
string

Comma-separated KPIs to compute. Allowed: stock_value, article_count. Default: both.

header Parameters
x-logitrail-merchant-id
required
string non-empty

Logitrail's Merchant ID.

Responses

Response samples

Content type
application/json
{
  • "statistics": {
    }
}

Get warehouse stock value history for your products

Returns a historical, date-ranged aggregate of stock quantity × purchase value from periodic warehouse snapshots for your products. Optionally group by stat_date and/or product_id (combine both for a per-product timeline); defaults to a single grand-total row when group_by is omitted. Set the date range with stat_date_min (required) and stat_date_max (optional, defaults to today), both YYYY-MM-DD and inclusive; the range may span at most 92 days. Returns both per-group rows and grand totals for the requested KPIs (stock_value, article_count; both computed by default).

Authorizations:
idp
query Parameters
stat_date_min
string

Required. Inclusive lower bound (YYYY-MM-DD) on the snapshot date. The range from stat_date_min to stat_date_max may span at most 92 days.

stat_date_max
string

Inclusive upper bound (YYYY-MM-DD) on the snapshot date. Omit to use today.

group_by
string

Comma-separated dimensions to group by. Allowed: stat_date, product_id, purchase_value_currency. Omit for a single grand-total row. Combine stat_date and product_id for a per-product timeline.

kpis
string

Comma-separated KPIs to compute. Allowed: stock_value, article_count. Default: both.

header Parameters
x-logitrail-merchant-id
required
string non-empty

Logitrail's Merchant ID.

Responses

Response samples

Content type
application/json
{
  • "statistics": {
    }
}

Find orders this order can take reserved stock from

For an order that is waiting for stock, lists what it is still missing and which of your other orders hold that stock on the shelf and could give it up, so this order can ship first. Each product shows how many units are missing (shortfall) and any best-before date or batch requirement of the order line; only stock matching that requirement is counted. Each proposed order shows how many units it could give per product and whether it alone covers everything that is missing. Orders that only cover part of it are listed too. Only orders in status confirmed or waiting_products that are not yet being picked are proposed, and stock already being picked for an order is never counted. Nothing is changed. Error codes: RECEIVING_ORDER_STATUS_NOT_ALLOWED — the order is not confirmed or waiting_products; RECEIVING_ORDER_LOCKED_FOR_PICKING — the order is already being picked; RECEIVING_ORDER_NOT_IN_QUEUE — the order is not yet ready for fulfilment.

Authorizations:
idp
path Parameters
orderId
required
string^[a-f0-9]{24}$

The receiving order: the order that must ship first and takes reservations from other orders.

header Parameters
x-logitrail-merchant-id
required
string non-empty

Logitrail's Merchant ID.

Responses

Response samples

Content type
application/json
{
  • "order_id": "string",
  • "order_number": "string",
  • "merchant_id": "string",
  • "products": [
    ],
  • "skipped_products": [
    ],
  • "candidate_count": 0,
  • "candidates": [
    ]
}

Take reserved stock from your other orders

Moves stock that is reserved on the shelf for the given orders (donor_order_ids, at most 20, all yours) to this order, so it can ship first. Only what this order is missing is moved; in exchange this order gives up the reservations that were still waiting for stock, so every order keeps its reserved amount and the other orders wait for the next stock instead. Best-before date and batch requirements of this order's lines are respected. Everything happens at once or not at all, and every rule is checked again at that moment, so the result may differ from an earlier proposal. Use dry_run: true to preview the result without changing anything. An order or product that could not give stock is reported in the response with a reason code instead of failing the request: DONOR_ORDER_NOT_FOUND (not one of your orders), DONOR_ORDER_STATUS_NOT_ALLOWED (not confirmed or waiting_products), DONOR_ORDER_LOCKED_FOR_PICKING (already being picked), DONOR_ORDER_NOT_IN_QUEUE (not yet ready for fulfilment), DONOR_ORDER_IS_RECEIVING_ORDER, NO_ELIGIBLE_UNITS (no matching stock on the shelf), SHORTFALL_ALREADY_COVERED, PRODUCT_LIMIT_REACHED (your product_limits cap), PRODUCT_HAS_MULTIPLE_RESTRICTION_SLOTS (the product is on several order lines with different best-before or batch requirements and is left alone). The change is recorded in the logbook of every order involved.

Authorizations:
idp
path Parameters
orderId
required
string^[a-f0-9]{24}$

The receiving order: the order that must ship first and takes reservations from other orders.

header Parameters
x-logitrail-merchant-id
required
string non-empty

Logitrail's Merchant ID.

Request Body schema: application/json
donor_order_ids
required
Array of strings [ 1 .. 20 ] items [ items^[a-f0-9]{24}$ ]

Orders to take reservations from, in the order they should be drained. Must belong to the same merchant as the receiving order.

Array of objects

Optional cap on the units taken per product, across all donor orders. Products not listed are taken up to the receiving order’s shortfall.

dry_run
boolean
Default: false

When true, runs the take and reports the result without saving anything.

Responses

Request samples

Content type
application/json
{
  • "donor_order_ids": [
    ],
  • "product_limits": [
    ],
  • "dry_run": false
}

Response samples

Content type
application/json
{
  • "order_id": "string",
  • "order_number": "string",
  • "merchant_id": "string",
  • "dry_run": true,
  • "committed": true,
  • "total_taken": 0,
  • "products": [
    ],
  • "skipped_products": [
    ],
  • "donors": [
    ]
}

Split stock of a product into another SKU

Moves the units of one of your products (source_sku) that are on the shelf and available into another SKU (target.sku), keeping each unit on its shelf and with its best-before date. Use it, for example, to separate a best-before batch into its own product. Units that are reserved for orders, being picked, not yet shelved, blocked, or removed are never moved. If you have no product with the target SKU it is created as a copy of the source product (name, translations and other details; you can override the name); an existing target product is used as it is. filters narrow down what is moved: best_before (exact date), location (shelf code) and max_quantity, combined with AND. If nothing matches, the request succeeds with moved_quantity 0 and nothing is created. The source units are blocked with an incident and the same number of units appear in the target product, booked through a system-made inbound shipment without invoicing; no inbound_shipment.processed webhook is sent for it, but inventory change webhooks are sent for both products. The Idempotency-Key header is required: repeat the request with the same key to safely retry, and the first result is returned. A single split moves at most 5000 units. Only one split of the same source product runs at a time (409 SPLIT_IN_PROGRESS; retry shortly). If the stock records of the source product are inconsistent the request is refused with 409 PHANTOM_STOCK_SUSPECTED and nothing is changed; contact Logitrail support.

Authorizations:
idp
header Parameters
x-logitrail-merchant-id
required
string non-empty

Logitrail's Merchant ID.

idempotency-key
required
string [ 1 .. 64 ] characters ^[\x21-\x7E]+$
Example: 4f0d6c2e-6a43-4c55-9d0e-2b8f1f7a9c31

Required key that makes retrying this request safe. Generate a new value, for example a UUID, for every new split, and send the same value on every retry of that split. A repeat with the same key and the same body returns the result of the first request (with the response header Idempotent-Replayed: true) and moves nothing; the same key with a different body is refused with 409 IDEMPOTENCY_KEY_REUSED. Keys are scoped to the merchant and remembered for 30 days. Up to 64 visible ASCII characters, no spaces.

Request Body schema: application/json
source_sku
required
string non-empty

SKU of the product whose stock is split. The product must belong to your merchant account.

required
object

The product that receives the split stock.

object

Optional filters, combined with AND. Without filters every available unit of the source product is moved.

Responses

Request samples

Content type
application/json
{
  • "source_sku": "41473",
  • "target": {
    },
  • "filters": {
    }
}

Response samples

Content type
application/json
{
  • "moved_quantity": 9007199254740991,
  • "source_product_id": "string",
  • "source_sku": "string",
  • "target_product_id": "string",
  • "target_sku": "string",
  • "target_created": true,
  • "inbound_shipment_id": "string",
  • "incident_id": "string"
}