Skip to content

Set or adjust stock levels for up to 100 product/outlet lines in one all-or-nothing request

POST
/inventory/batch
curl --request POST \
--url https://api.oletapp.com/your-store/v1/inventory/batch \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "lines": [ { "adjust": { "quantity": 1, "reason_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "unit_cost": 1 }, "outlet_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "product_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "set": { "on_hand": 1, "reason_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "unit_cost": 1 }, "sku": "example" } ] }'

Each line names exactly one of set (absolute, same rules as PUT /products/{id}/inventory/{outlet_id}) or adjust (relative, same rules as POST /inventory/adjustments), and exactly one of product_id or sku.

  • sku matches product.sku case-insensitively among live products only, never a barcode. Every sku in the batch is resolved in ONE query before the transaction; an unknown sku fails the whole batch.

  • No duplicate product_id/outlet_id pair is allowed anywhere in the batch, including an sku line resolving to the same product/outlet as a product_id line.

  • Rows are locked in a stable order and the whole batch commits or nothing does: one bad line fails the whole request and writes zero rows.

  • Idempotency-Key is REQUIRED: a retried batch replayed without one could apply its relative adjust lines twice, moving stock again.

  • 400 VALIDATION_FAILED: a line named zero or both of set/adjust, zero or both of product_id/sku, or duplicated a product_id/outlet_id pair.

  • 422 UNPROCESSABLE: one or more sku values did not match a live product, naming lines.N.

  • 422 INSUFFICIENT_STOCK: an adjust line would take stock below zero, naming lines.N.

  • 422 REASON_NOT_ALLOWED: a line names a reason that is a system reason or is turned off, naming lines.N. A reason must be enabled and must not be a system reason.

  • 422 OUTLET_DELETED: a line names an outlet that has been deleted. Nothing is written.

A set or adjust line may carry unit_cost (integer cents, optional). On a set line it becomes the average cost for the outlet whatever the direction, including when on_hand is unchanged; left out, the current average is kept, or the supply price when there is none. On an adjust line it is blended into the average when stock goes up and ignored otherwise.

Requires scope: inventory:write

Media typeapplication/json
object
lines
required
Array<object>
>= 1 items <= 100 items
object
adjust
object
quantity
required
number
reason_id
required
string format: uuid
/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/
unit_cost
Any of:
integer
<= 900719925474
outlet_id
required
string format: uuid
/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/
product_id
string format: uuid
/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/
set
object
on_hand
required
number
reason_id
string format: uuid
/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/
unit_cost
Any of:
integer
<= 900719925474
sku
string
>= 1 characters <= 100 characters
Examplegenerated
{
"lines": [
{
"adjust": {
"quantity": 1,
"reason_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"unit_cost": 1
},
"outlet_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"product_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"set": {
"on_hand": 1,
"reason_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"unit_cost": 1
},
"sku": "example"
}
]
}

Set or adjust stock levels for up to 100 product/outlet lines in one all-or-nothing request

Media typeapplication/json
object
data
required
Array<object>
object
changed
required
boolean | null
delta
required
number | null
on_hand
required
number | null
outlet_id
required
string format: uuid
/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/
previous
required
number | null
product_id
required
string format: uuid
/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/
replayed

True only on an idempotency replay.

boolean
sku

Echoed only when the line was given by sku.

string
Examplegenerated
{
"data": [
{
"changed": true,
"delta": 1,
"on_hand": 1,
"outlet_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"previous": 1,
"product_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"replayed": true,
"sku": "example"
}
]
}

An error response.

Media typeapplication/json
object
error
required
object
code
required

A machine-readable error code, e.g. VALIDATION_FAILED.

string
Allowed values: UNAUTHORIZED PLAN_REQUIRED INSUFFICIENT_SCOPE NOT_FOUND VALIDATION_FAILED CONFLICT STALE_VERSION RATE_LIMITED IDEMPOTENCY_KEY_REUSED IDEMPOTENCY_IN_PROGRESS IMAGE_FETCH_FAILED UNPROCESSABLE INTERNAL RETAILER_CANCELLED RETAILER_SUSPENDED RETAILER_NOT_FOUND TRIAL_DECISION_REQUIRED PLAN_USERS_EXCEEDED RETAILER_PAUSED PRODUCT_LIMIT_REACHED NOT_STOCK_TRACKED REASON_DIRECTION_MISMATCH REASON_NOT_ALLOWED INSUFFICIENT_STOCK OUTLET_DELETED
fields
Array<object>
object
field
required
string
message
required
string
message
required

A plain-English sentence describing what went wrong.

string
Example
{
"error": {
"code": "UNAUTHORIZED"
}
}