Goods received
Goods received on purchase orders and the GR/IR account (goods received, not invoiced): what a receipt posts, per order line what was received, billed and is still open, and the open items on a day tied to the GR/IR account.
| Operation | Summary | Scope | Idempotency-Key |
|---|---|---|---|
GET /v1/entities/{entity}/grni |
Goods received not invoiced on a day | read |
|
GET /v1/purchase-orders/{order}/goods-received |
Received, billed and open GR/IR per order line | read |
|
POST /v1/purchase-orders/{order}/grni-write-off |
Write off goods received that will never be billed | post |
required |
POST /v1/purchase-orders/{order}/receipts/preview |
Show what a receipt would post | read |
GET /v1/entities/{entity}/grni
Goods received not invoiced on a day
The GR/IR open items: every purchase order line with goods or services received and not billed on the day, at receipt value, and their total tied to the GR/IR account of the ledger (the difference is zero). The period-end accrual list.
| Required scope | read (admin implies every scope) |
| Operation id | grni_open_items_v1_entities__entity__grni_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01. |
| query | as_of |
string (date) or null | The day (end of day); default today. |
Example request
curl "$ERP_URL/v1/entities/CH01/grni" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | GrniReportOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"entity": "string",
"as_of": "2026-09-21",
"currency": "string",
"accounts": [
"string"
],
"lines": [
{
"order_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"order_number": "string",
"vendor": "string",
"vendor_name": "string",
"line_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"line_no": 1,
"description": "string",
"item": "string",
"last_receipt": "2026-09-21",
"received_quantity": "string",
"received_value": "string",
"cleared_quantity": "string",
"cleared_value": "string",
"written_off_value": "string",
"open_quantity": "string",
"open_value": "string"
}
],
"billed_after_write_off": [
{
"order_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"order_number": "string",
"vendor_name": "string",
"line_no": 1,
"description": "string",
"quantity": "string"
}
],
"total": "string",
"ledger": "string",
"difference": "string"
}
GET /v1/purchase-orders/{order}/goods-received
Received, billed and open GR/IR per order line
Per order line: what was received at receipt value, what bills billed (net of credit notes) and cleared, and what is open on the GR/IR account (received, not billed).
| Required scope | read (admin implies every scope) |
| Operation id | order_goods_received_v1_purchase_orders__order__goods_received_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | order |
string | yes | Purchase order id (uuid) or number, e.g. PO-2026-000001. |
Example request
curl "$ERP_URL/v1/purchase-orders/PO-2026-000001/goods-received" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | OrderGrniOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"order_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"order_number": "string",
"currency": "string",
"lines": [
{
"line_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"line_no": 1,
"description": "string",
"item": "string",
"last_receipt": "2026-09-21",
"received_quantity": "string",
"received_value": "string",
"billed_quantity": "string",
"cleared_quantity": "string",
"cleared_value": "string",
"written_off_quantity": "string",
"written_off_value": "string",
"open_quantity": "string",
"open_value": "string",
"billed_after_write_off": "string"
}
],
"open_value": "string",
"write_off_waiting": true
}
POST /v1/purchase-orders/{order}/grni-write-off
Write off goods received that will never be billed
Take what is open on the GR/IR account of the order's lines (goods received free of
charge, or billed without the order's number) off GR/IR: one journal entry debits GR/IR and
credits the chosen account (default: the line's own account, else that of its bills, else
for a stock item the inventory changes account). It is dated from the line's last receipt
to today. The stock is not touched. People only (policy action record_receipt); the entry
passes the posting approval (limit and four eyes), and while it waits the lines count as
written off. Refusals: grni_nothing_open, grni_line_nothing_open,
grni_write_off_pending, grni_write_off_future, grni_write_off_before_receipt,
grni_write_off_control_account, grni_write_off_account_missing.
| Required scope | post (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | write_off_grni_v1_purchase_orders__order__grni_write_off_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | order |
string | yes | Purchase order id (uuid) or number, e.g. PO-2026-000001. |
Request body
application/json (required): GrniWriteOffInput
| Field | Type | Required | Description |
|---|---|---|---|
write_off_date |
string (date) or null | Day of the entry; default today (company-local). | |
reason |
string | yes | Why it will never be billed; kept on the entry. (min length 1; max length 500) |
lines |
array of integer or null | The order lines to write off; default every line with something open. | |
account |
string or null | The account credited (debited for a debit balance), by code or id. Default: for a stock item the inventory changes account (role inventory_adjustment), for any other line its own account. |
Example request
curl -X POST "$ERP_URL/v1/purchase-orders/PO-2026-000001/grni-write-off" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"lines": [
2
],
"reason": "Samples the vendor sent free of charge",
"write_off_date": "2026-09-30"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | GrniWriteOffOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"order_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"order_number": "string",
"entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "string",
"currency": "string",
"lines": [
{
"line_no": 1,
"quantity": "string",
"value": "string",
"account": "string"
}
],
"total": "string"
}
POST /v1/purchase-orders/{order}/receipts/preview
Show what a receipt would post
The value each line of a receipt would post, in the functional currency at the receipt day's rate, and the account it debits (inventory, or the order line's account) against the GR/IR account. Nothing is recorded.
| Required scope | read (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | preview_receipt_v1_purchase_orders__order__receipts_preview_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | order |
string | yes | Purchase order id (uuid) or number, e.g. PO-2026-000001. |
Request body
application/json (required): ReceiptInput
| Field | Type | Required | Description |
|---|---|---|---|
receipt_date |
string (date) or null | Date the goods or services arrived (ISO 8601); default today where the company is. | |
note |
string or null | Free text, e.g. the delivery note number (up to 500 characters). (max length 500) | |
warehouse |
string or null | Code or id of the warehouse the stock items come into (or go back from); default the entity's default warehouse. Lines that are no stock items ignore it. (max length 40) | |
lines |
array of ReceiptLineInput |
yes | What was received per order line, 1 to 500 lines. (min items 1; max items 500) |
Example request
curl -X POST "$ERP_URL/v1/purchase-orders/PO-2026-000001/receipts/preview" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"lines": [
{
"line_no": 1,
"quantity": "6"
}
],
"note": "Delivery note 4711",
"receipt_date": "2026-09-08"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | ReceiptPreviewOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"order_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"order_number": "string",
"receipt_date": "2026-09-21",
"currency": "string",
"order_currency": "string",
"rate": "string",
"warehouse": "string",
"grni_account": "string",
"lines": [
{
"line_no": 1,
"description": "string",
"item": "string",
"quantity": "string",
"value": "string",
"account": "string",
"posts": true
}
],
"total": "string"
}
Schemas
GrniLateBill
| Field | Type | Required | Description |
|---|---|---|---|
order_id |
string (uuid) | yes | Id of the purchase order. |
order_number |
string | yes | Number of the purchase order. |
vendor_name |
string | yes | Name of the vendor. |
line_no |
integer | yes | Number of the order line. |
description |
string | yes | Description of the order line. |
quantity |
string | yes | Billed after the line was written off. |
GrniOpenLine
| Field | Type | Required | Description |
|---|---|---|---|
order_id |
string (uuid) | yes | Id of the purchase order. |
order_number |
string | yes | Number of the purchase order. |
vendor |
string | yes | Vendor code. |
vendor_name |
string | yes | Name of the vendor. |
line_id |
string (uuid) | yes | Id of the order line. |
line_no |
integer | yes | Number of the order line. |
description |
string | yes | Description of the order line. |
item |
string or null | yes | Code of the stock item; null for none. |
last_receipt |
string (date) or null | yes | Day of the line's last receipt. |
received_quantity |
string | yes | Received up to the day. |
received_value |
string | yes | Its receipt value. |
cleared_quantity |
string | yes | Cleared by bills up to the day. |
cleared_value |
string | yes | The receipt value cleared. |
written_off_value |
string | yes | The receipt value written off up to the day, as never to be billed. |
open_quantity |
string | yes | Received, not billed, not written off. |
open_value |
string | yes | Received, not billed: the accrual. |
GrniReportOut
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Entity code. |
as_of |
string (date) | yes | The day, end of day. |
currency |
string | yes | Functional currency. |
accounts |
array of string | yes | The GR/IR account codes. |
lines |
array of GrniOpenLine |
yes | Every order line with something received and not billed (or billed and sent back), oldest order first. |
billed_after_write_off |
array of GrniLateBill |
yes | Order lines billed up to the day after they were written off as never to be billed: to check with the vendor (a credit note if the bill is wrong). |
total |
string | yes | Total received, not billed. |
ledger |
string | yes | Balance of the GR/IR accounts on the day (credit), from posted entries. |
difference |
string | yes | Total less ledger: zero, since only receipts, their clearing and write-offs book the GR/IR account. |
GrniWriteOffInput
| Field | Type | Required | Description |
|---|---|---|---|
write_off_date |
string (date) or null | Day of the entry; default today (company-local). | |
reason |
string | yes | Why it will never be billed; kept on the entry. (min length 1; max length 500) |
lines |
array of integer or null | The order lines to write off; default every line with something open. | |
account |
string or null | The account credited (debited for a debit balance), by code or id. Default: for a stock item the inventory changes account (role inventory_adjustment), for any other line its own account. |
GrniWriteOffLine
| Field | Type | Required | Description |
|---|---|---|---|
line_no |
integer | yes | Number of the order line. |
quantity |
string | yes | The quantity written off. |
value |
string | yes | Its receipt value, taken off GR/IR. |
account |
string | yes | The account it goes to. |
GrniWriteOffOut
| Field | Type | Required | Description |
|---|---|---|---|
order_id |
string (uuid) | yes | Id of the purchase order. |
order_number |
string | yes | Number of the purchase order. |
entry_id |
string (uuid) | yes | Id of the journal entry. |
status |
string | yes | posted, or pending_approval while a second person decides it (the write-off counts once posted; a rejected one leaves the lines open). |
currency |
string | yes | Functional currency of the entity. |
lines |
array of GrniWriteOffLine |
yes | Per order line written off. |
total |
string | yes | Total taken off GR/IR (negative for a debit balance). |
OrderGrniLine
| Field | Type | Required | Description |
|---|---|---|---|
line_id |
string (uuid) | yes | Id of the order line. |
line_no |
integer | yes | Number of the order line. |
description |
string | yes | Description of the order line. |
item |
string or null | yes | Code of the stock item the line buys; null for none. |
last_receipt |
string (date) or null | yes | Day of the line's last receipt (a write-off is dated no earlier). |
received_quantity |
string | yes | Received, from the posted receipts. |
received_value |
string | yes | Their receipt value (functional currency). |
billed_quantity |
string | yes | Billed on posted bills, net of the quantities credit notes credited. |
cleared_quantity |
string | yes | What bills cleared of what was received. |
cleared_value |
string | yes | The receipt value of what was cleared. |
written_off_quantity |
string | yes | Written off as never to be billed (posted or waiting for approval). |
written_off_value |
string | yes | The receipt value written off. |
open_quantity |
string | yes | Received, not billed, not written off. |
open_value |
string | yes | Its value: the line's GR/IR balance. |
billed_after_write_off |
string | yes | Billed after the line was written off, so nothing to clear: the bill's cost offsets the write-off on its account (zero for none). |
OrderGrniOut
| Field | Type | Required | Description |
|---|---|---|---|
order_id |
string (uuid) | yes | Id of the purchase order. |
order_number |
string | yes | Number of the purchase order. |
currency |
string | yes | Functional currency of the entity. |
lines |
array of OrderGrniLine |
yes | Per order line. |
open_value |
string | yes | The order's GR/IR balance (received, not billed). |
write_off_waiting |
boolean | yes | A write-off of the order waits for a second person to approve it. |
ReceiptPreviewLine
| Field | Type | Required | Description |
|---|---|---|---|
line_no |
integer | yes | Number of the order line. |
description |
string | yes | Description of the order line. |
item |
string or null | yes | Code of the stock item the line buys; null for none. |
quantity |
string | yes | Quantity received (negative: sent back). |
value |
string | yes | What the receipt posts for the line in the functional currency: the receipt value (negative: the value the goods sent back take out). Zero when it posts nothing. |
account |
string or null | yes | The account debited (the stock item's inventory account, or the order line's account); null when the line posts nothing. |
posts |
boolean | yes | False for a line that needs no receipt, or names no account (it posts nothing). |
ReceiptPreviewOut
| Field | Type | Required | Description |
|---|---|---|---|
order_id |
string (uuid) | yes | Id of the purchase order. |
order_number |
string | yes | Number of the purchase order. |
receipt_date |
string (date) | yes | The day of the receipt. |
currency |
string | yes | Functional currency of the entity. |
order_currency |
string | yes | Currency of the order. |
rate |
string | yes | Exchange rate of the receipt day: functional currency per unit of the order's currency (1 in the same currency). |
warehouse |
string or null | yes | The warehouse the stock items come into; null when no line is a stock item. |
grni_account |
string | yes | The GR/IR account credited (debited when sent back). |
lines |
array of ReceiptPreviewLine |
yes | Per receipt line. |
total |
string | yes | Total value the receipt posts. |