Dunning
Customer statements: the open items of one customer on a day, tied to the receivables control account, as JSON, CSV or a PDF in the customer's language, archived with its hash when a person issues it. Vendor statements: the movements on a vendor's account between two days with the running balance. Reminder letters: which customers are due one, the letter in their language, archived, sent or recorded as handed over. No fee and no interest; nothing here posts.
| Operation | Summary | Scope | Idempotency-Key |
|---|---|---|---|
GET /v1/documents/{document_id}/dunning-block |
Get whether the reminders of an invoice are stopped | read |
|
POST /v1/documents/{document_id}/dunning-block |
Stop the reminders of an invoice | post |
required |
DELETE /v1/documents/{document_id}/dunning-block |
Resume the reminders of an invoice | post |
|
GET /v1/entities/{entity}/customers/{party}/activity |
Get a customer's activity statement: invoices, credit notes and payments | read |
|
POST /v1/entities/{entity}/customers/{party}/reminders |
Make a reminder letter to a customer | post |
required |
GET /v1/entities/{entity}/customers/{party}/statement |
Get a customer's statement of account | read |
|
POST /v1/entities/{entity}/customers/{party}/statement/issue |
Issue a customer's statement: archive its PDF | post |
required |
GET /v1/entities/{entity}/reminder-settings |
Get the fees and default interest of the reminder levels | read |
|
PUT /v1/entities/{entity}/reminder-settings |
Save the fees and default interest of the reminder levels | admin |
|
GET /v1/entities/{entity}/reminders |
List the reminder letters | read |
|
GET /v1/entities/{entity}/reminders/proposal |
List the customers due a reminder letter | read |
|
GET /v1/entities/{entity}/vendors/{party}/statement |
Get a vendor's statement: what we owe, by bill, credit note and payment | read |
|
GET /v1/reminders/{notice_id} |
Get a reminder letter | read |
|
POST /v1/reminders/{notice_id}/mark-sent |
Record that a reminder letter was handed over | post |
required |
GET /v1/reminders/{notice_id}/pdf |
Download a reminder letter | read |
|
POST /v1/reminders/{notice_id}/send |
Send a reminder letter by e-mail | post |
required |
POST /v1/reminders/{notice_id}/withdraw |
Withdraw a reminder letter not handed over | post |
required |
GET /v1/documents/{document_id}/dunning-block
Get whether the reminders of an invoice are stopped
The stop on the invoice's reminders, with who set it, when and why; block is null when
reminders go out as usual.
| Required scope | read (admin implies every scope) |
| Operation id | get_dunning_block_v1_documents__document_id__dunning_block_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | document_id |
string | yes | Document id (uuid) or number, e.g. INV-2026-000001. |
Example request
curl "$ERP_URL/v1/documents/INV-2026-000001/dunning-block" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | DunningBlockState |
| 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)
{
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"block": {
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"reason": "string",
"blocked_by": {},
"blocked_at": "2026-09-21T09:30:00Z"
}
}
POST /v1/documents/{document_id}/dunning-block
Stop the reminders of an invoice
Stop the reminders of a posted sales invoice, with why (a dispute, for instance): the
reminder proposal leaves it out and lists it apart with the reason, so no letter names it
and it is charged no fee and no interest while the stop holds. It stays open and payable.
A person does it (the policy action release_dunning; an agent key answers 403). 409
dunning_block_exists when its reminders are stopped already, 409
dunning_block_not_invoice for another document. Needs an Idempotency-Key.
| Required scope | post (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | block_reminders_v1_documents__document_id__dunning_block_post |
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): DunningBlockInput
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | yes | Why the reminders stop. (min length 1; max length 500) |
Example request
curl -X POST "$ERP_URL/v1/documents/INV-2026-000001/dunning-block" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"reason": "The customer disputes the delivered quantity"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | DunningBlockState |
| 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)
{
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"block": {
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"reason": "string",
"blocked_by": {},
"blocked_at": "2026-09-21T09:30:00Z"
}
}
DELETE /v1/documents/{document_id}/dunning-block
Resume the reminders of an invoice
Lift the stop: the invoice is due its next reminder again as if it had not been stopped
(interest runs from its due date or the last day a letter charged, as before). The stop and
who lifted it stay on record. A person does it; 409 dunning_block_missing when its
reminders are not stopped.
| Required scope | post (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | resume_reminders_v1_documents__document_id__dunning_block_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/dunning-block" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | DunningBlockState |
| 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)
{
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"block": {
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"reason": "string",
"blocked_by": {},
"blocked_at": "2026-09-21T09:30:00Z"
}
}
GET /v1/entities/{entity}/customers/{party}/activity
Get a customer's activity statement: invoices, credit notes and payments
What the customer owes us and how it came about: per currency, the opening balance on
start, every invoice, credit note and payment received up to end (with a payment's
reversal and a later application of an amount on account or a credit note) with the running
balance, and the closing balance, in the currency of each movement. open_items are the
items still open at the end of end, as on the open-items statement of that day
(GET .../statement?as_of=); closing_functional is the customer's balance on the
receivables control account in the functional currency, the statement's
reconciliation.control_balance.
It is read from the customer's own lines on the receivables control account, so the closing
balance is the one in the books. Read-only: it posts nothing, it is not archived and it is not
a request for payment. 404 for an unknown entity or customer (a vendor is not a customer: its
statement is GET .../vendors/{party}/statement), 422 when start is after end.
| Required scope | read (admin implies every scope) |
| Operation id | get_customer_activity_v1_entities__entity__customers__party__activity_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| path | party |
string | yes | Customer code, e.g. C-DE, or its id. A vendor is not a customer: its statement is GET .../vendors/{party}/statement. |
| query | start |
string (date) | yes | The first day of the period, ISO 8601. What the customer owed before it is the opening balance. |
| query | end |
string (date) | yes | The last day of the period, ISO 8601, not before start. |
| query | format |
"csv" | "json" | "pdf" |
json (default): the statement; csv: one row per movement with the opening and closing balance of each currency; pdf: an A4 PDF in the customer's language (language chooses another), rendered now and not stored. (default "json") |
|
| query | language |
"de" | "en" | "fr" | "it" | "sv" or null |
format=pdf only: de, fr, it, en or sv. Omitted: the customer's language, else the language of its country. |
|
| query | download |
boolean | csv and pdf: true sends the file as an attachment. (default false) |
Example request
curl "$ERP_URL/v1/entities/CH01/customers/C-DE/activity?start=2026-07-01&end=2026-09-30" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | JSON (the statement), CSV (its movements) or the PDF in the customer's language. | CustomerActivity, text/csv, application/pdf |
| 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",
"party_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"party_code": "string",
"party": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"language": "de",
"currencies": [
{
"currency": "string",
"opening": "string",
"increases": "string",
"decreases": "string",
"closing": "string",
"lines": [
null
]
}
],
"functional_currency": "string",
"closing_functional": "string",
"open_items": [
{
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"kind": "payment_on_account",
"document_date": "2026-09-21",
"due_date": "2026-09-21",
"currency": "string",
"amount": "string",
"open": "string",
"open_functional": "0.00",
"reserved": false,
"days_overdue": 1,
"bucket": "string"
}
]
}
POST /v1/entities/{entity}/customers/{party}/reminders
Make a reminder letter to a customer
Make the letter to the customer in currency (omitted: the one currency it is due a
reminder in, its own of several), dated today in the entity's country with today's amounts:
it names the customer's invoices in that currency that are due a reminder (all of them, or
documents), at the highest level they reached (each invoice's own when
they differ), and asks for their open amount within 10 days. The PDF is in the
customer's language (language chooses another) and is archived with its SHA-256 (the
document archive, kind dunning_notice); the letter never changes. Then send it
(POST /v1/reminders/{id}/send) or record how it was handed over
(POST /v1/reminders/{id}/mark-sent): the next level counts from that day.
The letter deducts what the customer has to its credit in the currency and not yet applied (credit notes with something open, receipts' amounts on account), as far as no letter still standing deducted it already: the letters together never ask for less than is owed.
A person makes it: the policy action release_dunning is denied to agents, and an agent key
answers 403 whatever the policy says. It claims no fee and no interest and posts nothing.
409 no_reminder_due when nothing is due, 409 reminder_covered_by_credits when the
customer's credits cover the invoices asked for, 422 reminder_not_due for an invoice asked
for that is not, 422 reminder_currency_needed when no currency is named and the customer
is due reminders in several, none its own, 422 reminder_in_future or reminder_backdated
for an as_of other than today. Needs an Idempotency-Key.
| Required scope | post (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | create_reminder_v1_entities__entity__customers__party__reminders_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| path | party |
string | yes | Customer code, e.g. C-DE, or its id. A vendor is not a customer: its statement is GET .../vendors/{party}/statement. |
Request body
application/json (required): ReminderCreateInput
| Field | Type | Required | Description |
|---|---|---|---|
as_of |
string (date) or null | The day of the letter: today in the entity's country, the only day a letter is dated (its amounts are today's). Omitted: today. | |
currency |
string or null | The currency of the letter. Omitted: the one the customer (or the invoices named) is due a reminder in; of several, the customer's own (422 reminder_currency_needed when it is not one of them). (min length 3; max length 3) |
|
documents |
array of string (uuid) or null | Only these invoices (each must be due a reminder); omitted: every invoice of the customer in the currency that is due one. (min items 1) | |
language |
"de" | "en" | "fr" | "it" | "sv" or null |
Language of the letter; omitted: the customer's language. |
Example request
curl -X POST "$ERP_URL/v1/entities/CH01/customers/C-DE/reminders" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | ReminderOut |
| 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",
"entity": "string",
"party_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"party_code": "string",
"party": "string",
"currency": "string",
"level": 1,
"letter_date": "2026-09-21",
"deadline_date": "2026-09-21",
"language": "de",
"total": "string",
"fee": "0.00",
"charge": {
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"status": "string",
"charged": true
},
"file": {
"file_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"filename": "string",
"sha256": "string",
"size": 1
},
"created_by": {},
"created_at": "2026-09-21T09:30:00Z",
"items": [
{
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"level": 1,
"due_date": "2026-09-21",
"days_overdue": 1,
"open_amount": "string",
"flat_fee": "0.00",
"interest": "0.00",
"interest_rate": "string",
"interest_from": "2026-09-21",
"interest_until": "2026-09-21",
"interest_parts": [
null
]
}
],
"dispatches": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"channel": "email",
"handed_over_on": "2026-09-21",
"recipients": [
"string"
],
"subject": "string",
"message_id": "string",
"sent_by": {},
"sent_at": "2026-09-21T09:30:00Z"
}
],
"state": "handed_over",
"withdrawal": {
"reason": "string",
"withdrawn_by": {},
"withdrawn_at": "2026-09-21T09:30:00Z"
},
"email": "string"
}
GET /v1/entities/{entity}/customers/{party}/statement
Get a customer's statement of account
The open items of one customer on a day: what the customer still owes us then.
It is the aging report restricted to the customer, from the same positions and rules:
allocations (payments, credit notes, a cash discount taken) count from the day they took
effect, so those dated after as_of do not; a part payment lowers the open amount of its
invoice; a credit note is a negative item; a payment waiting for approval reserves its amount
(reserved). items are oldest due date first, balances give the balance and the open
amount per age band for each currency, and totals_functional the same in the entity's
functional currency.
It ties to the ledger. reconciliation compares totals_functional.total with the
customer's own lines on the receivables control account up to as_of: they are equal, apart
from the period-end revaluation postings that sit on the control account (unrealised_fx,
named); unexplained is zero, and anything else would be a posting no document carries.
issued lists the statements of this day that were archived.
A statement is read-only: it posts nothing, and it is not a request for payment. 404 for an
unknown entity or customer (a vendor is not a customer: its statement is
GET .../vendors/{party}/statement). Interest accrued but not yet charged comes in a later
slice of ADR-0028.
| Required scope | read (admin implies every scope) |
| Operation id | get_statement_v1_entities__entity__customers__party__statement_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| path | party |
string | yes | Customer code, e.g. C-DE, or its id. A vendor is not a customer: its statement is GET .../vendors/{party}/statement. |
| query | as_of |
string (date) | yes | The day the statement is as of, ISO 8601, e.g. 2026-09-30. Allocations dated after it, and documents dated after it, are not counted. Required: a statement is evidence of a day, so the day is always named. |
| query | format |
"csv" | "json" | "pdf" |
json (default): the statement; csv: one row per open item; pdf: the statement as an A4 PDF in the customer's language (language chooses another). For pdf, the archived file is returned when the statement of this customer, day and language was issued (POST .../statement/issue; the header X-Statement-Archived: true says so), otherwise the PDF is rendered now and nothing is stored. (default "json") |
|
| query | language |
"de" | "en" | "fr" | "it" | "sv" or null |
format=pdf only: de, fr, it, en or sv. Omitted: the customer's language, else the language of its country. Another language is another file. |
|
| query | download |
boolean | csv and pdf: true sends the file as an attachment; false (default) inline. (default false) |
Example request
curl "$ERP_URL/v1/entities/CH01/customers/C-DE/statement?as_of=2026-09-30" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | JSON (the statement), CSV (its open items) or the PDF in the customer's language. | Statement, text/csv, application/pdf |
| 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)
{
"as_of": "2026-09-25",
"balances": [
{
"balance": "681.00",
"buckets": {
"1-30": "0.00",
"31-60": "0.00",
"61-90": "681.00",
"90+": "0.00",
"current": "0.00"
},
"currency": "CHF",
"documents": 1,
"overdue": "681.00",
"overdue_documents": 1
}
],
"entity": "CH01",
"functional_currency": "CHF",
"issued": [
{
"file_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"filename": "statement-C-CH-2026-09-25-de.pdf",
"issued_at": "2026-09-26T08:15:00Z",
"issued_by": {
"id": "user:alice",
"name": "Alice",
"type": "human"
},
"language": "de",
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"size": 4213
}
],
"items": [
{
"amount": "1081.00",
"bucket": "61-90",
"currency": "CHF",
"days_overdue": 86,
"document_date": "2026-06-01",
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"due_date": "2026-07-01",
"kind": "sales_invoice",
"number": "INV-2026-000001",
"open": "681.00",
"open_functional": "681.00",
"reserved": false
}
],
"language": "de",
"party": "Matter Treuhand AG",
"party_code": "C-CH",
"party_id": "0b7d1c3e-5a4e-4d0a-9f7a-2f6d1c1d8a11",
"reconciliation": {
"control_account": "1100",
"control_balance": "681.00",
"difference": "0.00",
"pending_settlements": "0.00",
"totals_functional": "681.00",
"unexplained": "0.00",
"unrealised_fx": "0.00"
},
"totals_functional": {
"1-30": "0.00",
"31-60": "0.00",
"61-90": "681.00",
"90+": "0.00",
"current": "0.00",
"total": "681.00"
}
}
POST /v1/entities/{entity}/customers/{party}/statement/issue
Issue a customer's statement: archive its PDF
Store the PDF of the customer's statement on as_of in the document archive, so that
what the customer was told on that day can be shown later.
The exact bytes are kept with their SHA-256 (document.kind customer_statement, the
customer, the day and the language in its metadata; the audit event is statement.issue).
There is one file per customer, day and language: a second issue returns the first file with
created false, and nothing is rewritten (the archive wins, as for invoices). The statement
is deterministic for a day: the same ledger gives the same bytes. Download the file with
GET .../statement?format=pdf and the same as_of and language.
Only a person issues, and it needs the post scope: an agent key answers 403 whatever its
scopes. 422 for an as_of after today (it would not be what the customer was told), 404 for
an unknown entity or customer. A statement posts nothing and is not a request for payment.
Needs an Idempotency-Key.
| Required scope | post (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | issue_statement_v1_entities__entity__customers__party__statement_issue_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| path | party |
string | yes | Customer code, e.g. C-DE, or its id. A vendor is not a customer: its statement is GET .../vendors/{party}/statement. |
Request body
application/json (required): StatementIssueInput
| Field | Type | Required | Description |
|---|---|---|---|
as_of |
string (date) | yes | The day the statement is as of, ISO 8601; not in the future. |
language |
"de" | "en" | "fr" | "it" | "sv" or null |
Language of the PDF; omitted: the customer's language. Another language is another archived file. |
Example request
curl -X POST "$ERP_URL/v1/entities/CH01/customers/C-DE/statement/issue" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"as_of": "2026-09-30"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | StatementIssued |
| 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)
{
"created": true,
"download": "/v1/entities/CH01/customers/C-CH/statement?as_of=2026-09-25&format=pdf&language=de",
"file": {
"file_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"filename": "statement-C-CH-2026-09-25-de.pdf",
"issued_at": "2026-09-26T08:15:00Z",
"issued_by": {
"id": "user:alice",
"name": "Alice",
"type": "human"
},
"language": "de",
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"size": 4213
}
}
GET /v1/entities/{entity}/reminder-settings
Get the fees and default interest of the reminder levels
What a reminder letter of each level charges: the fee per letter and the flat fee once per
invoice to a business customer (in the functional currency), and the yearly default-interest
rate for business customers and for consumers (actual/365, from the due date). Until settings
are saved (configured false) the country's defaults apply: Switzerland and Liechtenstein
5 % a year and no fee; Germany the base rate plus 9 points (business) or 5 points (consumers),
shown at today's base rate and charged at each day's (it changes on 1 January and 1 July), and
EUR 40 once per invoice to a business customer; elsewhere nothing. A German rate saved at
its default keeps following the base rate; a rate changed is fixed.
| Required scope | read (admin implies every scope) |
| Operation id | get_reminder_settings_v1_entities__entity__reminder_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/reminder-settings" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | ReminderSettingsOut |
| 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",
"currency": "string",
"configured": true,
"levels": [
{
"level": 1,
"fee": "string",
"flat_fee": "string",
"interest_rate": "string",
"consumer_interest_rate": "string"
}
],
"defaults": [
{
"level": 1,
"fee": "string",
"flat_fee": "string",
"interest_rate": "string",
"consumer_interest_rate": "string"
}
]
}
PUT /v1/entities/{entity}/reminder-settings
Save the fees and default interest of the reminder levels
Replace what each of the three levels charges (each level once; 422
reminder_settings_levels otherwise). Letters already made keep what they charged; the
change is in the audit trail with the values before and after.
| Required scope | admin (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | put_reminder_settings_v1_entities__entity__reminder_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): ReminderSettingsInput
| Field | Type | Required | Description |
|---|---|---|---|
levels |
array of ReminderLevelFees-Input |
yes | The three levels, each once. (min items 3; max items 3) |
Example request
curl -X PUT "$ERP_URL/v1/entities/CH01/reminder-settings" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"levels": [
{
"consumer_interest_rate": "5",
"fee": "0",
"flat_fee": "0",
"interest_rate": "5",
"level": 1
},
{
"consumer_interest_rate": "5",
"fee": "20",
"flat_fee": "0",
"interest_rate": "5",
"level": 2
},
{
"consumer_interest_rate": "5",
"fee": "30",
"flat_fee": "0",
"interest_rate": "5",
"level": 3
}
]
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | ReminderSettingsOut |
| 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",
"currency": "string",
"configured": true,
"levels": [
{
"level": 1,
"fee": "string",
"flat_fee": "string",
"interest_rate": "string",
"consumer_interest_rate": "string"
}
],
"defaults": [
{
"level": 1,
"fee": "string",
"flat_fee": "string",
"interest_rate": "string",
"consumer_interest_rate": "string"
}
]
}
GET /v1/entities/{entity}/reminders
List the reminder letters
The letters made in the entity, oldest first, with the invoices each named, each
hand-over and its state (unsent, out_of_date, handed_over, withdrawn).
| Required scope | read (admin implies every scope) |
| Operation id | list_reminders_v1_entities__entity__reminders_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| query | party |
string or null | Only this customer's (code or id). | |
| query | document |
string (uuid) or null | Only the letters naming this invoice (its id). |
Example request
curl "$ERP_URL/v1/entities/CH01/reminders" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of ReminderOut |
| 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)
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"party_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"party_code": "string",
"party": "string",
"currency": "string",
"level": 1,
"letter_date": "2026-09-21",
"deadline_date": "2026-09-21",
"language": "de",
"total": "string",
"fee": "0.00",
"charge": {
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": null,
"status": "string",
"charged": true
},
"file": {
"file_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"filename": "string",
"sha256": "string",
"size": 1
},
"created_by": {},
"created_at": "2026-09-21T09:30:00Z",
"items": [
{
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"level": 1,
"due_date": "2026-09-21",
"days_overdue": 1,
"open_amount": "string",
"flat_fee": "0.00",
"interest": "0.00",
"interest_rate": null,
"interest_from": null,
"interest_until": null,
"interest_parts": [
null
]
}
],
"dispatches": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"channel": "email",
"handed_over_on": "2026-09-21",
"recipients": [
null
],
"subject": null,
"message_id": null,
"sent_by": {},
"sent_at": "2026-09-21T09:30:00Z"
}
],
"state": "handed_over",
"withdrawal": {
"reason": "string",
"withdrawn_by": {},
"withdrawn_at": "2026-09-21T09:30:00Z"
},
"email": "string"
}
]
GET /v1/entities/{entity}/reminders/proposal
List the customers due a reminder letter
Which customers are due a reminder letter today (in the entity's country; as_of says
which day that is), one row per customer and currency, with the invoices it would name and
its level: 1 payment reminder, 2 second reminder, 3 final reminder (levels says when each
is due: a Swiss entity reminds 10 days after the due date, then 20 days after the reminder
was handed over, then 10; a German one 7, 14 and 10). An invoice is due its next level when
it is open today and long enough past its due date, or since the last letter that named it
was handed over. There is no other day: the clock runs on real time and the amounts are
today's. unsent lists the letters made and not handed over yet: they start no clock, so
the next level waits for them. out_of_date lists those that can no longer be sent (an
amount on them changed, or they are more than 7 days old): withdraw them.
Read-only; no fee and no interest is claimed. A customer that is another entity of the organization is not reminded.
| Required scope | read (admin implies every scope) |
| Operation id | get_reminder_proposal_v1_entities__entity__reminders_proposal_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/reminders/proposal" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | ReminderProposal |
| 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",
"levels": [
{
"level": 1,
"days_after": 1,
"deadline_days": 1
}
],
"due": [
{
"party_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"party_code": "string",
"party": "string",
"currency": "string",
"level": 1,
"language": "de",
"total": "string",
"fee": "0.00",
"email": "string",
"invoices": [
null
],
"credits": [
null
]
}
],
"unsent": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"party_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"party_code": "string",
"party": "string",
"currency": "string",
"level": 1,
"letter_date": "2026-09-21",
"deadline_date": "2026-09-21",
"language": "de",
"total": "string",
"fee": "0.00",
"charge": null,
"file": {
"file_id": null,
"filename": null,
"sha256": null,
"size": null
},
"created_by": {},
"created_at": "2026-09-21T09:30:00Z",
"items": [
null
],
"dispatches": [
null
],
"state": "handed_over",
"withdrawal": null,
"email": "string"
}
],
"out_of_date": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"party_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"party_code": "string",
"party": "string",
"currency": "string",
"level": 1,
"letter_date": "2026-09-21",
"deadline_date": "2026-09-21",
"language": "de",
"total": "string",
"fee": "0.00",
"charge": null,
"file": {
"file_id": null,
"filename": null,
"sha256": null,
"size": null
},
"created_by": {},
"created_at": "2026-09-21T09:30:00Z",
"items": [
null
],
"dispatches": [
null
],
"state": "handed_over",
"withdrawal": null,
"email": "string"
}
],
"blocked": [
{
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"party_code": "string",
"party": "string",
"currency": "string",
"due_date": "2026-09-21",
"days_overdue": 1,
"open": "string",
"reason": "string",
"blocked_by": {},
"blocked_at": "2026-09-21T09:30:00Z"
}
],
"sending_configured": true
}
GET /v1/entities/{entity}/vendors/{party}/statement
Get a vendor's statement: what we owe, by bill, credit note and payment
What we owe the vendor and how it came about: per currency, the opening balance on
start, every bill, credit note and payment up to end with the running balance, and the
closing balance, in the currency of each movement (a vendor billing in euros has a block in
euros). open_items are the bills and credit notes still open at the end of end, as in the
payables aging; closing_functional is the vendor's balance on the payables control account
in the functional currency.
It is read from the vendor's own lines on the payables control account, so the closing
balance is the one in the books; compare it with the closing balance of the statement the
vendor sent. their_reference is the vendor's own number of each bill. Read-only: it posts
nothing. 404 for an unknown entity or vendor (a customer is not a vendor), 422 when start
is after end.
| Required scope | read (admin implies every scope) |
| Operation id | get_vendor_statement_v1_entities__entity__vendors__party__statement_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| path | party |
string | yes | Vendor code, e.g. V-BM, or its id. A customer is not a vendor. |
| query | start |
string (date) | yes | The first day of the period, ISO 8601. What we owed before it is the opening balance. |
| query | end |
string (date) | yes | The last day of the period, ISO 8601, not before start. |
| query | format |
"csv" | "json" | "pdf" |
json (default): the statement; csv: one row per movement with the opening and closing balance of each currency; pdf: an A4 PDF in the vendor's language (language chooses another), rendered now and not stored. (default "json") |
|
| query | language |
"de" | "en" | "fr" | "it" | "sv" or null |
format=pdf only: de, fr, it, en or sv. |
|
| query | download |
boolean | csv and pdf: true sends the file as an attachment. (default false) |
Example request
curl "$ERP_URL/v1/entities/CH01/vendors/V-BM/statement?start=2026-07-01&end=2026-09-30" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | JSON (the statement), CSV (its movements) or the PDF in the vendor's language. | VendorStatement, text/csv, application/pdf |
| 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",
"party_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"party_code": "string",
"party": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"language": "de",
"currencies": [
{
"currency": "string",
"opening": "string",
"increases": "string",
"decreases": "string",
"closing": "string",
"lines": [
null
]
}
],
"functional_currency": "string",
"closing_functional": "string",
"open_items": [
{
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"kind": "payment_on_account",
"document_date": "2026-09-21",
"due_date": "2026-09-21",
"currency": "string",
"amount": "string",
"open": "string",
"open_functional": "0.00",
"reserved": false,
"days_overdue": 1,
"bucket": "string"
}
]
}
GET /v1/reminders/{notice_id}
Get a reminder letter
The letter, the invoices it names, its file, its hand-overs and its state. 404 when unknown.
| Required scope | read (admin implies every scope) |
| Operation id | get_reminder_v1_reminders__notice_id__get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | notice_id |
string (uuid) | yes | Reminder letter id (GET /v1/entities/{entity}/reminders). |
Example request
curl "$ERP_URL/v1/reminders/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | ReminderOut |
| 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)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"party_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"party_code": "string",
"party": "string",
"currency": "string",
"level": 1,
"letter_date": "2026-09-21",
"deadline_date": "2026-09-21",
"language": "de",
"total": "string",
"fee": "0.00",
"charge": {
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"status": "string",
"charged": true
},
"file": {
"file_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"filename": "string",
"sha256": "string",
"size": 1
},
"created_by": {},
"created_at": "2026-09-21T09:30:00Z",
"items": [
{
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"level": 1,
"due_date": "2026-09-21",
"days_overdue": 1,
"open_amount": "string",
"flat_fee": "0.00",
"interest": "0.00",
"interest_rate": "string",
"interest_from": "2026-09-21",
"interest_until": "2026-09-21",
"interest_parts": [
null
]
}
],
"dispatches": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"channel": "email",
"handed_over_on": "2026-09-21",
"recipients": [
"string"
],
"subject": "string",
"message_id": "string",
"sent_by": {},
"sent_at": "2026-09-21T09:30:00Z"
}
],
"state": "handed_over",
"withdrawal": {
"reason": "string",
"withdrawn_by": {},
"withdrawn_at": "2026-09-21T09:30:00Z"
},
"email": "string"
}
POST /v1/reminders/{notice_id}/mark-sent
Record that a reminder letter was handed over
Record that a person handed the letter over outside this system: by their own e-mail, by
post, on the customer's portal. Nothing is sent. The first hand-over starts the clock of the
next level. Only a person records (the policy action send_document; an agent key answers
403). 409 reminder_withdrawn for a withdrawn letter; 422 for a day before the letter
(handover_before_document), after today (handover_in_future) or more than 7 days ago
(handover_too_old): the clock of the next level runs on real days. Needs an
Idempotency-Key.
| Required scope | post (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | mark_reminder_sent_v1_reminders__notice_id__mark_sent_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | notice_id |
string (uuid) | yes | Reminder letter id (GET /v1/entities/{entity}/reminders). |
Request body
application/json (required): ReminderMarkSentInput
| Field | Type | Required | Description |
|---|---|---|---|
channel |
"email" | "other" | "portal" | "post" |
yes | email (from a person's own mailbox), post, portal or other. |
recipient |
string | yes | Who got it. (min length 1; max length 200) |
handed_over_on |
string (date) | yes | The day it was handed over: not before the letter, not after today, and within the last 7 days. |
Example request
curl -X POST "$ERP_URL/v1/reminders/3fa85f64-5717-4562-b3fc-2c963f66afa6/mark-sent" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"channel": "post",
"handed_over_on": "2026-10-10",
"recipient": "Berlin GmbH"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | ReminderOut |
| 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",
"entity": "string",
"party_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"party_code": "string",
"party": "string",
"currency": "string",
"level": 1,
"letter_date": "2026-09-21",
"deadline_date": "2026-09-21",
"language": "de",
"total": "string",
"fee": "0.00",
"charge": {
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"status": "string",
"charged": true
},
"file": {
"file_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"filename": "string",
"sha256": "string",
"size": 1
},
"created_by": {},
"created_at": "2026-09-21T09:30:00Z",
"items": [
{
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"level": 1,
"due_date": "2026-09-21",
"days_overdue": 1,
"open_amount": "string",
"flat_fee": "0.00",
"interest": "0.00",
"interest_rate": "string",
"interest_from": "2026-09-21",
"interest_until": "2026-09-21",
"interest_parts": [
null
]
}
],
"dispatches": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"channel": "email",
"handed_over_on": "2026-09-21",
"recipients": [
"string"
],
"subject": "string",
"message_id": "string",
"sent_by": {},
"sent_at": "2026-09-21T09:30:00Z"
}
],
"state": "handed_over",
"withdrawal": {
"reason": "string",
"withdrawn_by": {},
"withdrawn_at": "2026-09-21T09:30:00Z"
},
"email": "string"
}
GET /v1/reminders/{notice_id}/pdf
Download a reminder letter
The archived PDF of the letter, as it was made (checked against its SHA-256, which the
header X-Content-SHA256 carries).
| Required scope | read (admin implies every scope) |
| Operation id | get_reminder_pdf_v1_reminders__notice_id__pdf_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | notice_id |
string (uuid) | yes | Reminder letter id (GET /v1/entities/{entity}/reminders). |
| query | download |
boolean | true sends the file as an attachment; false inline. (default false) |
Example request
curl "$ERP_URL/v1/reminders/3fa85f64-5717-4562-b3fc-2c963f66afa6/pdf" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | The PDF. | application/pdf |
| 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 |
POST /v1/reminders/{notice_id}/send
Send a reminder letter by e-mail
E-mail the archived letter to the customer, as an invoice is sent: to to (only the
customer's own addresses) or its billing address, within the organization's hourly limit.
The hand-over is recorded with the message id; the next level counts from today.
Only a person sends (the policy action send_document; an agent key answers 403). 409 in a
test copy (it never e-mails customers, who may be real ones), reminder_withdrawn for a
withdrawn letter, reminder_out_of_date when an amount on it changed since its day or it is
more than 7 days old (withdraw it and make a new one), and when this server has no mail
server (download the letter instead); 422 for an address that is not the customer's;
429 beyond the hourly limit; 502 when the mail server refuses, and nothing is recorded. Needs
an Idempotency-Key.
| Required scope | post (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | send_reminder_v1_reminders__notice_id__send_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | notice_id |
string (uuid) | yes | Reminder letter id (GET /v1/entities/{entity}/reminders). |
Request body
application/json (required): ReminderSendInput
| Field | Type | Required | Description |
|---|---|---|---|
to |
array of string or null | Addresses of the customer (as for an invoice); omitted: its billing address. (min items 1; max items 5) | |
message |
string or null | A text placed before the standard text. (max length 1000) |
Example request
curl -X POST "$ERP_URL/v1/reminders/3fa85f64-5717-4562-b3fc-2c963f66afa6/send" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | ReminderOut |
| 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 |
| 429 | A per-organization limit was reached, e.g. messages sent per hour (rate_limited, with retry_after_seconds). |
ErrorEnvelope |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"party_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"party_code": "string",
"party": "string",
"currency": "string",
"level": 1,
"letter_date": "2026-09-21",
"deadline_date": "2026-09-21",
"language": "de",
"total": "string",
"fee": "0.00",
"charge": {
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"status": "string",
"charged": true
},
"file": {
"file_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"filename": "string",
"sha256": "string",
"size": 1
},
"created_by": {},
"created_at": "2026-09-21T09:30:00Z",
"items": [
{
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"level": 1,
"due_date": "2026-09-21",
"days_overdue": 1,
"open_amount": "string",
"flat_fee": "0.00",
"interest": "0.00",
"interest_rate": "string",
"interest_from": "2026-09-21",
"interest_until": "2026-09-21",
"interest_parts": [
null
]
}
],
"dispatches": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"channel": "email",
"handed_over_on": "2026-09-21",
"recipients": [
"string"
],
"subject": "string",
"message_id": "string",
"sent_by": {},
"sent_at": "2026-09-21T09:30:00Z"
}
],
"state": "handed_over",
"withdrawal": {
"reason": "string",
"withdrawn_by": {},
"withdrawn_at": "2026-09-21T09:30:00Z"
},
"email": "string"
}
POST /v1/reminders/{notice_id}/withdraw
Withdraw a reminder letter not handed over
Withdraw a letter that was not handed over (out of date, or made by mistake), with why.
The withdrawal is written once: the letter is never sent or recorded after it, and the
invoices it named are due their next letter again as if it had not been made. Only a person
withdraws (the policy action release_dunning; an agent key answers 403). 409
reminder_handed_over once it was handed over, reminder_withdrawn when it was withdrawn
already. Needs an Idempotency-Key.
| Required scope | post (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | withdraw_reminder_v1_reminders__notice_id__withdraw_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | notice_id |
string (uuid) | yes | Reminder letter id (GET /v1/entities/{entity}/reminders). |
Request body
application/json (required): ReminderWithdrawInput
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | yes | Why it is withdrawn. (min length 1; max length 500) |
Example request
curl -X POST "$ERP_URL/v1/reminders/3fa85f64-5717-4562-b3fc-2c963f66afa6/withdraw" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"reason": "The customer paid part of it meanwhile"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | ReminderOut |
| 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",
"entity": "string",
"party_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"party_code": "string",
"party": "string",
"currency": "string",
"level": 1,
"letter_date": "2026-09-21",
"deadline_date": "2026-09-21",
"language": "de",
"total": "string",
"fee": "0.00",
"charge": {
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"status": "string",
"charged": true
},
"file": {
"file_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"filename": "string",
"sha256": "string",
"size": 1
},
"created_by": {},
"created_at": "2026-09-21T09:30:00Z",
"items": [
{
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"level": 1,
"due_date": "2026-09-21",
"days_overdue": 1,
"open_amount": "string",
"flat_fee": "0.00",
"interest": "0.00",
"interest_rate": "string",
"interest_from": "2026-09-21",
"interest_until": "2026-09-21",
"interest_parts": [
null
]
}
],
"dispatches": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"channel": "email",
"handed_over_on": "2026-09-21",
"recipients": [
"string"
],
"subject": "string",
"message_id": "string",
"sent_by": {},
"sent_at": "2026-09-21T09:30:00Z"
}
],
"state": "handed_over",
"withdrawal": {
"reason": "string",
"withdrawn_by": {},
"withdrawn_at": "2026-09-21T09:30:00Z"
},
"email": "string"
}
Schemas
ActivityCurrency
The movements of one currency, from the opening to the closing balance.
| Field | Type | Required | Description |
|---|---|---|---|
currency |
string | yes | ISO 4217 currency of the amounts. |
opening |
string | yes | What we owed at the start of the first day. |
increases |
string | yes | The sum of the positive movements. |
decreases |
string | yes | The sum of the negative movements (a negative amount). |
closing |
string | yes | What we owed at the end of the last day: opening + increases + decreases. Compare it with the closing balance of the statement the vendor sent. |
lines |
array of ActivityLine |
yes | The movements, oldest first. |
ActivityLine
One movement on the vendor's account: a bill, a credit note, a payment.
| Field | Type | Required | Description |
|---|---|---|---|
day |
string (date) | yes | The day it was booked. |
kind |
"application" | "credit_note" | "invoice" | "other" | "payment" | "payment_reversal" | "write_off" |
yes | invoice (the vendor's bill), credit_note, payment (what we paid, or a refund received), payment_reversal, application (an amount on account or a credit note applied to a bill later, ADR-0045: one line off the payment or credit note, one on the bill), write_off (a customer invoice written off as uncollectable), or other (an opening balance or a manual entry). |
reference |
string | yes | Our number: the bill's, the payment's or the entry's. |
their_reference |
string or null | yes | The vendor's own number of the bill or credit note, to find it on the vendor's statement. |
document_id |
string (uuid) or null | yes | The bill or credit note it concerns. |
document_number |
string or null | yes | Our number of that bill or credit note (for a payment: the one it settles). |
entry_id |
string (uuid) | yes | The journal entry that booked it. |
amount |
string | yes | In the currency of the block: positive when it raises what we owe (a bill), negative when it lowers it (a payment, a credit note). |
balance |
string | yes | What we owe after it: the running balance. |
CustomerActivity
A customer's activity statement: what the customer owes us, by invoice, credit note and payment, between two days.
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Entity code. |
party_id |
string (uuid) | yes | Id of the customer. |
party_code |
string | yes | Code of the customer. |
party |
string | yes | Name of the customer. |
start |
string (date) | yes | The first day of the period. |
end |
string (date) | yes | The last day of the period. |
language |
"de" | "en" | "fr" | "it" | "sv" |
yes | The language of its PDF: the customer's, else the one spoken in its country. |
currencies |
array of CustomerActivityCurrency |
yes | One block per currency the customer has movements or a balance in, by code: the currency of each invoice, credit note and payment. |
functional_currency |
string | yes | The entity's functional currency. |
closing_functional |
string | yes | What the customer owes at the end of end in the functional currency: its lines on the receivables control account, revaluations included (the control_balance of the open-items statement of that day). |
open_items |
array of StatementItem |
yes | The invoices, credit notes and amounts on account still open at the end of end, oldest due date first (credit notes negative): the open-items statement of that day. |
CustomerActivityCurrency
The movements of one currency, from the opening to the closing balance.
| Field | Type | Required | Description |
|---|---|---|---|
currency |
string | yes | ISO 4217 currency of the amounts. |
opening |
string | yes | What the customer owed at the start of the first day. |
increases |
string | yes | The sum of the positive movements. |
decreases |
string | yes | The sum of the negative movements (a negative amount). |
closing |
string | yes | What the customer owed at the end of the last day: opening + increases + decreases. A negative amount is a credit of the customer. |
lines |
array of CustomerActivityLine |
yes | The movements, oldest first. |
CustomerActivityLine
One movement on the customer's account: an invoice, a credit note, a payment received.
| Field | Type | Required | Description |
|---|---|---|---|
day |
string (date) | yes | The day it was booked. |
kind |
"application" | "credit_note" | "invoice" | "other" | "payment" | "payment_reversal" | "write_off" |
yes | invoice, credit_note, payment (a payment received, or a refund paid out), payment_reversal, application (an amount on account or a credit note applied to an invoice later, ADR-0045: one line off the payment or credit note, one on the invoice), or other (an opening balance or a manual entry). |
reference |
string | yes | Our number: the invoice's, the credit note's, the payment's or the entry's. |
document_id |
string (uuid) or null | yes | The invoice or credit note it concerns. |
document_number |
string or null | yes | The number of that invoice or credit note (for a payment: the one it pays). |
entry_id |
string (uuid) | yes | The journal entry that booked it. |
amount |
string | yes | In the currency of the block: positive when it raises what the customer owes (an invoice), negative when it lowers it (a payment, a credit note). |
balance |
string | yes | What the customer owes after it: the running balance. |
DunningBlockInput
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | yes | Why the reminders stop. (min length 1; max length 500) |
DunningBlockOut
The reminders of an invoice are stopped (for instance while the customer disputes it).
| Field | Type | Required | Description |
|---|---|---|---|
document_id |
string (uuid) | yes | Id of the invoice. |
number |
string | yes | Invoice number. |
reason |
string | yes | Why the reminders stop. |
blocked_by |
object | yes | Who stopped them: type, id and name. |
blocked_at |
string (date-time) | yes | When (UTC). |
DunningBlockState
Whether the reminders of an invoice are stopped.
| Field | Type | Required | Description |
|---|---|---|---|
document_id |
string (uuid) | yes | Id of the invoice. |
block |
DunningBlockOut or null |
yes | The stop while it holds; null when reminders go out as usual. |
DunningLevelOut
One level of reminder and when it is due.
| Field | Type | Required | Description |
|---|---|---|---|
level |
integer | yes | 1 payment reminder, 2 second reminder, 3 final reminder. |
days_after |
integer | yes | Level 1: days after the due date; later levels: days after the previous letter was handed over. |
deadline_days |
integer | yes | Days the letter gives to pay, from its day. |
InterestPart
Part of an invoice's default interest that one rate covers, when the rate changed in the span: a German company's default rates follow the base rate, which changes on 1 January and 1 July, and the days on each side of the change are charged at their own rate.
| Field | Type | Required | Description |
|---|---|---|---|
interest_rate |
string | yes | The yearly rate in percent of these days. |
interest_from |
string (date) | yes | The part runs from the day after this one. |
interest_until |
string (date) | yes | The last day of the part. |
interest |
string | yes | The interest of these days, in the invoice's currency. |
ReminderBlocked
An open invoice whose reminders a person stopped: no letter names it while the stop holds.
| Field | Type | Required | Description |
|---|---|---|---|
document_id |
string (uuid) | yes | Id of the invoice. |
number |
string | yes | Invoice number. |
party_code |
string | yes | Code of the customer. |
party |
string | yes | Name of the customer. |
currency |
string | yes | ISO 4217 currency. |
due_date |
string (date) | yes | Due date. |
days_overdue |
integer | yes | Days past the due date today (0 or less: not due). |
open |
string | yes | What is open today. |
reason |
string | yes | Why the reminders are stopped. |
blocked_by |
object | yes | Who stopped them: type, id and name. |
blocked_at |
string (date-time) | yes | When (UTC). |
ReminderChargeOut
The sales invoice that books a letter's fees and interest, made at its first hand-over.
| Field | Type | Required | Description |
|---|---|---|---|
document_id |
string (uuid) | yes | Id of the invoice. |
number |
string or null | yes | Its number; null while it waits for approval. |
status |
string | yes | posted, pending_approval, rejected (by the approver, or withdrawn from approval), draft (a rejected invoice reopened) or void (reopened and voided). |
charged |
boolean | yes | False once the invoice is rejected or void: the letter's charges were not booked, and a later letter charges the same interest days and flat fees again. |
ReminderCreateInput
| Field | Type | Required | Description |
|---|---|---|---|
as_of |
string (date) or null | The day of the letter: today in the entity's country, the only day a letter is dated (its amounts are today's). Omitted: today. | |
currency |
string or null | The currency of the letter. Omitted: the one the customer (or the invoices named) is due a reminder in; of several, the customer's own (422 reminder_currency_needed when it is not one of them). (min length 3; max length 3) |
|
documents |
array of string (uuid) or null | Only these invoices (each must be due a reminder); omitted: every invoice of the customer in the currency that is due one. (min items 1) | |
language |
"de" | "en" | "fr" | "it" | "sv" or null |
Language of the letter; omitted: the customer's language. |
ReminderCredit
What the customer has to its credit in the letter's currency, deducted from what the letter asks for: a credit note with something open, or a receipt's amount on account (ADR-0045). Each credit is deducted once: what a letter still standing (not withdrawn, naming an invoice still open that no later letter names) deducted is not deducted again.
| Field | Type | Required | Description |
|---|---|---|---|
kind |
"payment_on_account" | "sales_credit_note" |
yes | sales_credit_note, or payment_on_account: what is left on account of a receipt, not yet applied to an invoice. |
document_id |
string (uuid) | yes | Id of the credit note, or of the receipt. |
number |
string | yes | Number of the credit note or the receipt. |
date |
string (date) | yes | Date of the credit note or the receipt. |
open |
string | yes | What the letter deducts of it (positive): what is left of it today, less what letters still standing deducted already. An application waiting for approval reserves its amount. |
ReminderDispatchOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | Id of the hand-over. |
channel |
"email" | "other" | "portal" | "post" | "system_email" |
yes | system_email (this server sent it) or what a person recorded: email, post, portal, other. |
handed_over_on |
string (date) | yes | The day it was handed over. |
recipients |
array of string | yes | To whom. |
subject |
string or null | yes | Subject of the e-mail; null for a recorded one. |
message_id |
string or null | yes | Message-ID of the e-mail; null otherwise. |
sent_by |
object | yes | Who: type, id and name. |
sent_at |
string (date-time) | yes | When it was sent or recorded (UTC). |
ReminderDue
A customer that is due a reminder letter in one currency.
| Field | Type | Required | Description |
|---|---|---|---|
party_id |
string (uuid) | yes | Id of the customer. |
party_code |
string | yes | Code of the customer. |
party |
string | yes | Name of the customer. |
currency |
string | yes | The currency of the letter. |
level |
integer | yes | The letter's level: the highest next level of its invoices. |
language |
"de" | "en" | "fr" | "it" | "sv" |
yes | The customer's language: that of the letter. |
total |
string | yes | What the letter would ask for: the invoices' open amounts less credits, plus fee and the flat fees and interest of the invoices. A customer whose credits cover its overdue invoices is due no letter. |
fee |
string | The fee of the letter's level, in its currency (converted from the functional currency at today's rate); 0 for none. (default "0.00") |
|
email |
string or null | yes | Where an e-mail would go by default: the customer's billing address. |
invoices |
array of ReminderInvoice |
yes | The invoices due a reminder. |
credits |
array of ReminderCredit |
The customer's credit notes with something open and receipts' amounts on account in the currency, as far as no letter still standing deducted them, which the letter deducts. |
ReminderFile
The archived PDF of a letter.
| Field | Type | Required | Description |
|---|---|---|---|
file_id |
string (uuid) | yes | Id of the archived file (document.id). |
filename |
string | yes | File name, e.g. reminder-C-DE-2026-10-10-1.pdf. |
sha256 |
string | yes | SHA-256 of the archived bytes. |
size |
integer | yes | Size in bytes. |
ReminderInvoice
An overdue invoice and where its reminders stand.
| Field | Type | Required | Description |
|---|---|---|---|
document_id |
string (uuid) | yes | Id of the invoice. |
number |
string | yes | Invoice number. |
document_date |
string (date) | yes | Invoice date. |
due_date |
string (date) | yes | Due date. |
days_overdue |
integer | yes | Days past the due date today. |
currency |
string | yes | ISO 4217 currency. |
open |
string | yes | What is open today, in the invoice's currency. |
last_level |
integer | yes | The level of the last letter that named it; 0 for none. |
last_sent_on |
string (date) or null | yes | The day that letter was first handed over; null when none was, or never. |
next_level |
integer | yes | The level the next letter gives it. |
flat_fee |
string | The flat fee the letter charges once for this invoice (a business customer; Germany: EUR 40, § 288 para. 5 BGB), in its currency; 0 for none. (default "0.00") |
|
interest |
string | Default interest the letter charges on this invoice, in its currency: actual/365 on what was open each day from interest_from (exclusive) to interest_until; 0 for none. (default "0.00") |
|
interest_rate |
string or null | The yearly rate in percent (when it changed in the span, that of the letter's day: see interest_parts); null when no interest is charged. |
|
interest_from |
string (date) or null | The interest runs from the day after this one: the due date, or the last day an earlier letter charged interest for (no day is charged twice). | |
interest_until |
string (date) or null | The last day the interest covers: the letter's day. | |
interest_parts |
array of InterestPart |
When the rate changed in the span (the German base rate on 1 January or 1 July), the parts at each rate, in order, adding up to interest; empty when one rate covers it all. |
ReminderItemOut
| Field | Type | Required | Description |
|---|---|---|---|
document_id |
string (uuid) | yes | Id of the invoice. |
number |
string | yes | Invoice number. |
level |
integer | yes | The level the invoice reached with this letter. |
due_date |
string (date) | yes | Its due date. |
days_overdue |
integer | yes | Days past the due date on the letter's day. |
open_amount |
string | yes | What was open on the letter's day. |
flat_fee |
string | The flat fee charged for it. (default "0.00") |
|
interest |
string | The default interest charged on it. (default "0.00") |
|
interest_rate |
string or null | Yearly rate in percent. | |
interest_from |
string (date) or null | The interest runs from the day after this one. | |
interest_until |
string (date) or null | The last day the interest covers. | |
interest_parts |
array of InterestPart |
When the rate changed in the span (the German base rate on 1 January or 1 July), the parts at each rate, in order, adding up to interest; empty when one rate covers it all. |
ReminderLevelFees-Input
What a reminder of one level charges.
| Field | Type | Required | Description |
|---|---|---|---|
level |
integer | yes | 1 payment reminder, 2 second, 3 final reminder. (min 1; max 3) |
fee |
decimal string | yes | Fee per letter of this level, in the company's functional currency (a letter in another currency converts it at the letter's day); 0 for none. (min 0) |
flat_fee |
decimal string | yes | Flat fee charged once per invoice to a business customer, in the functional currency (Germany: EUR 40 on the first reminder, § 288 para. 5 BGB); 0 for none. When the charge of the letter that set it is rejected or voided, the next letter charges it. (min 0) |
interest_rate |
decimal string | yes | Default interest for business customers, percent a year (actual/365), from the due date; 0 for none. (min 0; max 100) |
consumer_interest_rate |
decimal string | yes | Default interest for consumers (customers that are not a business), percent a year; 0 for none. (min 0; max 100) |
ReminderLevelFees-Output
What a reminder of one level charges.
| Field | Type | Required | Description |
|---|---|---|---|
level |
integer | yes | 1 payment reminder, 2 second, 3 final reminder. (min 1; max 3) |
fee |
string | yes | Fee per letter of this level, in the company's functional currency (a letter in another currency converts it at the letter's day); 0 for none. |
flat_fee |
string | yes | Flat fee charged once per invoice to a business customer, in the functional currency (Germany: EUR 40 on the first reminder, § 288 para. 5 BGB); 0 for none. When the charge of the letter that set it is rejected or voided, the next letter charges it. |
interest_rate |
string | yes | Default interest for business customers, percent a year (actual/365), from the due date; 0 for none. |
consumer_interest_rate |
string | yes | Default interest for consumers (customers that are not a business), percent a year; 0 for none. |
ReminderMarkSentInput
| Field | Type | Required | Description |
|---|---|---|---|
channel |
"email" | "other" | "portal" | "post" |
yes | email (from a person's own mailbox), post, portal or other. |
recipient |
string | yes | Who got it. (min length 1; max length 200) |
handed_over_on |
string (date) | yes | The day it was handed over: not before the letter, not after today, and within the last 7 days. |
ReminderOut
A reminder letter: what it asked for, its file and its hand-overs.
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | Id of the letter. |
entity |
string | yes | Entity code. |
party_id |
string (uuid) | yes | Id of the customer. |
party_code |
string | yes | Code of the customer. |
party |
string | yes | Name of the customer. |
currency |
string | yes | ISO 4217 currency. |
level |
integer | yes | 1 payment reminder, 2 second reminder, 3 final reminder. |
letter_date |
string (date) | yes | The day of the letter. |
deadline_date |
string (date) | yes | The day by which it asks for payment. |
language |
"de" | "en" | "fr" | "it" | "sv" |
yes | The language of the letter. |
total |
string | yes | What it asks for: the open amounts of its invoices less the customer's credits it deducts, plus its fee and the flat fees and interest of its invoices, on the letter's day. |
fee |
string | The fee of its level; 0 for none. (default "0.00") |
|
charge |
ReminderChargeOut or null |
The sales invoice of its fees and interest (outside VAT), made when it was first handed over; null before, or when it charges nothing. | |
file |
ReminderFile |
yes | The archived PDF. |
created_by |
object | yes | Who made it. |
created_at |
string (date-time) | yes | When. |
items |
array of ReminderItemOut |
yes | The invoices it names. |
dispatches |
array of ReminderDispatchOut |
yes | Each hand-over, oldest first. Empty: made but not sent, which starts no clock for the next level. |
state |
"handed_over" | "out_of_date" | "unsent" | "withdrawn" |
yes | unsent (made, not handed over, still right: send or record it), out_of_date (not handed over, and an amount on it changed since or it is more than 7 days old: it cannot be sent; withdraw it and make a new one), handed_over or withdrawn. |
withdrawal |
ReminderWithdrawalOut or null |
Who withdrew it, when and why; null when nobody did. | |
email |
string or null | yes | Where an e-mail would go by default: the customer's billing address. |
ReminderProposal
Which customers are due a reminder letter today, and the letters not yet sent.
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Entity code. |
as_of |
string (date) | yes | Today, in the entity's country: the day of the letters. |
levels |
array of DunningLevelOut |
yes | The levels and when each is due. |
due |
array of ReminderDue |
yes | One row per customer and currency that is due a letter, by customer code. |
unsent |
array of ReminderOut |
yes | Letters made, not handed over yet and still right: send or record them, or the next level never comes. |
out_of_date |
array of ReminderOut |
Letters not handed over that are out of date (an amount on them changed, or they are more than 7 days old): they cannot be sent and hold back the next letter until they are withdrawn. | |
blocked |
array of ReminderBlocked |
Open invoices whose reminders a person stopped (a dispute): left out of every letter, with why. | |
sending_configured |
boolean | yes | Whether this server sends e-mail (false in a test copy, which never does). |
ReminderSendInput
| Field | Type | Required | Description |
|---|---|---|---|
to |
array of string or null | Addresses of the customer (as for an invoice); omitted: its billing address. (min items 1; max items 5) | |
message |
string or null | A text placed before the standard text. (max length 1000) |
ReminderSettingsInput
| Field | Type | Required | Description |
|---|---|---|---|
levels |
array of ReminderLevelFees-Input |
yes | The three levels, each once. (min items 3; max items 3) |
ReminderSettingsOut
The fees and default interest of each reminder level of a company.
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Entity code. |
currency |
string | yes | The functional currency the fees are in. |
configured |
boolean | yes | Whether a person saved settings; false: the country's defaults apply. |
levels |
array of ReminderLevelFees-Output |
yes | The three levels, in order. |
defaults |
array of ReminderLevelFees-Output |
yes | The country's defaults: Switzerland and Liechtenstein 5 % a year and no fee; Germany the base rate plus 9 points (business) or 5 points (consumers), shown at today's base rate and charged at each day's (split where it changes, on 1 January or 1 July), and EUR 40 once per invoice to a business customer; elsewhere nothing. |
ReminderWithdrawInput
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | yes | Why it is withdrawn. (min length 1; max length 500) |
ReminderWithdrawalOut
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | yes | Why it was withdrawn. |
withdrawn_by |
object | yes | Who: type, id and name. |
withdrawn_at |
string (date-time) | yes | When (UTC). |
Statement
A customer's statement of account: the open items on a day.
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Entity code. |
party_id |
string (uuid) | yes | Id of the customer. |
party_code |
string | yes | Code of the customer. |
party |
string | yes | Name of the customer. |
as_of |
string (date) | yes | The day the statement is as of. |
language |
"de" | "en" | "fr" | "it" | "sv" |
yes | The language of its PDF: the customer's, else the one spoken in its country. |
items |
array of StatementItem |
yes | Open items, oldest due date first; credit notes are negative items. |
balances |
array of StatementBalance |
yes | The balance and its age structure per currency, by currency code. |
functional_currency |
string | yes | The entity's functional currency. |
totals_functional |
map of string | yes | The open amount per age band and total in the functional currency: the figures reconciliation ties to the ledger. |
reconciliation |
StatementReconciliation |
yes | The total against the customer's lines on the control account. |
issued |
array of StatementFile |
yes | Statements of this customer and day that were archived (POST .../issue), oldest first, one per language. |
StatementBalance
The open items of one currency: the balance and what is overdue, by age.
| Field | Type | Required | Description |
|---|---|---|---|
currency |
string | yes | ISO 4217 currency. |
balance |
string | yes | The open amount in this currency; credit notes reduce it. |
overdue |
string | yes | The part of the balance that is past its due date (every band but current). |
documents |
integer | yes | How many items are listed in this currency. |
overdue_documents |
integer | yes | How many of them are past their due date (bucket is not current). |
buckets |
map of string | yes | The open amount per age band, every band present: current, 1-30, 31-60, 61-90, 90+. They add up to balance. |
StatementFile
A statement kept in the document archive.
| Field | Type | Required | Description |
|---|---|---|---|
file_id |
string (uuid) | yes | Id of the archived file (document.id). |
filename |
string | yes | File name, e.g. statement-C-CH-2026-09-30-de.pdf. |
language |
"de" | "en" | "fr" | "it" | "sv" |
yes | Language the PDF was issued in. |
sha256 |
string | yes | SHA-256 of the archived bytes. |
size |
integer | yes | Size in bytes. |
issued_by |
object | yes | Who issued it: type, id and name. |
issued_at |
string (date-time) | yes | When it was archived. |
StatementIssueInput
| Field | Type | Required | Description |
|---|---|---|---|
as_of |
string (date) | yes | The day the statement is as of, ISO 8601; not in the future. |
language |
"de" | "en" | "fr" | "it" | "sv" or null |
Language of the PDF; omitted: the customer's language. Another language is another archived file. |
StatementIssued
The result of issuing a statement.
| Field | Type | Required | Description |
|---|---|---|---|
file |
StatementFile |
yes | The archived PDF. |
created |
boolean | yes | True when this call archived the file; false when the statement of this customer, day and language was archived before and that file is returned (the archive wins: what the customer was told is not rewritten). |
download |
string | yes | The path that returns the PDF (GET .../statement?format=pdf), which serves the archived file for this day and language. |
StatementItem
One open item: an invoice or bill, or a credit note or a payment's amount on account as a negative item.
| Field | Type | Required | Description |
|---|---|---|---|
document_id |
string (uuid) | yes | Id of the invoice, bill or credit note; of the receipt or payment for an amount on account. |
number |
string | yes | Document number, e.g. INV-2026-000012. |
kind |
"payment_on_account" | "purchase_bill" | "purchase_credit_note" | "sales_credit_note" | "sales_invoice" |
yes | sales_invoice or sales_credit_note on a customer's statement, purchase_bill or purchase_credit_note on a vendor's; payment_on_account on either: what is left on account of a receipt or payment, not yet applied (never overdue). |
document_date |
string (date) | yes | Document date. |
due_date |
string (date) | yes | Due date. |
currency |
string | yes | ISO 4217 currency of the amounts. |
amount |
string | yes | Gross total, negative for a credit note. |
open |
string | yes | What is open on as_of, negative for a credit note: the total less the allocations dated on or before as_of (payments, credit notes, a cash discount taken). |
open_functional |
string | The carrying amount the ledger holds for the item in the entity's functional currency on as_of, negative for a credit note. (default "0.00") |
|
reserved |
boolean | True when payments waiting for approval reserve the whole open amount: open is zero, the ledger still carries the item. (default false) |
|
days_overdue |
integer | yes | Days past the due date on as_of; 0 when not yet due. |
bucket |
string | yes | current, 1-30, 31-60, 61-90 or 90+ days overdue. |
StatementReconciliation
The statement's total against the ledger: the customer's lines on the receivables control account up to the day.
| Field | Type | Required | Description |
|---|---|---|---|
control_account |
string | yes | Code of the receivables control account. |
control_balance |
string | yes | The balance of this customer's lines on the control account in the ledger as of as_of, in the functional currency (what the customer owes us). |
totals_functional |
string | yes | The statement's total in the functional currency: the sum of the items' open_functional, the carrying amounts the ledger holds. |
difference |
string | yes | control_balance less totals_functional. |
unrealised_fx |
string | yes | Entries of source fx (period-end revaluations) on the customer's control lines up to as_of that no reversal has cancelled yet: they sit on the control account but on no document. |
pending_settlements |
string | yes | A memo: the functional amount of payments and applications waiting for approval on the listed items (an application on both of its items). They reserve the open amount in the document currency but are not booked, so they are in neither control_balance nor totals_functional. |
unexplained |
string | yes | difference less unrealised_fx: zero when the statement ties to the ledger; anything else is a posting on the control account that no document carries. |
VendorStatement
A vendor's statement: what we owe the vendor, by bill, credit note and payment.
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Entity code. |
party_id |
string (uuid) | yes | Id of the vendor. |
party_code |
string | yes | Code of the vendor. |
party |
string | yes | Name of the vendor. |
start |
string (date) | yes | The first day of the period. |
end |
string (date) | yes | The last day of the period. |
language |
"de" | "en" | "fr" | "it" | "sv" |
yes | The language of its PDF: the vendor's, else the one spoken in its country. |
currencies |
array of ActivityCurrency |
yes | One block per currency the vendor has movements or a balance in, by code; the vendor's own currency is among them when it has any. |
functional_currency |
string | yes | The entity's functional currency. |
closing_functional |
string | yes | What we owe the vendor at the end of end in the functional currency: its lines on the payables control account, revaluations included. |
open_items |
array of StatementItem |
yes | The bills and credit notes still open at the end of end, oldest due date first (credit notes negative): the same open items as the payables aging. |