Create Stock Movement
POST /stock-movements
Record a check-in, check-out, stock addition or stock removal
Scope stock:write, plus the owner's role permission for the type (transactions.checkin,
checkout, add_stock, remove_stock).
With is_draft: false the movement is finalized at once: stock changes and ledger rows are
written. With is_draft: true (the default) nothing changes until
POST /stock-movements/{id}/finalize.
Rules, the same as in Next Inventory itself: check_out needs a recipient_id;
check_out / remove_stock are refused (400) when an item has less stock than requested;
check_in / check_out take assets only, add_stock / remove_stock consumables and
sellables only.
Request
POST https://example.com/api/index.php/v1/nextinventory/stock-movements
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
Idempotency-Key |
string |
header |
No | Optional. Any unique string (1 to 191 printable ASCII characters, no spaces; a UUID is ideal). The request runs once: a retry with the same key gets the first response back, with the header Idempotent-Replayed: true, instead of running again. Keys are kept for 24 hours per token. 409 IDEMPOTENCY_IN_PROGRESS while the first request is still running; 422 IDEMPOTENCY_KEY_REUSED if the key comes with a different request. A 5xx answer is not kept, so it can be retried with the same key. (min length 1, max length 191, pattern ^[\x21-\x7E]+$) |
Request body
Content type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
movement_type |
string |
Yes | One of check_in, check_out, add_stock, remove_stock. |
movement_date |
string |
Yes | (date) |
warehouse_id |
integer |
Yes | |
recipient_id |
integer |
No | Required for check_out. |
reference |
string |
No | |
details |
string |
No | |
is_draft |
boolean |
No | Defaults to true. |
items |
object[] |
Yes | |
items.item_id |
integer |
Yes | |
items.quantity |
number |
Yes | |
items.cost_per_unit |
number |
No | Stock increases under FIFO/LIFO; defaults to the item's unit price. |
items.weight |
number |
No | |
items.weight_unit |
string |
No |
{
"movement_type": "add_stock",
"is_draft": false,
"movement_date": "2026-11-11",
"warehouse_id": 1,
"reference": "PO-1042",
"items": [
{
"item_id": 17,
"quantity": 40,
"cost_per_unit": 3.2
}
]
}
Responses
201 — Created.
{
"data": {
"id": 1
}
}
| Field | Type | Meaning |
|---|---|---|
data.id |
integer |
Errors
| Status | Code | When |
|---|---|---|
4 |
A refusal. Common codes: MISSING_TOKEN / INVALID_TOKEN (401); API_DISABLED, ACCOUNT_UNUSABLE, ACCESS_DENIED, TASK_NOT_ALLOWED, MISSING_SCOPE, APP_DISABLED, DEMO_LOCKED, FORBIDDEN (403); NOT_FOUND (404); BAD_REQUEST / INVALID_JSON / INVALID_IDEMPOTENCY_KEY (400, validation and stock rules, with the reason in title); IDEMPOTENCY_IN_PROGRESS (409); PRECONDITION_FAILED (412, the record changed since the If-Match ETag was read); IDEMPOTENCY_KEY_REUSED (422); RATE_LIMITED / TOO_MANY_FAILED_LOGINS (429, with a Retry-After header in seconds); SERVER_ERROR (500). |
Examples
curl
curl -s -X POST "https://example.com/api/index.php/v1/nextinventory/stock-movements" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"movement_type":"add_stock","is_draft":false,"movement_date":"2026-11-11","warehouse_id":1,"reference":"PO-1042","items":[{"item_id":17,"quantity":40,"cost_per_unit":3.2}]}'
PHP
<?php
$ch = curl_init('https://example.com/api/index.php/v1/nextinventory/stock-movements');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_TOKEN'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => '{"movement_type":"add_stock","is_draft":false,"movement_date":"2026-11-11","warehouse_id":1,"reference":"PO-1042","items":[{"item_id":17,"quantity":40,"cost_per_unit":3.2}]}',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
JavaScript
const API_TOKEN = process.env.API_TOKEN;
const response = await fetch("https://example.com/api/index.php/v1/nextinventory/stock-movements", {
method: 'POST',
headers: {
"Authorization": `Bearer ${API_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"movement_type": "add_stock",
"is_draft": false,
"movement_date": "2026-11-11",
"warehouse_id": 1,
"reference": "PO-1042",
"items": [
{
"item_id": 17,
"quantity": 40,
"cost_per_unit": 3.2
}
]
}),
});
const data = await response.json();