Close
Period-end: depreciation, FX revaluation, deferred tax, checklist, close, reopen.
PUT /v1/documents/{document_id}/doubtful
Mark an invoice doubtful
Mark a posted customer invoice doubtful, or change its percent: the allowance run covers
that share of what is open on it (a specific allowance) instead of its aging bucket's
percent. Posts nothing by itself. Refused: 404 for an unknown invoice, 422 when it is not a
posted customer invoice (doubtful_not_invoice).
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | mark_doubtful_v1_documents__document_id__doubtful_put |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | document_id |
string | yes | Document id (uuid) or number, e.g. INV-2026-000001. |
Request body
application/json (required): DoubtfulInput
| Field | Type | Required | Description |
|---|---|---|---|
percent |
decimal string | yes | The share of what is open on the invoice the specific allowance covers (100: nothing is expected to come in). (max 100; greater than 0) |
note |
string or null | Why the invoice is doubtful (optional). (max length 500) |
Example request
curl -X PUT "$ERP_URL/v1/documents/INV-2026-000001/doubtful" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"note": "Dispute",
"percent": "50"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | DoubtfulOut |
| 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)
{
"note": "Dispute",
"percent": "50"
}
DELETE /v1/documents/{document_id}/doubtful
Unmark a doubtful invoice
The invoice is no longer doubtful: the general allowance covers it again. 404 when it is not marked.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | unmark_doubtful_v1_documents__document_id__doubtful_delete |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | document_id |
string | yes | Document id (uuid) or number, e.g. INV-2026-000001. |
Example request
curl -X DELETE "$ERP_URL/v1/documents/INV-2026-000001/doubtful" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 204 | No content | |
| 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 |
GET /v1/entities/{entity}/receivable-allowance/settings
Get the general allowance percents
The company's general allowance: a percent for each aging bucket and whether it is taken
with VAT. default is true while the suggested ones apply (0 % not yet due, 5 % overdue;
with VAT for Swiss and Liechtenstein companies).
| Required scope | read (admin implies every scope) |
| Operation id | allowance_settings_v1_entities__entity__receivable_allowance_settings_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
Example request
curl "$ERP_URL/v1/entities/CH01/receivable-allowance/settings" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | AllowanceSettingsOut |
| 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)
{
"include_vat": true,
"percents": [
{
"bucket": "current",
"percent": "0"
},
{
"bucket": "1-30",
"percent": "5"
},
{
"bucket": "31-60",
"percent": "5"
},
{
"bucket": "61-90",
"percent": "5"
},
{
"bucket": "90+",
"percent": "5"
}
]
}
PUT /v1/entities/{entity}/receivable-allowance/settings
Set the general allowance percents
Set the percent for each of the five aging buckets (each once; 422 allowance_buckets
otherwise) and whether it is taken with VAT. The next allowance run uses them; audited.
| Required scope | post (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | update_allowance_settings_v1_entities__entity__receivable_allowance_settings_put |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
Request body
application/json (required): AllowanceSettingsInput
| Field | Type | Required | Description |
|---|---|---|---|
percents |
array of BucketPercent-Input |
yes | The general allowance in percent for each of the five aging buckets, each bucket once. (min items 5; max items 5) |
include_vat |
boolean | yes | Whether the percent is taken on the receivable with VAT (Swiss practice for the del credere) or on its amount without VAT (the VAT comes back when the loss is final, so German practice takes the net). |
Example request
curl -X PUT "$ERP_URL/v1/entities/CH01/receivable-allowance/settings" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"include_vat": true,
"percents": [
{
"bucket": "current",
"percent": "0"
},
{
"bucket": "1-30",
"percent": "5"
},
{
"bucket": "31-60",
"percent": "5"
},
{
"bucket": "61-90",
"percent": "5"
},
{
"bucket": "90+",
"percent": "5"
}
]
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | AllowanceSettingsOut |
| 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)
{
"include_vat": true,
"percents": [
{
"bucket": "current",
"percent": "0"
},
{
"bucket": "1-30",
"percent": "5"
},
{
"bucket": "31-60",
"percent": "5"
},
{
"bucket": "61-90",
"percent": "5"
},
{
"bucket": "90+",
"percent": "5"
}
]
}
POST /v1/periods/{period_id}/close
Close a period
Lock the period once every check passes. Humans only (policy).
Evaluates the close checklist and, when no check fails, sets the period to closed so
nothing can be posted into it; the response is the checklist afterwards. Reopen with
POST /v1/periods/{period_id}/reopen. Refused: 403 when the policy or the caller's scopes
do not allow closing periods; 409 when the period is already closed or a check fails
(the message names the failing check ids). The entity's own close tasks do not hold the
close (the checks decide it, ADR-0034): a task not done by then stays not done, and is
recorded only after the period is reopened.
| Required scope | approve (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | close_period_v1_periods__period_id__close_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | period_id |
string (uuid) | yes | Fiscal period id (GET /v1/entities/{entity}/periods). |
Example request
curl -X POST "$ERP_URL/v1/periods/3fa85f64-5717-4562-b3fc-2c963f66afa6/close" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | Checklist |
| 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)
{
"entity": "string",
"period": "string",
"period_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"period_status": "string",
"ready": true,
"checks": [
{
"id": "string",
"title": "string",
"status": "fail",
"detail": "string",
"code": "string",
"details": {}
}
]
}
GET /v1/periods/{period_id}/close-checklist
Get the close checklist of a period
Evaluate the conditions for closing a period, without changing anything.
Each check has a status of pass, fail, warn or n/a: pending approvals, drafts,
depreciation, FX revaluation, deferred tax, receivables and payables control accounts,
bank reconciliation and balances, trial balance, ledger hash chain and earlier periods.
ready is true when no check fails (warnings do not block).
| Required scope | read (admin implies every scope) |
| Operation id | close_checklist_v1_periods__period_id__close_checklist_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | period_id |
string (uuid) | yes | Fiscal period id (GET /v1/entities/{entity}/periods). |
Example request
curl "$ERP_URL/v1/periods/3fa85f64-5717-4562-b3fc-2c963f66afa6/close-checklist" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | Checklist |
| 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",
"period": "string",
"period_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"period_status": "string",
"ready": true,
"checks": [
{
"id": "string",
"title": "string",
"status": "fail",
"detail": "string",
"code": "string",
"details": {}
}
]
}
POST /v1/periods/{period_id}/deferred-tax
Run deferred tax for a period
Post the change in deferred tax since the last run (after depreciation).
Measures the temporary differences of the book that carries deferred tax at the period
end and posts the movement against the deferred tax liability and asset accounts and the
deferred tax expense account, through the approval policy (submit; null when there is
nothing to post). items are the per-asset differences and total the movement.
Refused: 409 when deferred tax already ran for the period, when the period has not started
yet (period_not_started), or when depreciation is due but has not run; 422 when no
tax book of the entity carries deferred tax.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | run_deferred_tax_v1_periods__period_id__deferred_tax_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | period_id |
string (uuid) | yes | Fiscal period id (GET /v1/entities/{entity}/periods). |
Example request
curl -X POST "$ERP_URL/v1/periods/3fa85f64-5717-4562-b3fc-2c963f66afa6/deferred-tax" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | RunSummary |
| 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)
{
"run_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"kind": "string",
"period": "string",
"items": [
{}
],
"total": "string",
"submit": {
"entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "string",
"number": "string",
"policy": {
"decision": null,
"reason": null,
"rule_id": null,
"table_id": null
},
"approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"reason": "string"
}
}
POST /v1/periods/{period_id}/depreciation
Run depreciation for a period
Post the period's book depreciation for every active asset in service.
The asset's fiscal-year amount is spread over the regular periods of the year (evenly
per month, by days on a week-based calendar) and capped at what is left to depreciate.
One entry dated at the period end is posted (Dr depreciation expense / Cr accumulated
depreciation per asset) through the approval policy; submit is null when nothing is
due. Adjustment periods get no depreciation; an asset whose disposal waits for approval
is skipped (the disposal books its depreciation to its date). On the fiscal year's last
regular period a run that stands can be made again as a top-up that books only the
difference to the year's schedule (what is missing, or the excess taken back). Refused:
404 for an unknown period; 409 when depreciation already ran for the period (on the last
period: and nothing differs), the period is closed, or it has not started yet
(period_not_started: a period-end run is for the current period or an earlier one).
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | run_depreciation_v1_periods__period_id__depreciation_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | period_id |
string (uuid) | yes | Fiscal period id (GET /v1/entities/{entity}/periods). |
Example request
curl -X POST "$ERP_URL/v1/periods/3fa85f64-5717-4562-b3fc-2c963f66afa6/depreciation" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | RunSummary |
| 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)
{
"run_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"kind": "string",
"period": "string",
"items": [
{}
],
"total": "string",
"submit": {
"entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "string",
"number": "string",
"policy": {
"decision": null,
"reason": null,
"rule_id": null,
"table_id": null
},
"approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"reason": "string"
}
}
POST /v1/periods/{period_id}/fx-revaluation
Run FX revaluation for a period
Revalue open foreign-currency items at the closing ECB rate (auto-reversing).
Open receivables and payables in a foreign currency and foreign-currency bank accounts
are remeasured at the period-end rate. The unrealised difference is posted at the period
end (against the unrealised FX account) through the approval policy and reversed on the
first day of the next period once posted. items lists each remeasured item with its
result (a gain positive, a loss negative) and total is the result posted, in functional
currency.
It can run again after a posting dated in the period changed what it remeasured (a payment
of a revalued invoice or an invoice entered after the run; the close check
fx_revaluation then fails with fx_changed): the run again books only the change, at the
same closing rate, also auto-reversing; each item then also has booked (what the earlier
entries booked for it). Refused: 409 when the revaluation already ran and nothing changed
(fx_revaluation_done), while its entry waits for approval (fx_revaluation_pending) or
the period has not started yet (period_not_started); 422 for an adjustment period or when
the next fiscal year is not opened.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | run_fx_revaluation_v1_periods__period_id__fx_revaluation_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | period_id |
string (uuid) | yes | Fiscal period id (GET /v1/entities/{entity}/periods). |
Example request
curl -X POST "$ERP_URL/v1/periods/3fa85f64-5717-4562-b3fc-2c963f66afa6/fx-revaluation" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | RunSummary |
| 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)
{
"run_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"kind": "string",
"period": "string",
"items": [
{}
],
"total": "string",
"submit": {
"entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "string",
"number": "string",
"policy": {
"decision": null,
"reason": null,
"rule_id": null,
"table_id": null
},
"approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"reason": "string"
}
}
GET /v1/periods/{period_id}/receivable-allowance
Calculate the allowance for doubtful receivables
What the allowance should be at the period end, invoice by invoice: the open customer
invoices (not those of group companies), each with its bucket or doubtful percent, the base
it is taken on and the allowance; the totals per bucket; and adjustment, the difference to
what the allowance account holds, which the run posts. Once run, run_id is set and
booked is what the account held before the run. Posts nothing.
| Required scope | read (admin implies every scope) |
| Operation id | allowance_proposal_v1_periods__period_id__receivable_allowance_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | period_id |
string (uuid) | yes | Fiscal period id (GET /v1/entities/{entity}/periods). |
Example request
curl "$ERP_URL/v1/periods/3fa85f64-5717-4562-b3fc-2c963f66afa6/receivable-allowance" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | AllowanceProposal |
| 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",
"period_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"period": "string",
"as_of": "2026-09-21",
"currency": "string",
"include_vat": true,
"items": [
{
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"document_number": "string",
"party": "string",
"due_date": "2026-09-21",
"days_overdue": 1,
"bucket": "1-30",
"currency": "string",
"open": "string",
"base": "string",
"kind": "general",
"percent": "string",
"allowance": "string",
"note": "string"
}
],
"buckets": [
{
"bucket": "1-30",
"percent": "string",
"base": "string",
"allowance": "string"
}
],
"specific": "string",
"general": "string",
"target": "string",
"booked": "string",
"adjustment": "string",
"allowance_account": "string",
"expense_account": "string",
"run_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"run_status": "string"
}
POST /v1/periods/{period_id}/receivable-allowance
Run the allowance for doubtful receivables
Adjust the allowance account (role receivable_allowance) to the allowance the open
invoices call for at the period end: one entry dated the period end, Dr allowance expense /
Cr allowance to raise it or the reverse to release part of it, through the approval policy
(submit null when nothing differs). Not reversed: the next period's run adjusts it again.
Refused: 404 for an unknown period; 409 when the period's run stands (allowance_done; a
rejected one is made again), the period is closed or has not started
(period_not_started); 422 when the chart lacks the accounts (role_missing).
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | run_allowance_v1_periods__period_id__receivable_allowance_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | period_id |
string (uuid) | yes | Fiscal period id (GET /v1/entities/{entity}/periods). |
Example request
curl -X POST "$ERP_URL/v1/periods/3fa85f64-5717-4562-b3fc-2c963f66afa6/receivable-allowance" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | AllowanceRunResult |
| 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)
{
"run_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"period": "string",
"target": "string",
"booked": "string",
"adjustment": "string",
"submit": {
"entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "string",
"number": "string",
"policy": {
"decision": null,
"reason": null,
"rule_id": null,
"table_id": null
},
"approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"reason": "string"
}
}
POST /v1/periods/{period_id}/reopen
Reopen a period
Set a closed period back to open so entries can be posted to it again. Needs the
approve scope and a human: the approval policy's reopen_period action denies agents.
The reason is mandatory and recorded in the audit trail (period.reopen). Returns the
period; reopening an open period changes nothing but the audit trail.
| Required scope | approve (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | reopen_period_v1_periods__period_id__reopen_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | period_id |
string (uuid) | yes | Fiscal period id (GET /v1/entities/{entity}/periods). |
Request body
application/json (required): ReopenInput
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | yes | Why the period is reopened (audit trail). (min length 3; max length 1000) |
Example request
curl -X POST "$ERP_URL/v1/periods/3fa85f64-5717-4562-b3fc-2c963f66afa6/reopen" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "Late audit adjustment to the September accruals"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | PeriodOut |
| 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)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fiscal_year": 1,
"number": 1,
"kind": "string",
"start_date": "2026-09-21",
"end_date": "2026-09-21",
"status": "string",
"closed_at": "2026-09-21T09:30:00Z",
"closed_by": "string"
}
Schemas
AllowanceItem
One open invoice and the allowance it calls for.
| Field | Type | Required | Description |
|---|---|---|---|
document_id |
string (uuid) | yes | Id of the invoice. |
document_number |
string or null | yes | Number of the invoice. |
party |
string | yes | Name of the customer. |
due_date |
string (date) | yes | Due date of the invoice. |
days_overdue |
integer | yes | Days past the due date at the period end (0: not due). |
bucket |
"1-30" | "31-60" | "61-90" | "90+" | "current" |
yes | Its aging bucket at the period end. |
currency |
string | yes | Currency of the invoice. |
open |
string | yes | What is open on it at the period end, in its currency. |
base |
string | yes | What the allowance is taken on, in the functional currency: what the books carry for it, with or without VAT as the settings say. |
kind |
"general" | "specific" |
yes | specific: the invoice is marked doubtful; general: the bucket's percent. |
percent |
string | yes | The percent applied. |
allowance |
string | yes | The allowance it calls for, in the functional currency. |
note |
string or null | Why it is doubtful, for a specific one. |
AllowanceProposal
What the allowance should be at a period end, and the entry that brings the allowance account there.
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code of the company. |
period_id |
string (uuid) | yes | The period. |
period |
string | yes | The period as 2026-09. |
as_of |
string (date) | yes | The period end the receivables are taken at. |
currency |
string | yes | The functional currency of the amounts. |
include_vat |
boolean | yes | Whether the base includes VAT. |
items |
array of AllowanceItem |
yes | The open invoices, oldest due date first. |
buckets |
array of BucketTotal |
yes | The general allowance per aging bucket. |
specific |
string | yes | The specific allowance on the doubtful invoices. |
general |
string | yes | The general allowance on the others. |
target |
string | yes | The allowance needed: specific plus general. |
booked |
string | yes | What the allowance account holds at the period end (posted entries). |
adjustment |
string | yes | What the run posts: positive raises the allowance (an expense), negative releases part of it; 0 posts nothing. |
allowance_account |
string or null | yes | The allowance account (role receivable_allowance); none when the chart lacks it. |
expense_account |
string or null | yes | The account the change goes to (role allowance_expense, else bad_debt_expense). |
run_id |
string (uuid) or null | The period's run when it was made (then nothing more is run). | |
run_status |
string or null | Status of the run's entry: posted, pending_approval; none when not run or when it posted nothing. |
AllowanceRunResult
| Field | Type | Required | Description |
|---|---|---|---|
run_id |
string (uuid) | yes | The run. |
period |
string | yes | The period as 2026-09. |
target |
string | yes | The allowance needed at the period end. |
booked |
string | yes | What the allowance account held before the run. |
adjustment |
string | yes | What the run posted (0: nothing). |
submit |
SubmitResult or null |
yes | How the entry was submitted (the approval policy decides); none when nothing was posted. |
AllowanceSettingsInput
| Field | Type | Required | Description |
|---|---|---|---|
percents |
array of BucketPercent-Input |
yes | The general allowance in percent for each of the five aging buckets, each bucket once. (min items 5; max items 5) |
include_vat |
boolean | yes | Whether the percent is taken on the receivable with VAT (Swiss practice for the del credere) or on its amount without VAT (the VAT comes back when the loss is final, so German practice takes the net). |
AllowanceSettingsOut
| Field | Type | Required | Description |
|---|---|---|---|
percents |
array of BucketPercent-Output |
yes | The general allowance in percent for each of the five aging buckets, each bucket once. (min items 5; max items 5) |
include_vat |
boolean | yes | Whether the percent is taken on the receivable with VAT (Swiss practice for the del credere) or on its amount without VAT (the VAT comes back when the loss is final, so German practice takes the net). |
entity |
string | yes | Code of the company. |
default |
boolean | yes | True while the company has not set its own: these are the suggested ones (0 % for receivables not yet due, 5 % for overdue ones). |
BucketPercent-Input
| Field | Type | Required | Description |
|---|---|---|---|
bucket |
"1-30" | "31-60" | "61-90" | "90+" | "current" |
yes | The aging bucket: current (not yet due) or days past due (1-30, 31-60, 61-90, 90+). |
percent |
decimal string | yes | The share of these receivables the general allowance covers. (min 0; max 100) |
BucketPercent-Output
| Field | Type | Required | Description |
|---|---|---|---|
bucket |
"1-30" | "31-60" | "61-90" | "90+" | "current" |
yes | The aging bucket: current (not yet due) or days past due (1-30, 31-60, 61-90, 90+). |
percent |
string | yes | The share of these receivables the general allowance covers. |
BucketTotal
| Field | Type | Required | Description |
|---|---|---|---|
bucket |
"1-30" | "31-60" | "61-90" | "90+" | "current" |
yes | The aging bucket. |
percent |
string | yes | The general allowance's percent for it. |
base |
string | yes | The receivables of the bucket not marked doubtful. |
allowance |
string | yes | Their general allowance. |
DoubtfulInput
| Field | Type | Required | Description |
|---|---|---|---|
percent |
decimal string | yes | The share of what is open on the invoice the specific allowance covers (100: nothing is expected to come in). (max 100; greater than 0) |
note |
string or null | Why the invoice is doubtful (optional). (max length 500) |
DoubtfulOut
| Field | Type | Required | Description |
|---|---|---|---|
percent |
string | yes | The share of what is open on the invoice the specific allowance covers (100: nothing is expected to come in). |
note |
string or null | Why the invoice is doubtful (optional). (max length 500) | |
document_id |
string (uuid) | yes | Id of the invoice. |
document_number |
string or null | yes | Number of the invoice. |
marked_at |
string (date-time) | yes | When it was marked doubtful (UTC). |
ReopenInput
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | yes | Why the period is reopened (audit trail). (min length 3; max length 1000) |
RunSummary
| Field | Type | Required | Description |
|---|---|---|---|
run_id |
string (uuid) | yes | Id of the recorded run (one per kind and period). |
kind |
string | yes | depreciation, fx_revaluation or deferred_tax. |
period |
string | yes | The period as YYYY-NN, e.g. 2026-09. |
items |
array of object | yes | What was processed. Depreciation: asset, amount, dataset_version. FX revaluation: item (document number or bank account), currency, open, carrying, closing, difference (closing less carrying), result (what the entry books to profit and loss: a gain positive, a loss negative) and, on a run again, booked (what the earlier entries booked). Deferred tax: the per-asset differences of the book (see DifferenceLine). |
total |
string | yes | Depreciation: the amount posted. FX revaluation: the result posted (a gain positive, a loss negative). Deferred tax: the movement posted. In the entity's functional currency. |
submit |
SubmitResult or null |
yes | The posted entry with the approval policy decision (allow, require_approval, deny); null when there was nothing to post. |