Keep a shop's stock in step with BIRP
Read the quantities BIRP holds, and record receipts, corrections and losses that happen outside BIRP.
Version v1.1, updated
On this page
You will show in a shop the quantities BIRP holds, and record in BIRP the stock changes the shop sees first. BIRP keeps stock per product, warehouse and unit.
Before you start
A key with
stock:readto read levels and warehouses,stock:writeto record adjustments, andproducts:readto match products.The products already in BIRP, linked to your ids. See Sync a product catalogue into BIRP.
Note. Adjustments apply to resale goods. The stock of finished products changes through production in BIRP, and the API reads it only.
Steps
Find the warehouses
GET /v1/warehouses lists the warehouses of the company. A warehouse of kind retail belongs to a shop and is never adjusted through the API; adjust the warehouses of kind warehouse.
Read the levels
GET /v1/stock/levels returns one row per product, warehouse and unit. Narrow it with warehouse_id or product_id, and read every page with the cursor.
curl "https://api.birp.io/v1/stock/levels?limit=200" \
-H "Authorization: Bearer $BIRP_API_KEY"const response = await fetch("https://api.birp.io/v1/stock/levels?limit=200", {
headers: {
Authorization: `Bearer ${process.env.BIRP_API_KEY}`,
},
});
if (!response.ok) throw new Error(`BIRP API error ${response.status}`);
const result = await response.json();<?php
$ch = curl_init("https://api.birp.io/v1/stock/levels?limit=200");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("BIRP_API_KEY"),
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);import os
import requests
response = requests.request(
"GET",
"https://api.birp.io/v1/stock/levels",
params={"limit": 200},
headers={
"Authorization": f"Bearer {os.environ['BIRP_API_KEY']}",
},
timeout=10,
)
response.raise_for_status()
result = response.json()Stock levels have no updated_since. To keep a shop current, read the levels of the products it sells on a schedule, warehouse by warehouse.
Record a receipt, a correction or a loss
POST /v1/stock/adjustments records a change of a goods item in a warehouse. The reason sets the direction the quantity may take.
curl -X POST "https://api.birp.io/v1/stock/adjustments" \
-H "Authorization: Bearer $BIRP_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"note":"Weekly count","product":{"sku":"SP-FLOUR-25"},"quantity":"-2.5","reason":"adjustment","reference":"WC-COUNT-2026-10-05","warehouse":{"id":"00000000-0000-4000-8000-000000000041"}}'const response = await fetch("https://api.birp.io/v1/stock/adjustments", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BIRP_API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"note": "Weekly count",
"product": {
"sku": "SP-FLOUR-25"
},
"quantity": "-2.5",
"reason": "adjustment",
"reference": "WC-COUNT-2026-10-05",
"warehouse": {
"id": "00000000-0000-4000-8000-000000000041"
}
}),
});
if (!response.ok) throw new Error(`BIRP API error ${response.status}`);
const result = await response.json();<?php
$ch = curl_init("https://api.birp.io/v1/stock/adjustments");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("BIRP_API_KEY"),
"Idempotency-Key: " . bin2hex(random_bytes(16)),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => <<<'JSON'
{
"note": "Weekly count",
"product": {
"sku": "SP-FLOUR-25"
},
"quantity": "-2.5",
"reason": "adjustment",
"reference": "WC-COUNT-2026-10-05",
"warehouse": {
"id": "00000000-0000-4000-8000-000000000041"
}
}
JSON,
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);import os
import uuid
import requests
response = requests.request(
"POST",
"https://api.birp.io/v1/stock/adjustments",
json={
"note": "Weekly count",
"product": {
"sku": "SP-FLOUR-25"
},
"quantity": "-2.5",
"reason": "adjustment",
"reference": "WC-COUNT-2026-10-05",
"warehouse": {
"id": "00000000-0000-4000-8000-000000000041"
}
},
headers={
"Authorization": f"Bearer {os.environ['BIRP_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
timeout=10,
)
response.raise_for_status()
result = response.json()Put your own document number in reference, so the movement can be found in BIRP. Without unit, the unit of the product applies.
Check the result
The answer holds the level that results from the adjustment, the same row GET /v1/stock/levels returns afterwards.
{
"data": {
"created_at": "2026-10-05T09:12:44.120Z",
"id": "a1b2c3d4-0000-4000-8000-0000000000aa",
"level": {
"id": "EXAMPLE-LEVEL-0001",
"lot_count": 2,
"product_id": "00000000-0000-4000-8000-000000000011",
"product_kind": "goods",
"quantity": "12.5",
"unit": "kg",
"warehouse_id": "00000000-0000-4000-8000-000000000041"
},
"movements": [
{
"id": "b2c3d4e5-0000-4000-8000-0000000000bb",
"lot_id": "c3d4e5f6-0000-4000-8000-0000000000cc",
"quantity": "-2.500",
"type": "adjustment"
}
],
"note": "Weekly count",
"product_id": "00000000-0000-4000-8000-000000000011",
"quantity": "-2.500",
"reason": "adjustment",
"reference": "WC-COUNT-2026-10-05",
"unit": "kg",
"warehouse_id": "00000000-0000-4000-8000-000000000041"
}
}What can go wrong
Related
- warehouses-and-stock
- sync-products
- pagination-and-filtering