Warehouse
The warehouse module tracks goods with a movement ledger: a product's stock level is the sum of its receipts and issues. Through the API you manage warehouses, issue warehouse documents (WZ, PZ, PW, RW, MM), record stock adjustments, fetch a stock report for any day and issue WZ (goods issue) documents for sales invoices.
The module requires the MAX plan on the Fakturnia account. Without it every warehouse endpoint returns 403 with the SUBSCRIPTION_REQUIRED code. There is no limit on the number of warehouses.
Scopes: warehouse:browse, warehouse:create, warehouse:update, warehouse:delete, warehouse:stock:read, warehouse:adjust, warehouse:wz:create, warehouse:documents:create.
Warehouses
# List
curl -s https://prod.api.fakturnia.pl/external/v1/warehouse \
-H "Authorization: Bearer $TOKEN"
# Create
curl -s -X POST https://prod.api.fakturnia.pl/external/v1/warehouse \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "Magazyn główny", "symbol": "MAG1", "address": "ul. Prosta 1, Warszawa" }'
The first warehouse automatically becomes the default one. symbol goes into warehouse document numbering. Update with PUT /warehouse/{id}, delete with DELETE /warehouse/{id}.
Deletion is blocked (409 CONFLICT) in two cases: the warehouse has stock movements, or it is the only warehouse on the account.
Warehouse documents (WZ, PZ, PW, RW, MM)
POST /warehouse/documents issues a warehouse document and books its stock movements at once. POST /warehouse/documents/validate takes the same body and returns a preview without saving: the number, warehouses, catalogue lines with the stock before and after, and missing goods. Scope: warehouse:documents:create.
curl -s -X POST https://prod.api.fakturnia.pl/external/v1/warehouse/documents \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"documentType": "PZ",
"issueDate": "2026-09-27",
"buyerId": "665d0a1234567890abcdef56",
"products": [ { "productId": "665f0c1234567890abcdef12", "quantity": 20, "netPrice": 45.5 } ]
}'
| Type | Meaning | Contractor (buyerId / buyer) |
Warehouse |
|---|---|---|---|
WZ |
goods issue | recipient | warehouseId (defaults to the default one) |
PZ |
goods receipt | supplier | warehouseId (defaults to the default one) |
PW |
internal receipt | none | warehouseId (defaults to the default one) |
RW |
internal issue | none | warehouseId (defaults to the default one) |
MM |
transfer between warehouses | none | sourceWarehouseId (defaults to the default one) and targetWarehouseId (required) |
Rules:
- Lines reference catalogue products (
productId); the movement is booked on that product. Name, unit and price come from the catalogue unless you send them: PZ/PW use the purchase price (purchasePrice), WZ/RW/MM the net sale price. A service used on a warehouse document is marked as goods in the catalogue. - Missing goods block an issue. WZ, RW and MM that would take stock below zero (stock minus reservations of other pending documents) are rejected with
422 STOCK_SHORTAGE, like in the app.allowNegativeStock: trueissues the document anyway. - Account settings apply like in the editor: the type's symbol and numbering, place of issue, comments, signatures from the warehouse signature setting, the goods issue / receipt date (WZ, PZ) and the release confirmation mode (
stockStatus: "pending"reserves the goods until confirmed in the app).useAccountDefaults: falseswitches the defaults off; any field you send wins. - WZ for an invoice:
sourceInvoiceIdlinks the documents both ways and the WZ number is printed on the invoice. WithoutbuyerIdthe recipient comes from the invoice. - An unknown or foreign warehouse, an MM without a target or with the same warehouse twice:
422 WAREHOUSE_INVALID. An unknown product:404 PRODUCT_NOT_FOUND. - The lines of an issued warehouse document cannot be changed with
PUT/PATCH /documents/{id}(409 WAREHOUSE_LINES_LOCKED): delete the document and issue a new one. Changing the date moves the movements, andDELETE /documents/{id}reverses them.
Stock adjustments (stocktaking)
A manual adjustment creates a movement of type INW. It is also how you record the opening balance when starting the module:
curl -s -X POST https://prod.api.fakturnia.pl/external/v1/warehouse/adjustments \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"productId": "665f0c1234567890abcdef12",
"warehouseId": "665e0b1234567890abcdef34",
"quantity": 10,
"unitCost": 80,
"note": "Opening balance"
}'
| Field | Meaning |
|---|---|
productId |
catalog product ID (Products) |
warehouseId |
warehouse ID |
quantity |
stock change: positive = receipt, negative = issue |
unitCost |
net unit cost, the valuation base for receipts (optional) |
date |
movement date YYYY-MM-DD, defaults to today |
note |
description shown in the movement history |
Stock report
curl -s "https://prod.api.fakturnia.pl/external/v1/warehouse/stock?asOf=2026-06-30" \
-H "Authorization: Bearer $TOKEN"
{
"success": true,
"data": {
"levels": [
{ "productId": "665f0c...", "warehouseId": "665e0b...", "quantity": 9 }
],
"asOf": "2026-06-30"
}
}
Without parameters it returns the current state. asOf gives the state at the end of the given day (e.g. for stocktaking), warehouseId limits the report to a single warehouse.
Automatic WZ for an invoice
After issuing a sales invoice, a single call creates a WZ document and takes the goods off stock:
curl -s -X POST https://prod.api.fakturnia.pl/external/v1/warehouse/wz-from-invoice/{invoiceId} \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "warehouseId": "665e0b1234567890abcdef34" }'
Rules:
- Only goods lines make it onto the WZ: lines that exist in the product catalog with
productType: "goods"set. Services and lines not in the catalog are skipped and their names come back in theskippedfield. - If no line is goods, you get a
422with a hint to mark products as goods in the catalog. - One WZ per invoice; a second attempt returns
409 CONFLICT. warehouseIdis optional, the default warehouse is used when omitted; an unknown warehouse is422 WAREHOUSE_INVALID.issueDatedefaults to the invoice date.- Documents are linked both ways (the WZ number is printed on the invoice and sent to KSeF) and the stock movements (issue) are generated automatically.
- The WZ gets the account settings like a document from the editor (WZ symbol, place, comments, signatures, release confirmation mode).
- Missing goods reject the WZ (
422 STOCK_SHORTAGE) unless you sendallowNegativeStock: true.
WZ documents appear on the document list through the type filter: GET /documents?documentType=WZ (see Pagination and filtering).
One warehouse and the movement ledger
GET /warehouse/{id} returns one warehouse (404 for a foreign or deleted one). GET /warehouse/movements is the ledger the stock levels are computed from: every receipt and issue with its document, quantity, unit cost and the person who recorded it. Filters productId, warehouseId, documentId, type (comma-separated list), dateFrom / dateTo; paginated like the lists. The stock report additionally accepts productId when you care about one product. Examples and the entry shape: History & activity.
Best practices
- Read stock from the report, do not compute it yourself.
GET /warehouse/stocksums the movements exactly like the app does, andasOfgives a historical level (say, for a month-end stocktake). A local copy of stock levels will drift sooner or later. - Issue with a document, not an adjustment. WZ/RW/MM guard the stock level (
422 STOCK_SHORTAGE), while a negative adjustment (quantity < 0) is accepted without a check. An adjustment withquantity: 0andunitCostis a revaluation (WYCENAmovement) that keeps the quantity. - Provide
unitCoston receipts. It is the basis of stock valuation; receipts without a cost impoverish the value reports. - Create WZ for invoices through
wz-from-invoice, not manually. The endpoint links the documents both ways, generates the movements and guards against duplicates (409on a second attempt), so idempotency comes for free. - Maintain the product catalog. Only lines with
productType: "goods"make it onto a WZ; if theskippedfield returns names you did not expect, fix the types in the catalog first instead of working around it on your side. - Filter warehouse documents by type.
GET /documents?documentType=WZ(or a comma-separated list likeWZ,PZ) returns them along with the rest of the document metadata.