Payments
Receipts and payments with allocations, payment proposals and payment runs.
| Operation | Summary | Scope | Idempotency-Key |
|---|---|---|---|
POST /v1/applications |
Apply an amount on account or a credit note to open documents | draft |
required |
GET /v1/entities/{entity}/open-credits |
List amounts on account and open credit notes | read |
|
POST /v1/entities/{entity}/payment-proposals |
Suggest vendor payments | read |
|
POST /v1/entities/{entity}/payment-runs |
Create the payments of a proposal | draft |
required |
POST /v1/payments |
Record a payment or receipt | draft |
required |
GET /v1/payments/{payment_id} |
Get a payment | read |
|
POST /v1/payments/{payment_id}/refund |
Pay back an amount on account | draft |
required |
POST /v1/payments/{payment_id}/reverse |
Reverse a payment or receipt | draft |
required |
POST /v1/applications
Apply an amount on account or a credit note to open documents
Apply what is left on account of a posted payment or receipt (payment), or the open
amount of a posted credit note (credit_note), to open posted invoices or bills of the same
customer or vendor, on application_date; a document in another currency is settled by the
document_amount its allocation names in that currency (ADR-0054). One allocation per document
and one entry that moves the amount on the control account from the payment or credit note to
the documents; each side is relieved at the rate it is carried at (the payment's, the credit
note's, the document's historical rate) and the difference is booked as realised FX. The entry
goes through the approval policy (submit.policy); until it is posted the amounts are reserved
on both sides. Refuses with 422 when the amounts are more than is left to apply or open on a
document, a document cannot be settled by it (other party, kind or status), or the
date is before the payment, the credit note or a document; with 404 for an unknown payment,
credit note or document; with 409 when the period is closed, or a bill is blocked for payment
(bill_payment_blocked: its purchase order variance waits for a second person's approval, as
for a payment). All or nothing.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | create_application_v1_applications_post |
Request body
application/json (required): ApplicationInput
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code (or id) of the entity, e.g. CH01. |
payment |
string or null | Id or number of a posted payment or receipt with an amount on account to apply. Give this or credit_note. |
|
credit_note |
string or null | Id or number of a posted credit note with an open amount to apply. Give this or payment. |
|
application_date |
string (date) | yes | The day the application takes effect (ISO 8601): on or after the payment or credit note and every document it settles, in an open period. The aging and the statement count it from this day. |
allocations |
array of ApplyTo |
yes | The invoices or bills it settles, each an open posted document of the same customer or vendor, with the amount applied (and, for a document in another currency, what it settles there). Together at most what is left on account (or open on the credit note). (min items 1) |
Example request
curl -X POST "$ERP_URL/v1/applications" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"allocations": [
{
"amount": "250.00",
"document": "INV-2026-000012"
}
],
"application_date": "2026-10-10",
"entity": "CH01",
"payment": "RCPT-2026-000004"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | ApplicationResult |
| 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 |
| 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)
{
"source": "credit_note",
"source_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"allocations": [
"3fa85f64-5717-4562-b3fc-2c963f66afa6"
],
"status": "string",
"fx_difference": "string",
"left": "string",
"submit": {
"entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "string",
"number": "string",
"policy": {
"decision": "allow",
"reason": "string",
"rule_id": "string",
"table_id": "string"
},
"approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"reason": "string"
}
}
GET /v1/entities/{entity}/open-credits
List amounts on account and open credit notes
What can still be applied to open invoices or bills with POST /v1/applications: posted
payments and receipts with an amount on account left, and posted credit notes with an open
amount, oldest first, with what is left of each (applications waiting for approval reserve
theirs). 404 for an unknown customer or vendor.
| Required scope | read (admin implies every scope) |
| Operation id | open_credits_v1_entities__entity__open_credits_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| query | party |
string or null | Code (or id) of one customer or vendor; default: all of them. | |
| query | kind |
"customer" | "vendor" or null |
customer or vendor; default: both. |
Example request
curl "$ERP_URL/v1/entities/CH01/open-credits" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of OpenCredit |
| 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)
[
{
"source": "credit_note",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"kind": "string",
"party_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"party_code": "string",
"party": "string",
"date": "2026-09-21",
"currency": "string",
"amount": "string",
"open": "string"
}
]
POST /v1/entities/{entity}/payment-proposals
Suggest vendor payments
Suggest vendor payments: open bills due by due_until, or whose cash discount
would expire by then, with the amount after the discount. Nothing is stored; confirm
the reviewed lines with POST /v1/entities/{entity}/payment-runs. Each line says why it is
proposed (overdue, due or cash_discount) and carries warnings such as a missing IBAN
or a draft vendor. bank_balances gives, per currency, the bank account the run would pay
from and its book balance on the payment date. 422 when cash discounts are taken but the
entity has no cash discount account.
| Required scope | read (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | propose_payments_v1_entities__entity__payment_proposals_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
Request body
application/json (required): ProposalCriteria
| Field | Type | Required | Description |
|---|---|---|---|
payment_date |
string (date) | yes | The day the payments would be booked; cash discounts are offered while it is within their period. A preview may look ahead; the run itself is dated today at the latest, and the bank pays on the execution date chosen when the payment file is sent. |
due_until |
string (date) or null | Last payment date: bills due on or before it are proposed (default the payment date). | |
take_discounts |
boolean | Deduct cash discounts still available on the payment date, and also propose bills not yet due whose discount would expire by due_until. (default true) |
|
vendors |
array of string or null | Vendor codes (default all). | |
currency |
string or null | ISO 4217 code: propose only bills in this currency; default: all. (pattern ^[A-Z]{3}$) |
|
bank_account |
string or null | GL bank account code the run will pay from, as the run's bank_account; default: per currency. Only bank_balances depends on it. |
Example request
curl -X POST "$ERP_URL/v1/entities/CH01/payment-proposals" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"due_until": "2026-10-15",
"payment_date": "2026-09-30",
"take_discounts": true,
"vendors": [
"V-BM",
"V-IT"
]
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | PaymentProposal |
| 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",
"payment_date": "2026-09-21",
"due_until": "2026-09-21",
"take_discounts": true,
"lines": [
{
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"external_number": "string",
"vendor": "string",
"vendor_name": "string",
"currency": "string",
"document_date": "2026-09-21",
"due_date": "2026-09-21",
"cash_discount_percent": "string",
"cash_discount_date": "2026-09-21",
"total": "string",
"open": "string",
"discount": "string",
"amount": "string",
"reason": "cash_discount",
"payment_iban": "string",
"payment_reference": "string",
"warnings": [
"string"
],
"problems": [
null
],
"credit_available": "0.00",
"preselected": true
}
],
"totals_by_currency": {},
"skipped": [
{
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"vendor": "string",
"vendor_name": "string",
"currency": "string",
"open": "string",
"code": "string",
"reason": "string"
}
],
"bank_balances": [
{
"currency": "string",
"account": "string",
"account_currency": "string",
"balance": "string"
}
]
}
POST /v1/entities/{entity}/payment-runs
Create the payments of a proposal
Create the payments of a reviewed proposal, all or none: one per vendor (a bill
with a structured reference on its own), each through the approval policy: a payment
is posted or pending_approval, counted in the result. Refuses with 422 when a line is
not a bill of the entity, a bill is listed twice, or an amount or cash discount is not
allowed, in which case no payment is created. A run that takes a bank account below zero in
the books on the payment date is made, with one warnings line per account and its
warning_reasons by code. Approved payments are released with
POST /v1/payment-exports.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | run_payments_v1_entities__entity__payment_runs_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
Request body
application/json (required): PaymentRunInput
| Field | Type | Required | Description |
|---|---|---|---|
payment_date |
string (date) | yes | Day the payments are booked (ISO 8601, e.g. 2026-09-30), today at the latest. The bank pays on the execution date of the payment file, which may be later but not after the cash discount period of a bill whose discount the payment takes. |
bank_account |
string or null | GL bank account code; default: per currency, as a payment's default bank account, refused when only a main account in another currency is left. | |
lines |
array of RunLine |
yes | The reviewed proposal lines to pay, 1 to 1000 vendor bills with the amount and cash discount of each. (min items 1; max items 1000) |
Example request
curl -X POST "$ERP_URL/v1/entities/CH01/payment-runs" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"bank_account": "1020",
"lines": [
{
"amount": "1176.00",
"discount": "24.00",
"document": "BILL-2026-000001"
}
],
"payment_date": "2026-09-30"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | PaymentRunResult |
| 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",
"payment_date": "2026-09-21",
"payments": [
{
"payment_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"vendor": "string",
"vendor_name": "string",
"currency": "string",
"amount": "string",
"discount": "string",
"documents": [
"string"
],
"status": "string",
"approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
],
"posted": 1,
"pending_approval": 1,
"warnings": [
"string"
],
"warning_reasons": [
{
"words": "string",
"code": "string",
"details": {},
"text": "string"
}
]
}
POST /v1/payments
Record a payment or receipt
Record a payment (to a vendor) or a receipt (from a customer) and allocate it to open
documents; realised FX is posted. An outgoing payment awaits the bank's execution on the
payments-in-transit account until its bank statement line is matched, unless it is recorded
as already executed by the bank (ADR-0060); a receipt goes to the bank. The allocations
and on_account must add up to amount; each allocation settles one posted document of the
party in the payment's currency. on_account keeps the rest on the party's account (an
overpayment, an advance, or a payment of nothing yet): an open item of the party in the
aging, the statement and its balances, booked at the payment's rate, applied later with
POST /v1/applications. The payment's entry goes through the approval policy: the result's
submit.policy is allow (posted and numbered) or require_approval (status
pending_approval); deny answers 403. Refuses with 422 when the amounts do not add up, a
document cannot be settled by this payment (wrong party, kind, status or currency), an
allocation exceeds the open amount, a cash discount is not allowed or a receipt is dated
after today (receipt_in_future, an outgoing one payment_in_future) or before a document
it settles (payment_before_document), and with 404 for an unknown document, party or
account. All or nothing: a refused payment leaves nothing behind. A payment that takes its
bank account below zero in the books on its date is recorded, with a warnings line and its
warning_reasons by code (the money may be there and not booked yet).
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | create_payment_v1_payments_post |
Request body
application/json (required): PaymentInput
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code (or id) of the entity paying or receiving, e.g. CH01. |
direction |
"incoming" | "outgoing" |
yes | incoming: a receipt from a customer; outgoing: a payment to a vendor. |
party |
string | yes | Customer or vendor code (or id), e.g. C-DE or V-BM. |
payment_date |
string (date) or null | Value date of the payment (ISO 8601); default today where the company is. The exchange rate of this day gives the realised FX difference. The fiscal period must be open. A receipt (incoming) is not dated after today (receipt_in_future), nor is a payment made (payment_in_future: a payment file asks the bank for a later execution date); neither is dated before a document it settles (payment_before_document). |
|
amount |
decimal string | yes | Total amount paid, a decimal string such as 1500.00; it must equal the sum of the allocation amounts and on_account (cash discounts are on top of it). (greater than 0) |
currency |
string or null | ISO 4217 code of the payment; default: the party's currency. A document in another currency is settled too: its allocation names what it settles in the document's currency (document_amount). (pattern ^[A-Z]{3}$) |
|
bank_account |
string or null | GL bank account code; default: the main bank account (role 'bank') when it holds the payment's currency, else the first active bank account in that currency, else the main one. A receipt collected by a payment provider (Shopify Payments) names the provider's balance (role 'provider_balance'), never a default. | |
reference |
string or null | Free text reference, e.g. the bill number or a bank reference. | |
allocations |
array of AllocationInput-Input |
The documents the payment settles: each allocation names a posted document of the party and the amount applied. May be empty when the whole amount is on_account. |
|
on_account |
decimal string | The part of the amount allocated to no document, e.g. an overpayment or an advance: it stays on the party's account (an open item of the party in the aging and the statement) until it is applied to an invoice or bill with POST /v1/applications. Default 0: the allocations take the whole amount. (min 0; default "0") |
|
bank_line_id |
string (uuid) or null | Id of the bank statement line the payment records, when it is entered from a bank statement; such a payment counts as already executed and is not put into a payment file. | |
party_kind |
"customer" | "vendor" or null |
Defaults to customer for incoming, vendor for outgoing; set it for refunds. | |
executed |
boolean | Outgoing payments only: true when the bank already executed the payment (paid in e-banking, by card or in cash): it is booked on the bank account at once and goes into no payment file. Default false: the payment awaits execution by the bank (a payment file, or a transfer still to be made) and is booked on the payments-in-transit account (role payments_in_transit) until the bank statement line that shows it is matched, which moves it to the bank account (ADR-0060). A payment recorded from a bank line (bank_line_id) and a receipt always go to the bank account. (default false) |
Example request
curl -X POST "$ERP_URL/v1/payments" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"allocations": [
{
"amount": "1200.00",
"document": "BILL-2026-000001"
}
],
"amount": "1200.00",
"bank_account": "1020",
"direction": "outgoing",
"entity": "CH01",
"party": "V-BM",
"payment_date": "2026-09-30",
"reference": "BM-4411"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | PaymentResult |
| 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 |
| 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)
{
"payment_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "string",
"fx_difference": "string",
"discount": "0.00",
"in_transit": false,
"submit": {
"entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "string",
"number": "string",
"policy": {
"decision": "allow",
"reason": "string",
"rule_id": "string",
"table_id": "string"
},
"approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"reason": "string"
},
"warnings": [
"string"
],
"warning_reasons": [
{
"words": "string",
"code": "string",
"details": {},
"text": "string"
}
]
}
GET /v1/payments/{payment_id}
Get a payment
One payment or receipt by id: its direction, party, amount, currency, bank account,
status and journal entry. 404 when it does not exist. The release status and problems
blocking a payment file are in GET /v1/payments.
| Required scope | read (admin implies every scope) |
| Operation id | get_payment_v1_payments__payment_id__get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | payment_id |
string (uuid) | yes | Payment id, as returned by POST /v1/payments or GET /v1/payments. |
Example request
curl "$ERP_URL/v1/payments/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | PaymentOut |
| 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",
"direction": "string",
"party_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"payment_date": "2026-09-21",
"currency": "string",
"amount": "string",
"bank_account_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"reference": "string",
"status": "string",
"journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"reversal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
POST /v1/payments/{payment_id}/refund
Pay back an amount on account
Pay back part or all of what a posted payment keeps on account: a customer's overpayment
paid back to the customer, or an advance paid to a vendor that the vendor pays back. The
refund is a payment the other way (outgoing to the customer, incoming from the vendor)
whose entry relieves the payment's line on account at the share of what it booked, the
difference to the bank at the refund's rate being realised FX, so the amount on account shrinks
in the aging, the statement and the balances by what was paid back. It goes through the
approval policy (submit.policy); waiting, it reserves its amount. It is reversed like any
payment (POST /v1/payments/{payment_id}/reverse), and the payment it pays back is not
reversed while it stands (payment_on_account_refunded).
Refused with 422: more than is left on account (more_than_left_to_apply), dated before the
payment or its latest application or refund (application_before_document,
application_before_settlement), a refund received dated after today (receipt_in_future),
a payment with nothing on account (nothing_to_apply) or one that is itself a refund kept on
account, which is applied to the credit note it pays instead (refund_not_on_account); 409
when a reversal of the payment waits or the period is closed; 404 for an unknown payment.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | refund_on_account_v1_payments__payment_id__refund_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | payment_id |
string (uuid) | yes | Payment id, as returned by POST /v1/payments or GET /v1/payments. |
Request body
application/json (required): OnAccountRefundInput
| Field | Type | Required | Description |
|---|---|---|---|
payment_date |
string (date) | yes | Value date of the refund (ISO 8601), on or after the payment it pays back and its latest application, in an open period. A refund received (from a vendor) is not dated after today (receipt_in_future), nor is one paid (payment_in_future). |
amount |
decimal string | yes | Amount paid back, in the payment's currency: at most what is left on account (more_than_left_to_apply). (greater than 0) |
bank_account |
string or null | GL bank account code; default: the main bank account for the currency. | |
reference |
string or null | Free text reference. | |
executed |
boolean | A refund paid out only: true when the bank already executed it; it is booked on the bank account at once and goes into no payment file. Default false: it awaits execution on the payments-in-transit account until its bank line is matched (ADR-0060). (default false) |
Example request
curl -X POST "$ERP_URL/v1/payments/3fa85f64-5717-4562-b3fc-2c963f66afa6/refund" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"amount": "185.78",
"payment_date": "2026-10-11",
"reference": "Refund"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | PaymentResult |
| 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)
{
"payment_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "string",
"fx_difference": "string",
"discount": "0.00",
"in_transit": false,
"submit": {
"entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "string",
"number": "string",
"policy": {
"decision": "allow",
"reason": "string",
"rule_id": "string",
"table_id": "string"
},
"approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"reason": "string"
},
"warnings": [
"string"
],
"warning_reasons": [
{
"words": "string",
"code": "string",
"details": {},
"text": "string"
}
]
}
POST /v1/payments/{payment_id}/reverse
Reverse a payment or receipt
Reverse a posted payment or receipt: an entry of its own, dated on (default today, or the
payment date of a payment scheduled later; not before the payment, in an open period),
undoes the payment's entry line by line (bank, receivables or payables at the documents'
historical rates, cash discount with its VAT correction, realised FX difference), so the
books are as before it to the cent. The reason
is required (at most 500 characters) and kept on the entry and in the audit trail.
The reversal goes through the approval policy as the payment did (post, with its source and
amount). status reversed: it is posted, the payment and its allocations are reversed and
the invoices, bills or credit notes it settled are open again by what it settled. status
posted: the reversal waits for a second person (reversal.approval_id, decided with
POST /v1/approvals/{approval_id}/approve, four eyes) and the payment stands until then. A
reversal asked by an agent, and one of a payment released in a payment file (released_in:
make sure the bank did not execute it), always waits for a person.
Refused with 409: the payment is not posted (one waiting for approval is rejected instead),
already reversed or its reversal already waits; it is matched to a bank statement line (undo
the match on that line first, POST /v1/bank-lines/{line_id}/unmatch, which reverses the
payment the match recorded); the period of the date is closed. 422: no period covers the date,
the date is before the payment's, or the reason is missing. 404: unknown payment.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | reverse_payment_v1_payments__payment_id__reverse_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | payment_id |
string (uuid) | yes | Payment id, as returned by POST /v1/payments or GET /v1/payments. |
Request body
application/json (required): PaymentReverseRequest
| Field | Type | Required | Description |
|---|---|---|---|
on |
string (date) or null | Date of the reversal entry, ISO 8601: not before the payment date, in an open period. Default: today, or the payment date when it is later (a scheduled outgoing payment). | |
reason |
string | yes | Why the payment is reversed (required, at most 500 characters): on the reversal entry, in the approval request and in the audit trail. (min length 1; max length 500) |
Example request
curl -X POST "$ERP_URL/v1/payments/3fa85f64-5717-4562-b3fc-2c963f66afa6/reverse" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"on": "2026-10-10",
"reason": "Booked twice"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | PaymentReversalResult |
| 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)
{
"payment_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "posted",
"reversal": {
"entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "string",
"number": "string",
"policy": {
"decision": "allow",
"reason": "string",
"rule_id": "string",
"table_id": "string"
},
"approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"reason": "string"
},
"released_in": "string"
}
Schemas
ApplicationInput
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code (or id) of the entity, e.g. CH01. |
payment |
string or null | Id or number of a posted payment or receipt with an amount on account to apply. Give this or credit_note. |
|
credit_note |
string or null | Id or number of a posted credit note with an open amount to apply. Give this or payment. |
|
application_date |
string (date) | yes | The day the application takes effect (ISO 8601): on or after the payment or credit note and every document it settles, in an open period. The aging and the statement count it from this day. |
allocations |
array of ApplyTo |
yes | The invoices or bills it settles, each an open posted document of the same customer or vendor, with the amount applied (and, for a document in another currency, what it settles there). Together at most what is left on account (or open on the credit note). (min items 1) |
ApplicationResult
| Field | Type | Required | Description |
|---|---|---|---|
source |
"credit_note" | "payment" |
yes | What was applied: payment or credit_note. |
source_id |
string (uuid) | yes | Id of the payment or credit note applied. |
allocations |
array of string (uuid) | yes | Ids of the new allocations, one per document. |
status |
string | yes | posted, or pending_approval when the approval policy needs a second person: until then the amounts are reserved, on both sides. |
fx_difference |
string | yes | Realised FX gain (positive) or loss (negative) booked, in the functional currency: the payment's or credit note's carrying amount against the documents'. 0 without a currency difference. |
left |
string | yes | What is left to apply afterwards, in its currency. |
submit |
SubmitResult |
yes | How the application's entry was submitted: policy, entry id and approval_id. |
ApplyTo
| Field | Type | Required | Description |
|---|---|---|---|
document |
string | yes | Id or number of the invoice or bill to settle. |
amount |
decimal string | yes | Amount applied to it, in the currency of the payment or credit note applied. (greater than 0) |
document_amount |
decimal string or null | What it settles on the document, in the document's currency. Only for a document in another currency than the payment or credit note applied; default: amount converted at the application date's rate. The difference is booked as realised FX gain or loss. (greater than 0) |
BankBalance
| Field | Type | Required | Description |
|---|---|---|---|
currency |
string | yes | ISO 4217 currency of the bills. |
account |
string | yes | Code of the bank account they are paid from. |
account_currency |
string | yes | The currency the account holds. |
balance |
string | yes | Its book balance on the payment date (posted entries), less the outgoing payments from it the bank has not executed yet (ADR-0060), in account_currency. |
CurrencyTotal
| Field | Type | Required | Description |
|---|---|---|---|
bills |
integer | yes | Number of bills. |
amount |
string | yes | Amount to pay in total, after discounts. |
discount |
string | yes | Cash discounts taken in total. |
OnAccountRefundInput
Paying back an amount kept on account: a customer's overpayment, or a vendor's refund of an advance paid to it.
| Field | Type | Required | Description |
|---|---|---|---|
payment_date |
string (date) | yes | Value date of the refund (ISO 8601), on or after the payment it pays back and its latest application, in an open period. A refund received (from a vendor) is not dated after today (receipt_in_future), nor is one paid (payment_in_future). |
amount |
decimal string | yes | Amount paid back, in the payment's currency: at most what is left on account (more_than_left_to_apply). (greater than 0) |
bank_account |
string or null | GL bank account code; default: the main bank account for the currency. | |
reference |
string or null | Free text reference. | |
executed |
boolean | A refund paid out only: true when the bank already executed it; it is booked on the bank account at once and goes into no payment file. Default false: it awaits execution on the payments-in-transit account until its bank line is matched (ADR-0060). (default false) |
OpenCredit
A payment's amount on account, or a credit note's open amount, that can still be applied.
| Field | Type | Required | Description |
|---|---|---|---|
source |
"credit_note" | "payment" |
yes | payment (an amount on account) or credit_note. |
id |
string (uuid) | yes | Id of the payment or credit note. |
number |
string or null | yes | Its number, e.g. RCPT-2026-000004 or CN-2026-000003. |
kind |
string | yes | The payment's direction (incoming, outgoing) or the credit note's kind (sales_credit_note, purchase_credit_note). |
party_id |
string (uuid) | yes | Id of the customer or vendor. |
party_code |
string | yes | Code of the customer or vendor. |
party |
string | yes | Name of the customer or vendor. |
date |
string (date) | yes | Payment date, or credit note date. |
currency |
string | yes | ISO 4217 currency of the amounts. |
amount |
string | yes | The amount kept on account when the payment was recorded, or the credit note's total. |
open |
string | yes | What is left to apply now (applications waiting for approval reserve theirs). |
PaymentInput
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code (or id) of the entity paying or receiving, e.g. CH01. |
direction |
"incoming" | "outgoing" |
yes | incoming: a receipt from a customer; outgoing: a payment to a vendor. |
party |
string | yes | Customer or vendor code (or id), e.g. C-DE or V-BM. |
payment_date |
string (date) or null | Value date of the payment (ISO 8601); default today where the company is. The exchange rate of this day gives the realised FX difference. The fiscal period must be open. A receipt (incoming) is not dated after today (receipt_in_future), nor is a payment made (payment_in_future: a payment file asks the bank for a later execution date); neither is dated before a document it settles (payment_before_document). |
|
amount |
decimal string | yes | Total amount paid, a decimal string such as 1500.00; it must equal the sum of the allocation amounts and on_account (cash discounts are on top of it). (greater than 0) |
currency |
string or null | ISO 4217 code of the payment; default: the party's currency. A document in another currency is settled too: its allocation names what it settles in the document's currency (document_amount). (pattern ^[A-Z]{3}$) |
|
bank_account |
string or null | GL bank account code; default: the main bank account (role 'bank') when it holds the payment's currency, else the first active bank account in that currency, else the main one. A receipt collected by a payment provider (Shopify Payments) names the provider's balance (role 'provider_balance'), never a default. | |
reference |
string or null | Free text reference, e.g. the bill number or a bank reference. | |
allocations |
array of AllocationInput-Input |
The documents the payment settles: each allocation names a posted document of the party and the amount applied. May be empty when the whole amount is on_account. |
|
on_account |
decimal string | The part of the amount allocated to no document, e.g. an overpayment or an advance: it stays on the party's account (an open item of the party in the aging and the statement) until it is applied to an invoice or bill with POST /v1/applications. Default 0: the allocations take the whole amount. (min 0; default "0") |
|
bank_line_id |
string (uuid) or null | Id of the bank statement line the payment records, when it is entered from a bank statement; such a payment counts as already executed and is not put into a payment file. | |
party_kind |
"customer" | "vendor" or null |
Defaults to customer for incoming, vendor for outgoing; set it for refunds. | |
executed |
boolean | Outgoing payments only: true when the bank already executed the payment (paid in e-banking, by card or in cash): it is booked on the bank account at once and goes into no payment file. Default false: the payment awaits execution by the bank (a payment file, or a transfer still to be made) and is booked on the payments-in-transit account (role payments_in_transit) until the bank statement line that shows it is matched, which moves it to the bank account (ADR-0060). A payment recorded from a bank line (bank_line_id) and a receipt always go to the bank account. (default false) |
PaymentOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | Id of the payment. |
direction |
string | yes | incoming (receipt) or outgoing (payment). |
party_id |
string (uuid) | yes | Id of the customer or vendor. |
number |
string or null | yes | Payment number such as PAY-2026-000001 or RCPT-2026-000001, assigned when it is posted; null before. |
payment_date |
string (date) | yes | Value date. |
currency |
string | yes | ISO 4217 currency of the payment. |
amount |
string | yes | Amount paid, in the payment currency. |
bank_account_id |
string (uuid) | yes | Id of the GL bank account. |
reference |
string or null | yes | Free text reference. |
status |
string | yes | draft, pending_approval, posted, rejected or reversed (its reversal is posted and the documents it settled are open again). |
journal_entry_id |
string (uuid) or null | yes | Id of the payment's journal entry. |
reversal_entry_id |
string (uuid) or null | Id of the entry that reverses the payment's entry, once a reversal was asked for (POST /v1/payments/{payment_id}/reverse): posted when the payment is reversed, else waiting for approval (or rejected); null before. |
PaymentProposal
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code of the entity. |
payment_date |
string (date) | yes | The day the proposal books the payments on. |
due_until |
string (date) | yes | Bills due on or before this date are proposed. |
take_discounts |
boolean | yes | Whether cash discounts are deducted. |
lines |
array of ProposalLine |
yes | The proposed bills, ordered by vendor and due date. |
totals_by_currency |
map of CurrencyTotal |
yes | Bills, amount and discount per currency. |
skipped |
array of SkippedBill |
Bills that would be due but are left out, with why (code): bill_payment_blocked (a purchase order variance waits for approval), bill_settled_elsewhere (another process settles it: a purchase on a company card, which the card issuer collects), party_blocked, party_inactive or party_block_waiting. |
|
bank_balances |
array of BankBalance |
Per currency of the proposed bills, the bank account the run would pay from and its book balance on the payment date: when the bills chosen add up to more (in the account's currency), the run takes it below zero in the books and says so in its warnings; it is not refused. |
PaymentReversalResult
| Field | Type | Required | Description |
|---|---|---|---|
payment_id |
string (uuid) | yes | Id of the payment. |
status |
"posted" | "reversed" |
yes | reversed: the reversal is posted, the documents the payment settled are open again; posted: the reversal waits for a second person (reversal.approval_id) and the payment stands until it is approved. |
reversal |
SubmitResult |
yes | The reversal entry's submission: policy, entry id, its number once posted and the approval_id while it waits. |
released_in |
string or null | The message id of the payment file the payment was released in, if it was: make sure the bank did not execute it (such a reversal always waits for a second person). |
PaymentReverseRequest
| Field | Type | Required | Description |
|---|---|---|---|
on |
string (date) or null | Date of the reversal entry, ISO 8601: not before the payment date, in an open period. Default: today, or the payment date when it is later (a scheduled outgoing payment). | |
reason |
string | yes | Why the payment is reversed (required, at most 500 characters): on the reversal entry, in the approval request and in the audit trail. (min length 1; max length 500) |
PaymentRunInput
| Field | Type | Required | Description |
|---|---|---|---|
payment_date |
string (date) | yes | Day the payments are booked (ISO 8601, e.g. 2026-09-30), today at the latest. The bank pays on the execution date of the payment file, which may be later but not after the cash discount period of a bill whose discount the payment takes. |
bank_account |
string or null | GL bank account code; default: per currency, as a payment's default bank account, refused when only a main account in another currency is left. | |
lines |
array of RunLine |
yes | The reviewed proposal lines to pay, 1 to 1000 vendor bills with the amount and cash discount of each. (min items 1; max items 1000) |
PaymentRunResult
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code of the entity. |
payment_date |
string (date) | yes | Date of the payments. |
payments |
array of RunPayment |
yes | The payments created, one per vendor (a bill with a structured reference on its own). |
posted |
integer | yes | Number of payments posted. |
pending_approval |
integer | yes | Number of payments waiting for a second person. |
warnings |
array of string | What to know that did not stop the run, in English: one line per bank account the run takes below zero in the books on the payment date (the money may be there and not booked yet). | |
warning_reasons |
array of Reason |
The same by code (bankBalance.warning.overdrawn, with account, entity, balance, after, currency and on; overdrawn_in_transit adds in_transit when payments the bank has not executed yet count towards it), in the order of warnings. |
ProposalCriteria
| Field | Type | Required | Description |
|---|---|---|---|
payment_date |
string (date) | yes | The day the payments would be booked; cash discounts are offered while it is within their period. A preview may look ahead; the run itself is dated today at the latest, and the bank pays on the execution date chosen when the payment file is sent. |
due_until |
string (date) or null | Last payment date: bills due on or before it are proposed (default the payment date). | |
take_discounts |
boolean | Deduct cash discounts still available on the payment date, and also propose bills not yet due whose discount would expire by due_until. (default true) |
|
vendors |
array of string or null | Vendor codes (default all). | |
currency |
string or null | ISO 4217 code: propose only bills in this currency; default: all. (pattern ^[A-Z]{3}$) |
|
bank_account |
string or null | GL bank account code the run will pay from, as the run's bank_account; default: per currency. Only bank_balances depends on it. |
ProposalLine
| Field | Type | Required | Description |
|---|---|---|---|
document_id |
string (uuid) | yes | Id of the bill. |
number |
string | yes | Number of the bill. |
external_number |
string or null | yes | The vendor's invoice number. |
vendor |
string | yes | Vendor code. |
vendor_name |
string | yes | Name of the vendor. |
currency |
string | yes | ISO 4217 currency of the bill. |
document_date |
string (date) | yes | Date of the bill. |
due_date |
string (date) | yes | Due date of the bill. |
cash_discount_percent |
string or null | yes | Cash discount percent of the bill, if any. |
cash_discount_date |
string (date) or null | yes | Last day the cash discount may be taken. |
total |
string | yes | Gross total of the bill. |
open |
string | yes | Open amount of the bill before the discount. |
discount |
string | yes | Cash discount taken by paying on the payment date. |
amount |
string | yes | Amount to pay: open less discount. |
reason |
"cash_discount" | "due" | "overdue" |
yes | Why it is proposed: overdue (due before the payment date), due (due by due_until) or cash_discount (not yet due, but the discount would expire). |
payment_iban |
string or null | yes | The bill's payment account or the vendor's. |
payment_reference |
string or null | yes | Structured reference printed on the bill, if any. |
warnings |
array of string | yes | Things to review before paying, e.g. a missing IBAN or a draft vendor. |
problems |
array of BlockerOut |
What would stop the payment going into a payment file (pain.001), by the same rule as POST /v1/payment-exports, e.g. payment_cross_border (currency, country): such a bill is paid in the bank's e-banking instead. Codes and details as the export's refusals. |
|
credit_available |
string | What the vendor has to its credit in the bill's currency: paid on account and not applied, and open credit notes (ADR-0045). Above zero, paying the bill in full would pay twice: apply the credit first (POST /v1/applications) or pay less. (default "0.00") |
|
preselected |
boolean | Whether to pay the bill as proposed; false while the vendor has credit (credit_available), with a warning. (default true) |
RunLine
| Field | Type | Required | Description |
|---|---|---|---|
document |
string | yes | Bill id or number. |
amount |
decimal string | yes | Amount to pay. (greater than 0) |
discount |
decimal string | Cash discount taken. (min 0; default "0") |
RunPayment
| Field | Type | Required | Description |
|---|---|---|---|
payment_id |
string (uuid) | yes | Id of the payment that was created. |
vendor |
string | yes | Vendor code. |
vendor_name |
string | yes | Vendor name. |
currency |
string | yes | ISO 4217 currency of the payment. |
amount |
string | yes | Amount paid, in the payment currency. |
discount |
string | yes | Cash discount taken on the bills of this payment. |
documents |
array of string | yes | Numbers of the bills the payment settles. |
status |
string | yes | posted, or pending_approval when the approval policy needs a second person. |
approval_id |
string (uuid) or null | yes | Id of the approval request when the payment waits for approval. |
entry_id |
string (uuid) or null | Id of the payment's journal entry. |
SkippedBill
| Field | Type | Required | Description |
|---|---|---|---|
document_id |
string (uuid) | yes | Id of the bill. |
number |
string | yes | Number of the bill. |
vendor |
string | yes | Vendor code. |
vendor_name |
string | yes | Name of the vendor. |
currency |
string | yes | ISO 4217 currency of the bill. |
open |
string | yes | Open amount of the bill. |
code |
string | yes | Why, as listed under skipped: see the codes there. |
reason |
string | yes | Why it is left out, in words. |