VAT returns
VAT settings and returns (CH, DE; Canada's GST/HST and QST): compute, finalise, amend, settle.
| Operation | Summary | Scope | Idempotency-Key |
|---|---|---|---|
GET /v1/entities/{entity}/vat-periods |
List the return periods of a year | read |
|
GET /v1/entities/{entity}/vat-settings |
Get the VAT settings of an entity | read |
|
PUT /v1/entities/{entity}/vat-settings |
Save the VAT settings of an entity | admin |
|
GET /v1/vat-returns |
List VAT returns | read |
|
POST /v1/vat-returns |
Compute a draft VAT return | draft |
required |
GET /v1/vat-returns/{return_id} |
Get a VAT return | read |
|
POST /v1/vat-returns/{return_id}/amend |
Open a correction of a final VAT return | approve |
required |
GET /v1/vat-returns/{return_id}/boxes/{box}/lines |
List the ledger lines behind a box | read |
|
POST /v1/vat-returns/{return_id}/discard |
Discard a draft VAT return | draft |
required |
GET /v1/vat-returns/{return_id}/file |
Return File | read |
|
POST /v1/vat-returns/{return_id}/finalize |
Finalise a VAT return | draft |
required |
GET /v1/vat-returns/{return_id}/payment-candidates |
List the entries that may have paid a VAT return | read |
|
POST /v1/vat-returns/{return_id}/payments |
Link the entry that paid a VAT return | draft |
required |
POST /v1/vat-returns/{return_id}/payments/{payment_id}/unlink |
Unlink a payment from a VAT return | draft |
required |
POST /v1/vat-returns/{return_id}/recompute |
Recompute a draft VAT return | draft |
required |
POST /v1/vat-returns/{return_id}/submitted |
Record that a VAT return was submitted | draft |
required |
POST /v1/vat-returns/{return_id}/submitted/withdraw |
Withdraw the recorded submission of a VAT return | draft |
required |
GET /v1/entities/{entity}/vat-periods
List the return periods of a year
The year's return periods as set in the entity's VAT settings: the VAT return
periods and, when a recapitulative statement period is set, its periods too. Each row
names the live return of the period (return_id, reference, payable) and its
status; a period without a draft, pending, final or amending return has the status
open. Periods are calendar months, quarters, half years or years, independent of the
fiscal calendar. Each row also says when its return is due to be filed and paid
(due_date, by the country's rule, null where none is known) and, seen from today,
due_status: filed, upcoming, due_soon, overdue or unknown.
| Required scope | read (admin implies every scope) |
| Operation id | vat_periods_v1_entities__entity__vat_periods_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| query | year |
integer | yes | Calendar year, 2000 to 2100. (min 2000; max 2100) |
Example request
curl "$ERP_URL/v1/entities/CH01/vat-periods?year=2026" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of VatPeriod |
| 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)
[
{
"filing_status": "not_submitted",
"submitted_on": "2026-09-21",
"submission_reference": "string",
"submitted_by": {},
"submitted_at": "2026-09-21T09:30:00Z",
"settled_amount": "string",
"paid_amount": "string",
"payment_difference": "string",
"payments": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_number": null,
"paid_on": "2026-09-21",
"amount": "string",
"linked_by": {},
"linked_at": "2026-09-21T09:30:00Z"
}
],
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"kind": "annual_summary",
"return_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"reference": "string",
"status": "string",
"payable": "string",
"due_date": "2026-09-21",
"payment_due_date": "2026-09-21",
"due_status": "unknown",
"payment_overdue": false
}
]
GET /v1/entities/{entity}/vat-settings
Get the VAT settings of an entity
How the entity files VAT: method, return period, recapitulative statement (ZM)
period, tax number, net tax rates and settlement account. Until settings are saved
(configured is false) the defaults apply: a Swiss entity files by the effective method
per quarter, a German one with the standard method per month and a monthly ZM, a Spanish
one per quarter with a quarterly Modelo 349, any other entity per quarter.
| Required scope | read (admin implies every scope) |
| Operation id | get_settings_v1_entities__entity__vat_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/vat-settings" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | VatSettingsOut |
| 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",
"country": "string",
"vat_number": "string",
"subdivision": "string",
"configured": true,
"registered_from": "2026-09-21",
"method": "effective",
"period": "half_year",
"basis": "invoice",
"cash_basis_law": "string",
"cash_basis_condition": "string",
"cash_basis_purchases": false,
"cash_basis_open": 0,
"recapitulative_period": "month",
"tax_number": "string",
"net_tax_rates": [
{
"accounts": [
"3400"
],
"activity_id": "A1234",
"series": "ch.vat.net_rate.treuhandbuero"
}
],
"settlement_account": "string",
"tax_fx": "daily",
"tax_fx_booking": false,
"filing_extension": false,
"registrations": [
"string"
],
"provincial_periods": {},
"quick_method": "resale",
"oss_available": false,
"oss": false,
"oss_from": "2026-09-21",
"oss_destination": false
}
PUT /v1/entities/{entity}/vat-settings
Save the VAT settings of an entity
Saves how the entity files VAT (the body replaces the stored settings) and returns
them. The effective and net_tax_rate methods exist in Switzerland only and standard
everywhere else; the net tax rate method needs the ESTV-approved rate series, each
known to the reference data, and at most one of them may leave its accounts empty.
Answers 422 for a method the country does not have, for net tax rates under another
method, for an unknown account or rate series, or for a tax_fx method the country
does not offer, for a period other than month or quarter in Italy or the United Kingdom,
and 409 when tax_fx would change while posted documents converted with
the current method are not yet covered by a final return.
| Required scope | admin (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | put_settings_v1_entities__entity__vat_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): VatSettingsInput
| Field | Type | Required | Description |
|---|---|---|---|
method |
"effective" | "net_tax_rate" | "quick" | "standard" |
yes | 'effective' or 'net_tax_rate' (CH), 'quick' (CA), else 'standard'. |
period |
"half_year" | "month" | "quarter" | "two_months" | "year" |
yes | Return period: month, two_months (the Norwegian termin), quarter, half_year or year. |
basis |
"cash" | "invoice" or null |
invoice or cash (VAT due when paid), where the country offers it (cash_basis_law). It applies to the documents posted from then on; those posted before keep theirs. Omit to keep the current basis (default invoice). |
|
recapitulative_period |
"month" | "quarter" or null |
EU recapitulative statement (ZM) period. | |
tax_number |
string or null | DE: Steuernummer. | |
registered_from |
string (date) or null | The day the VAT registration takes effect (a company registered after its books start, or one that kept its earlier returns elsewhere): periods that end before it are not listed as due and not named as unfiled. Omit to keep it; null: since the books start. | |
net_tax_rates |
array of VatNetTaxRate |
The net tax rates the ESTV approved, with their activity codes. Required for net_tax_rate and refused for any other method. Default: none. |
|
settlement_account |
string or null | Account for the VAT payable or receivable (default: role). | |
tax_fx |
"daily" | "ecb_daily" | "monthly" or null |
Official exchange rates for the VAT on foreign-currency documents. CH (MWSTV Art. 45): 'monthly' (ESTV monthly average, default) or 'daily' (daily selling rate); keep the method for at least a whole tax period. DE (UStG §16 Abs. 6): 'monthly' (BMF monthly average, default) or 'ecb_daily' (daily ECB rate, only with the tax office's approval). Omit to keep the current method. | |
tax_fx_booking |
boolean or null | Also book foreign-currency documents at the official VAT rate instead of the ECB fixing (only rates published when the document is posted). Omit to keep the current choice (default false). | |
filing_extension |
boolean or null | DE only: the tax office granted the permanent extension of the filing deadline (Dauerfristverlängerung, UStDV §§ 46-48), so each advance return is due on the 10th of the second month after its period. Omit to keep the current choice (default false). | |
registrations |
array of string or null | CA: the provinces besides its own whose sales tax the entity collects: CA-QC, CA-BC, CA-SK, CA-MB. Omit to keep the current list. |
|
provincial_periods |
map of "half_year" | "month" | "quarter" | "two_months" | "year" or null |
CA: per province (CA-BC, CA-SK, CA-MB, CA-QC), the period of its sales tax return where it differs from period. Omit to keep the current periods. |
|
quick_method |
"resale" | "services" or null |
CA, required with method quick: resale (goods bought for resale are at least 40% of taxable sales) or services. |
|
oss |
boolean or null | EU only: registered for the One-Stop Shop (OSS) union scheme. Omit to keep the current choice (default false). | |
oss_from |
string (date) or null | EU only: the day the OSS registration takes effect: the first day of the quarter after the registration, or the day of the first such sale when it is notified by the 10th of the following month (Implementing Regulation 282/2011 art. 57d). Omit to keep it; when oss is switched on without one, the first day of the current quarter. |
|
oss_destination |
boolean or null | EU only: charge the customer's VAT on distance sales to consumers in other member states below the EUR 10,000 threshold too (art. 59c(3); binding for two calendar years). Omit to keep the current choice (default false). |
Example request
curl -X PUT "$ERP_URL/v1/entities/CH01/vat-settings" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"method": "effective",
"period": "quarter",
"settlement_account": "2202"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | VatSettingsOut |
| 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",
"country": "string",
"vat_number": "string",
"subdivision": "string",
"configured": true,
"registered_from": "2026-09-21",
"method": "effective",
"period": "half_year",
"basis": "invoice",
"cash_basis_law": "string",
"cash_basis_condition": "string",
"cash_basis_purchases": false,
"cash_basis_open": 0,
"recapitulative_period": "month",
"tax_number": "string",
"net_tax_rates": [
{
"accounts": [
"3400"
],
"activity_id": "A1234",
"series": "ch.vat.net_rate.treuhandbuero"
}
],
"settlement_account": "string",
"tax_fx": "daily",
"tax_fx_booking": false,
"filing_extension": false,
"registrations": [
"string"
],
"provincial_periods": {},
"quick_method": "resale",
"oss_available": false,
"oss": false,
"oss_from": "2026-09-21",
"oss_destination": false
}
GET /v1/vat-returns
List VAT returns
Summaries of the organization's VAT returns, newest period first, without their
boxes: status, payable, the settlement entry and the number of errors and warnings.
Open one with GET /v1/vat-returns/{return_id} for the boxes and issues. All filters
are optional and combine; discarded and superseded returns are listed unless filtered.
| Required scope | read (admin implies every scope) |
| Operation id | list_returns_v1_vat_returns_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| query | entity |
string or null | Only returns of this entity, by code. | |
| query | kind |
"annual_summary" | "oss_return" | "pst_return_bc" | "pst_return_sk" | "qst_return" | "recapitulative_statement" | "rst_return_mb" | "vat_return" or null |
Only this kind: vat_return, recapitulative_statement, qst_return, pst_return_bc, pst_return_sk, rst_return_mb, annual_summary, oss_return. |
|
| query | status |
string or null | Only returns with this status: draft, pending_approval, final, amending, superseded or discarded. |
|
| query | year |
integer or null | Only returns whose period starts in this calendar year. |
Example request
curl "$ERP_URL/v1/vat-returns" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of VatReturnSummary |
| 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 |
| 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)
[
{
"filing_status": "not_submitted",
"submitted_on": "2026-09-21",
"submission_reference": "string",
"submitted_by": {},
"submitted_at": "2026-09-21T09:30:00Z",
"settled_amount": "string",
"paid_amount": "string",
"payment_difference": "string",
"payments": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_number": null,
"paid_on": "2026-09-21",
"amount": "string",
"linked_by": {},
"linked_at": "2026-09-21T09:30:00Z"
}
],
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"kind": "annual_summary",
"reference": "string",
"form_id": "string",
"form_version": "string",
"method": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"status": "string",
"currency": "string",
"payable": "string",
"amends_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_number": "string",
"errors": 1,
"warnings": 1,
"computed_at": "2026-09-21T09:30:00Z",
"finalized_at": "2026-09-21T09:30:00Z",
"finalized_by": {}
}
]
POST /v1/vat-returns
Compute a draft VAT return
Computes a draft return (vat_return, the EU recapitulative_statement, or the
annual_summary where the country has one, the Spanish Modelo 390) for an
entity and period (2026-08, 2026-Q3, 2026-H2 or 2026) from the posted, tax-tagged
journal lines, and stores which line fills which box. An existing draft of the period is
recomputed instead. The result carries the boxes, the reconciliation with the VAT accounts
and the issues. Answers 409 when the period already has a pending, final or amending
return (amend it instead) and 422 for a malformed period or an adjustment on a box the
ledger fills or computes.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | create_return_v1_vat_returns_post |
Request body
application/json (required): VatReturnInput
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Entity code, e.g. CH01 or DE01 (GET /v1/entities). |
kind |
"annual_summary" | "oss_return" | "pst_return_bc" | "pst_return_sk" | "qst_return" | "recapitulative_statement" | "rst_return_mb" | "vat_return" |
vat_return, recapitulative_statement (ZM, Modelo 349), qst_return (Quebec), pst_return_bc, pst_return_sk, rst_return_mb, annual_summary (Modelo 390, Dichiarazione IVA) or oss_return (EU One-Stop Shop, union scheme). Default vat_return; annual_summary takes the period YYYY. (default "vat_return") |
|
period |
string | yes | '2026-08', '2026-Q3', '2026-H2' or '2026'. |
adjustments |
array of VatAdjustment-Input |
Hand-entered amounts for boxes the ledger cannot fill (import VAT, Einlageentsteuerung and similar). Default: none. |
Example request
curl -X POST "$ERP_URL/v1/vat-returns" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"entity": "CH01",
"kind": "vat_return",
"period": "2026-Q3"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | VatReturnOut |
| 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)
{
"filing_status": "not_submitted",
"submitted_on": "2026-09-21",
"submission_reference": "string",
"submitted_by": {},
"submitted_at": "2026-09-21T09:30:00Z",
"settled_amount": "string",
"paid_amount": "string",
"payment_difference": "string",
"payments": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_number": "string",
"paid_on": "2026-09-21",
"amount": "string",
"linked_by": {},
"linked_at": "2026-09-21T09:30:00Z"
}
],
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"kind": "annual_summary",
"reference": "string",
"form_id": "string",
"form_version": "string",
"method": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"status": "string",
"currency": "string",
"payable": "string",
"amends_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_number": "string",
"errors": 1,
"warnings": 1,
"computed_at": "2026-09-21T09:30:00Z",
"finalized_at": "2026-09-21T09:30:00Z",
"finalized_by": {},
"result": {
"kind": "annual_summary",
"form": {
"id": "string",
"jurisdiction": "",
"name": "string",
"kind": "annual_summary",
"method": null,
"version": "string",
"currency": "string",
"base_precision": 1,
"tax_precision": 2,
"notes": [
null
],
"limitations": [
null
],
"sources": [
null
]
},
"entity": "string",
"entity_name": "string",
"vat_number": "string",
"tax_number": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"currency": "string",
"method": "string",
"boxes": [
{
"id": null,
"code": null,
"line": null,
"label": null,
"section": null,
"unit": null,
"rate": null,
"amount": null,
"tax": null,
"computed": null,
"lines": null,
"adjustable": null,
"adjusted": null,
"filled": null,
"xml": null,
"by_rate": null
}
],
"rows": [
{
"country_code": null,
"vat_id": null,
"party": null,
"box": null,
"amount": null
}
],
"oss_rows": [
{
"member_state": null,
"supply": null,
"rate": null,
"rate_type": null,
"base": null,
"vat": null
}
],
"oss_corrections": [
{
"period": null,
"member_state": null,
"vat": null
}
],
"activities": [
{
"activity_id": null,
"series": null,
"activity": null,
"rate": null,
"turnover": null,
"tax": null
}
],
"payable": "string",
"reconciliation": {
"accounts": null,
"ledger": null,
"reported": null,
"other_period": null,
"payable": null,
"adjustments": null,
"computation_difference": null,
"tax_fx_difference": null,
"whole_unit_rounding": null,
"unexplained": null,
"settlement": null,
"reconciling_items": null
},
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"issues": [
{
"severity": null,
"code": null,
"message": null,
"details": null
}
],
"lines": 1,
"dataset_version": "string",
"tax_fx": [
{
"series": null,
"currency": null,
"period": null,
"rate": null,
"quotation": null,
"lines": null,
"looked_up": null
}
],
"payment_code": "string",
"payment_due_date": "2026-09-21"
},
"explanation": "string",
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"test_copy": false
}
GET /v1/vat-returns/{return_id}
Get a VAT return
The return as stored: status, payable, the settlement entry and the computed result
(form, boxes with their Ziffer or Kennzahl, reconciliation, issues, adjustments). A final
return and its result do not change; a draft shows the state of its last computation.
| Required scope | read (admin implies every scope) |
| Operation id | get_return_v1_vat_returns__return_id__get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | return_id |
string (uuid) | yes | VAT return id. |
Example request
curl "$ERP_URL/v1/vat-returns/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | VatReturnOut |
| 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)
{
"filing_status": "not_submitted",
"submitted_on": "2026-09-21",
"submission_reference": "string",
"submitted_by": {},
"submitted_at": "2026-09-21T09:30:00Z",
"settled_amount": "string",
"paid_amount": "string",
"payment_difference": "string",
"payments": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_number": "string",
"paid_on": "2026-09-21",
"amount": "string",
"linked_by": {},
"linked_at": "2026-09-21T09:30:00Z"
}
],
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"kind": "annual_summary",
"reference": "string",
"form_id": "string",
"form_version": "string",
"method": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"status": "string",
"currency": "string",
"payable": "string",
"amends_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_number": "string",
"errors": 1,
"warnings": 1,
"computed_at": "2026-09-21T09:30:00Z",
"finalized_at": "2026-09-21T09:30:00Z",
"finalized_by": {},
"result": {
"kind": "annual_summary",
"form": {
"id": "string",
"jurisdiction": "",
"name": "string",
"kind": "annual_summary",
"method": null,
"version": "string",
"currency": "string",
"base_precision": 1,
"tax_precision": 2,
"notes": [
null
],
"limitations": [
null
],
"sources": [
null
]
},
"entity": "string",
"entity_name": "string",
"vat_number": "string",
"tax_number": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"currency": "string",
"method": "string",
"boxes": [
{
"id": null,
"code": null,
"line": null,
"label": null,
"section": null,
"unit": null,
"rate": null,
"amount": null,
"tax": null,
"computed": null,
"lines": null,
"adjustable": null,
"adjusted": null,
"filled": null,
"xml": null,
"by_rate": null
}
],
"rows": [
{
"country_code": null,
"vat_id": null,
"party": null,
"box": null,
"amount": null
}
],
"oss_rows": [
{
"member_state": null,
"supply": null,
"rate": null,
"rate_type": null,
"base": null,
"vat": null
}
],
"oss_corrections": [
{
"period": null,
"member_state": null,
"vat": null
}
],
"activities": [
{
"activity_id": null,
"series": null,
"activity": null,
"rate": null,
"turnover": null,
"tax": null
}
],
"payable": "string",
"reconciliation": {
"accounts": null,
"ledger": null,
"reported": null,
"other_period": null,
"payable": null,
"adjustments": null,
"computation_difference": null,
"tax_fx_difference": null,
"whole_unit_rounding": null,
"unexplained": null,
"settlement": null,
"reconciling_items": null
},
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"issues": [
{
"severity": null,
"code": null,
"message": null,
"details": null
}
],
"lines": 1,
"dataset_version": "string",
"tax_fx": [
{
"series": null,
"currency": null,
"period": null,
"rate": null,
"quotation": null,
"lines": null,
"looked_up": null
}
],
"payment_code": "string",
"payment_due_date": "2026-09-21"
},
"explanation": "string",
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"test_copy": false
}
POST /v1/vat-returns/{return_id}/amend
Open a correction of a final VAT return
Opens a correction of a final return: the original becomes amending, the period's
VAT postings are unlocked, and a new draft (its amends_id names the original) is
computed from the current ledger. Finalising the correction books only the difference to
the original in its settlement and marks the original superseded; discarding the
correction puts the original back in force. Takes no body. Needs the approval policy
action finalize_vat_return (403 when denied) and answers 409 unless the return is
final.
| Required scope | approve (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | amend_v1_vat_returns__return_id__amend_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | return_id |
string (uuid) | yes | VAT return id. |
Example request
curl -X POST "$ERP_URL/v1/vat-returns/3fa85f64-5717-4562-b3fc-2c963f66afa6/amend" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)"
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | VatReturnOut |
| 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)
{
"filing_status": "not_submitted",
"submitted_on": "2026-09-21",
"submission_reference": "string",
"submitted_by": {},
"submitted_at": "2026-09-21T09:30:00Z",
"settled_amount": "string",
"paid_amount": "string",
"payment_difference": "string",
"payments": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_number": "string",
"paid_on": "2026-09-21",
"amount": "string",
"linked_by": {},
"linked_at": "2026-09-21T09:30:00Z"
}
],
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"kind": "annual_summary",
"reference": "string",
"form_id": "string",
"form_version": "string",
"method": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"status": "string",
"currency": "string",
"payable": "string",
"amends_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_number": "string",
"errors": 1,
"warnings": 1,
"computed_at": "2026-09-21T09:30:00Z",
"finalized_at": "2026-09-21T09:30:00Z",
"finalized_by": {},
"result": {
"kind": "annual_summary",
"form": {
"id": "string",
"jurisdiction": "",
"name": "string",
"kind": "annual_summary",
"method": null,
"version": "string",
"currency": "string",
"base_precision": 1,
"tax_precision": 2,
"notes": [
null
],
"limitations": [
null
],
"sources": [
null
]
},
"entity": "string",
"entity_name": "string",
"vat_number": "string",
"tax_number": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"currency": "string",
"method": "string",
"boxes": [
{
"id": null,
"code": null,
"line": null,
"label": null,
"section": null,
"unit": null,
"rate": null,
"amount": null,
"tax": null,
"computed": null,
"lines": null,
"adjustable": null,
"adjusted": null,
"filled": null,
"xml": null,
"by_rate": null
}
],
"rows": [
{
"country_code": null,
"vat_id": null,
"party": null,
"box": null,
"amount": null
}
],
"oss_rows": [
{
"member_state": null,
"supply": null,
"rate": null,
"rate_type": null,
"base": null,
"vat": null
}
],
"oss_corrections": [
{
"period": null,
"member_state": null,
"vat": null
}
],
"activities": [
{
"activity_id": null,
"series": null,
"activity": null,
"rate": null,
"turnover": null,
"tax": null
}
],
"payable": "string",
"reconciliation": {
"accounts": null,
"ledger": null,
"reported": null,
"other_period": null,
"payable": null,
"adjustments": null,
"computation_difference": null,
"tax_fx_difference": null,
"whole_unit_rounding": null,
"unexplained": null,
"settlement": null,
"reconciling_items": null
},
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"issues": [
{
"severity": null,
"code": null,
"message": null,
"details": null
}
],
"lines": 1,
"dataset_version": "string",
"tax_fx": [
{
"series": null,
"currency": null,
"period": null,
"rate": null,
"quotation": null,
"lines": null,
"looked_up": null
}
],
"payment_code": "string",
"payment_due_date": "2026-09-21"
},
"explanation": "string",
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"test_copy": false
}
GET /v1/vat-returns/{return_id}/boxes/{box}/lines
List the ledger lines behind a box
The drill-down of one box: every journal line that was routed into it, with its journal entry, account, document, counterparty, the rule that routed it and the amount it contributes in the return's currency. Ordered by entry date and number. A box that is computed from other boxes, filled by adjustment only, or unknown has no lines (an empty list).
| Required scope | read (admin implies every scope) |
| Operation id | box_lines_v1_vat_returns__return_id__boxes__box__lines_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | return_id |
string (uuid) | yes | VAT return id. |
| path | box |
string | yes | Box id as in the return's boxes[].id: a Ziffer such as 303 (CH) or a Kennzahl id such as kz81 (DE). |
Example request
curl "$ERP_URL/v1/vat-returns/3fa85f64-5717-4562-b3fc-2c963f66afa6/boxes/303/lines" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of VatBoxLine |
| 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)
[
{
"journal_line_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_number": "string",
"entry_date": "2026-09-21",
"account": "string",
"account_name": "string",
"description": "string",
"document": "string",
"counterparty": "string",
"row_key": "string",
"rule": "string",
"amount": "string"
}
]
POST /v1/vat-returns/{return_id}/discard
Discard a draft VAT return
Discards a draft return, which stays listed with the status discarded; the period
can then be computed again. Discarding a correction puts the original back from
amending to final. Takes no body. Answers 409 unless the return is a draft.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | discard_v1_vat_returns__return_id__discard_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | return_id |
string (uuid) | yes | VAT return id. |
Example request
curl -X POST "$ERP_URL/v1/vat-returns/3fa85f64-5717-4562-b3fc-2c963f66afa6/discard" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | VatReturnOut |
| 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)
{
"filing_status": "not_submitted",
"submitted_on": "2026-09-21",
"submission_reference": "string",
"submitted_by": {},
"submitted_at": "2026-09-21T09:30:00Z",
"settled_amount": "string",
"paid_amount": "string",
"payment_difference": "string",
"payments": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_number": "string",
"paid_on": "2026-09-21",
"amount": "string",
"linked_by": {},
"linked_at": "2026-09-21T09:30:00Z"
}
],
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"kind": "annual_summary",
"reference": "string",
"form_id": "string",
"form_version": "string",
"method": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"status": "string",
"currency": "string",
"payable": "string",
"amends_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_number": "string",
"errors": 1,
"warnings": 1,
"computed_at": "2026-09-21T09:30:00Z",
"finalized_at": "2026-09-21T09:30:00Z",
"finalized_by": {},
"result": {
"kind": "annual_summary",
"form": {
"id": "string",
"jurisdiction": "",
"name": "string",
"kind": "annual_summary",
"method": null,
"version": "string",
"currency": "string",
"base_precision": 1,
"tax_precision": 2,
"notes": [
null
],
"limitations": [
null
],
"sources": [
null
]
},
"entity": "string",
"entity_name": "string",
"vat_number": "string",
"tax_number": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"currency": "string",
"method": "string",
"boxes": [
{
"id": null,
"code": null,
"line": null,
"label": null,
"section": null,
"unit": null,
"rate": null,
"amount": null,
"tax": null,
"computed": null,
"lines": null,
"adjustable": null,
"adjusted": null,
"filled": null,
"xml": null,
"by_rate": null
}
],
"rows": [
{
"country_code": null,
"vat_id": null,
"party": null,
"box": null,
"amount": null
}
],
"oss_rows": [
{
"member_state": null,
"supply": null,
"rate": null,
"rate_type": null,
"base": null,
"vat": null
}
],
"oss_corrections": [
{
"period": null,
"member_state": null,
"vat": null
}
],
"activities": [
{
"activity_id": null,
"series": null,
"activity": null,
"rate": null,
"turnover": null,
"tax": null
}
],
"payable": "string",
"reconciliation": {
"accounts": null,
"ledger": null,
"reported": null,
"other_period": null,
"payable": null,
"adjustments": null,
"computation_difference": null,
"tax_fx_difference": null,
"whole_unit_rounding": null,
"unexplained": null,
"settlement": null,
"reconciling_items": null
},
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"issues": [
{
"severity": null,
"code": null,
"message": null,
"details": null
}
],
"lines": 1,
"dataset_version": "string",
"tax_fx": [
{
"series": null,
"currency": null,
"period": null,
"rate": null,
"quotation": null,
"lines": null,
"looked_up": null
}
],
"payment_code": "string",
"payment_due_date": "2026-09-21"
},
"explanation": "string",
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"test_copy": false
}
GET /v1/vat-returns/{return_id}/file
Return File
Downloads the return in the requested format: xml is the eCH-0217 upload for the
ESTV portal, csv the ZM import for the BZStOnline-Portal, html and pdf are
printable (shown inline, marked with the status and as a correction where it is one) and
json is the return itself. Works for a draft too. Answers 422 when the format does not
fit the return: xml needs a Swiss VAT return with an amount payable and the entity's
UID, csv a recapitulative statement with at least one and at most 1000 rows.
| Required scope | read (admin implies every scope) |
| Operation id | return_file_v1_vat_returns__return_id__file_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | return_id |
string (uuid) | yes | VAT return id. |
| query | format |
"csv" | "html" | "json" | "pdf" | "xml" |
File format: json (the return as GET /v1/vat-returns/{return_id} gives it), xml (eCH-0217, Swiss VAT returns), csv (BZStOnline-Portal ZM import, recapitulative statements), or html / pdf (printable form). (default "json") |
|
| query | csv_version |
string | ZM CSV: the version the BZStOnline-Portal's import help shows. (pattern ^\d+\.\d+$; default "1.0") |
Example request
curl "$ERP_URL/v1/vat-returns/3fa85f64-5717-4562-b3fc-2c963f66afa6/file" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | eCH-0217 XML (CH), BZSt ZM CSV (DE), printable HTML or PDF, JSON. | application/xml, text/csv, text/html, application/pdf, VatReturnOut |
| 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)
{
"filing_status": "not_submitted",
"submitted_on": "2026-09-21",
"submission_reference": "string",
"submitted_by": {},
"submitted_at": "2026-09-21T09:30:00Z",
"settled_amount": "string",
"paid_amount": "string",
"payment_difference": "string",
"payments": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_number": "string",
"paid_on": "2026-09-21",
"amount": "string",
"linked_by": {},
"linked_at": "2026-09-21T09:30:00Z"
}
],
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"kind": "annual_summary",
"reference": "string",
"form_id": "string",
"form_version": "string",
"method": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"status": "string",
"currency": "string",
"payable": "string",
"amends_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_number": "string",
"errors": 1,
"warnings": 1,
"computed_at": "2026-09-21T09:30:00Z",
"finalized_at": "2026-09-21T09:30:00Z",
"finalized_by": {},
"result": {
"kind": "annual_summary",
"form": {
"id": "string",
"jurisdiction": "",
"name": "string",
"kind": "annual_summary",
"method": null,
"version": "string",
"currency": "string",
"base_precision": 1,
"tax_precision": 2,
"notes": [
null
],
"limitations": [
null
],
"sources": [
null
]
},
"entity": "string",
"entity_name": "string",
"vat_number": "string",
"tax_number": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"currency": "string",
"method": "string",
"boxes": [
{
"id": null,
"code": null,
"line": null,
"label": null,
"section": null,
"unit": null,
"rate": null,
"amount": null,
"tax": null,
"computed": null,
"lines": null,
"adjustable": null,
"adjusted": null,
"filled": null,
"xml": null,
"by_rate": null
}
],
"rows": [
{
"country_code": null,
"vat_id": null,
"party": null,
"box": null,
"amount": null
}
],
"oss_rows": [
{
"member_state": null,
"supply": null,
"rate": null,
"rate_type": null,
"base": null,
"vat": null
}
],
"oss_corrections": [
{
"period": null,
"member_state": null,
"vat": null
}
],
"activities": [
{
"activity_id": null,
"series": null,
"activity": null,
"rate": null,
"turnover": null,
"tax": null
}
],
"payable": "string",
"reconciliation": {
"accounts": null,
"ledger": null,
"reported": null,
"other_period": null,
"payable": null,
"adjustments": null,
"computation_difference": null,
"tax_fx_difference": null,
"whole_unit_rounding": null,
"unexplained": null,
"settlement": null,
"reconciling_items": null
},
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"issues": [
{
"severity": null,
"code": null,
"message": null,
"details": null
}
],
"lines": 1,
"dataset_version": "string",
"tax_fx": [
{
"series": null,
"currency": null,
"period": null,
"rate": null,
"quotation": null,
"lines": null,
"looked_up": null
}
],
"payment_code": "string",
"payment_due_date": "2026-09-21"
},
"explanation": "string",
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"test_copy": false
}
POST /v1/vat-returns/{return_id}/finalize
Finalise a VAT return
Finalises a draft: it is recomputed, and the settlement entry (source vat, clearing
the reported VAT to the settlement account) goes through the approval policy. The
response's settlement.policy is the decision: when the entry is posted the return is
final and the period's VAT postings are locked; when it needs a second person the
return is pending_approval until the entry is approved or rejected (a rejection puts
it back to draft). A return with nothing to settle, such as a recapitulative
statement, becomes final at once and settlement is null. Needs the approval policy
action finalize_vat_return (403 when denied, which is the default for agents). Answers
409 when the return is not a draft, the ledger changed since it was computed, it has
error issues, or the VAT accounts hold amounts it does not explain and no explanation
is given; 422 when posting_date is before the period's end or no open period follows.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | finalize_v1_vat_returns__return_id__finalize_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | return_id |
string (uuid) | yes | VAT return id. |
Request body
application/json (required): VatFinalizeInput
| Field | Type | Required | Description |
|---|---|---|---|
posting_date |
string (date) or null | Date of the settlement entry; default the period's last day, or the first open day after it. | |
explanation |
string or null | Required when the VAT accounts hold amounts the return does not explain. (max length 2000) |
Example request
curl -X POST "$ERP_URL/v1/vat-returns/3fa85f64-5717-4562-b3fc-2c963f66afa6/finalize" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"explanation": "The difference is a manual VAT correction booked on 2026-09-12.",
"posting_date": "2026-09-30"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | VatFinalizeOut |
| 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)
{
"vat_return": {
"filing_status": "not_submitted",
"submitted_on": "2026-09-21",
"submission_reference": "string",
"submitted_by": {},
"submitted_at": "2026-09-21T09:30:00Z",
"settled_amount": "string",
"paid_amount": "string",
"payment_difference": "string",
"payments": [
{
"id": null,
"journal_entry_id": null,
"entry_number": null,
"paid_on": null,
"amount": null,
"linked_by": null,
"linked_at": null
}
],
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"kind": "annual_summary",
"reference": "string",
"form_id": "string",
"form_version": "string",
"method": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"status": "string",
"currency": "string",
"payable": "string",
"amends_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_number": "string",
"errors": 1,
"warnings": 1,
"computed_at": "2026-09-21T09:30:00Z",
"finalized_at": "2026-09-21T09:30:00Z",
"finalized_by": {},
"result": {
"kind": "annual_summary",
"form": null,
"entity": "string",
"entity_name": "string",
"vat_number": null,
"tax_number": null,
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"currency": "string",
"method": null,
"boxes": [
null
],
"rows": [
null
],
"oss_rows": [
null
],
"oss_corrections": [
null
],
"activities": [
null
],
"payable": null,
"reconciliation": null,
"adjustments": [
null
],
"issues": [
null
],
"lines": 1,
"dataset_version": "string",
"tax_fx": [
null
],
"payment_code": null,
"payment_due_date": null
},
"explanation": "string",
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"test_copy": false
},
"settlement": {
"entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "string",
"number": "string",
"policy": {
"decision": null,
"reason": null,
"rule_id": null,
"table_id": null
},
"approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"reason": "string"
}
}
GET /v1/vat-returns/{return_id}/payment-candidates
List the entries that may have paid a VAT return
Posted entries of the return's company, dated on or after the settlement, with a line on the settlement account on the side opposite the settlement (a debit for VAT to pay, a credit for a refund) that no return has linked yet; VAT settlements are left out. Typically the bank entry made by matching the bank debit to the settlement account on the bank worksheet. Empty for a return that is not final or settled nothing.
| Required scope | read (admin implies every scope) |
| Operation id | payment_candidates_v1_vat_returns__return_id__payment_candidates_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | return_id |
string (uuid) | yes | VAT return id. |
Example request
curl "$ERP_URL/v1/vat-returns/3fa85f64-5717-4562-b3fc-2c963f66afa6/payment-candidates" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of VatPaymentCandidate |
| 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)
[
{
"journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_number": "string",
"entry_date": "2026-09-21",
"description": "string",
"source": "string",
"amount": "string"
}
]
POST /v1/vat-returns/{return_id}/payments
Link the entry that paid a VAT return
Links a posted entry as paying the return's VAT (or receiving its refund): its amount is
what it puts on the settlement account against the settlement and its day the payment day.
A part payment is one link of several; the return is paid once the links cover
settled_amount, and a difference is shown (payment_difference), not refused. Answers 409
unless the return is final and recorded as submitted, when it settled nothing, or when the
entry already pays a return; 422 for an entry of another company, not posted, a VAT
settlement, dated before the settlement, or without a line on the settlement account on the
paying side.
A person's act: needs the approval policy action finalize_vat_return, which agents are
always denied (403).
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | link_payment_v1_vat_returns__return_id__payments_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | return_id |
string (uuid) | yes | VAT return id. |
Request body
application/json (required): VatPaymentInput
| Field | Type | Required | Description |
|---|---|---|---|
journal_entry_id |
string (uuid) | yes | The posted entry that paid the VAT (or received the refund): of the return's company, dated on or after the settlement, with a line on the settlement account on the side opposite the settlement (GET .../payment-candidates). |
Example request
curl -X POST "$ERP_URL/v1/vat-returns/3fa85f64-5717-4562-b3fc-2c963f66afa6/payments" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | VatReturnOut |
| 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)
{
"filing_status": "not_submitted",
"submitted_on": "2026-09-21",
"submission_reference": "string",
"submitted_by": {},
"submitted_at": "2026-09-21T09:30:00Z",
"settled_amount": "string",
"paid_amount": "string",
"payment_difference": "string",
"payments": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_number": "string",
"paid_on": "2026-09-21",
"amount": "string",
"linked_by": {},
"linked_at": "2026-09-21T09:30:00Z"
}
],
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"kind": "annual_summary",
"reference": "string",
"form_id": "string",
"form_version": "string",
"method": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"status": "string",
"currency": "string",
"payable": "string",
"amends_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_number": "string",
"errors": 1,
"warnings": 1,
"computed_at": "2026-09-21T09:30:00Z",
"finalized_at": "2026-09-21T09:30:00Z",
"finalized_by": {},
"result": {
"kind": "annual_summary",
"form": {
"id": "string",
"jurisdiction": "",
"name": "string",
"kind": "annual_summary",
"method": null,
"version": "string",
"currency": "string",
"base_precision": 1,
"tax_precision": 2,
"notes": [
null
],
"limitations": [
null
],
"sources": [
null
]
},
"entity": "string",
"entity_name": "string",
"vat_number": "string",
"tax_number": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"currency": "string",
"method": "string",
"boxes": [
{
"id": null,
"code": null,
"line": null,
"label": null,
"section": null,
"unit": null,
"rate": null,
"amount": null,
"tax": null,
"computed": null,
"lines": null,
"adjustable": null,
"adjusted": null,
"filled": null,
"xml": null,
"by_rate": null
}
],
"rows": [
{
"country_code": null,
"vat_id": null,
"party": null,
"box": null,
"amount": null
}
],
"oss_rows": [
{
"member_state": null,
"supply": null,
"rate": null,
"rate_type": null,
"base": null,
"vat": null
}
],
"oss_corrections": [
{
"period": null,
"member_state": null,
"vat": null
}
],
"activities": [
{
"activity_id": null,
"series": null,
"activity": null,
"rate": null,
"turnover": null,
"tax": null
}
],
"payable": "string",
"reconciliation": {
"accounts": null,
"ledger": null,
"reported": null,
"other_period": null,
"payable": null,
"adjustments": null,
"computation_difference": null,
"tax_fx_difference": null,
"whole_unit_rounding": null,
"unexplained": null,
"settlement": null,
"reconciling_items": null
},
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"issues": [
{
"severity": null,
"code": null,
"message": null,
"details": null
}
],
"lines": 1,
"dataset_version": "string",
"tax_fx": [
{
"series": null,
"currency": null,
"period": null,
"rate": null,
"quotation": null,
"lines": null,
"looked_up": null
}
],
"payment_code": "string",
"payment_due_date": "2026-09-21"
},
"explanation": "string",
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"test_copy": false
}
POST /v1/vat-returns/{return_id}/payments/{payment_id}/unlink
Unlink a payment from a VAT return
Undoes a payment link made by mistake, with the reason (kept in the history; the link
is not deleted and the entry stays posted). 404 for a link that is not in force on this
return.
A person's act: needs the approval policy action finalize_vat_return, which agents are
always denied (403).
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | unlink_payment_v1_vat_returns__return_id__payments__payment_id__unlink_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | return_id |
string (uuid) | yes | VAT return id. |
| path | payment_id |
string (uuid) | yes | Id of the payment link (payments[].id of the return). |
Request body
application/json (required): VatReasonInput
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | yes | Why, in a few words; kept in the history. (min length 1; max length 500) |
Example request
curl -X POST "$ERP_URL/v1/vat-returns/3fa85f64-5717-4562-b3fc-2c963f66afa6/payments/3fa85f64-5717-4562-b3fc-2c963f66afa6/unlink" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"reason": "Recorded on the wrong return"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | VatReturnOut |
| 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)
{
"filing_status": "not_submitted",
"submitted_on": "2026-09-21",
"submission_reference": "string",
"submitted_by": {},
"submitted_at": "2026-09-21T09:30:00Z",
"settled_amount": "string",
"paid_amount": "string",
"payment_difference": "string",
"payments": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_number": "string",
"paid_on": "2026-09-21",
"amount": "string",
"linked_by": {},
"linked_at": "2026-09-21T09:30:00Z"
}
],
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"kind": "annual_summary",
"reference": "string",
"form_id": "string",
"form_version": "string",
"method": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"status": "string",
"currency": "string",
"payable": "string",
"amends_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_number": "string",
"errors": 1,
"warnings": 1,
"computed_at": "2026-09-21T09:30:00Z",
"finalized_at": "2026-09-21T09:30:00Z",
"finalized_by": {},
"result": {
"kind": "annual_summary",
"form": {
"id": "string",
"jurisdiction": "",
"name": "string",
"kind": "annual_summary",
"method": null,
"version": "string",
"currency": "string",
"base_precision": 1,
"tax_precision": 2,
"notes": [
null
],
"limitations": [
null
],
"sources": [
null
]
},
"entity": "string",
"entity_name": "string",
"vat_number": "string",
"tax_number": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"currency": "string",
"method": "string",
"boxes": [
{
"id": null,
"code": null,
"line": null,
"label": null,
"section": null,
"unit": null,
"rate": null,
"amount": null,
"tax": null,
"computed": null,
"lines": null,
"adjustable": null,
"adjusted": null,
"filled": null,
"xml": null,
"by_rate": null
}
],
"rows": [
{
"country_code": null,
"vat_id": null,
"party": null,
"box": null,
"amount": null
}
],
"oss_rows": [
{
"member_state": null,
"supply": null,
"rate": null,
"rate_type": null,
"base": null,
"vat": null
}
],
"oss_corrections": [
{
"period": null,
"member_state": null,
"vat": null
}
],
"activities": [
{
"activity_id": null,
"series": null,
"activity": null,
"rate": null,
"turnover": null,
"tax": null
}
],
"payable": "string",
"reconciliation": {
"accounts": null,
"ledger": null,
"reported": null,
"other_period": null,
"payable": null,
"adjustments": null,
"computation_difference": null,
"tax_fx_difference": null,
"whole_unit_rounding": null,
"unexplained": null,
"settlement": null,
"reconciling_items": null
},
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"issues": [
{
"severity": null,
"code": null,
"message": null,
"details": null
}
],
"lines": 1,
"dataset_version": "string",
"tax_fx": [
{
"series": null,
"currency": null,
"period": null,
"rate": null,
"quotation": null,
"lines": null,
"looked_up": null
}
],
"payment_code": "string",
"payment_due_date": "2026-09-21"
},
"explanation": "string",
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"test_copy": false
}
POST /v1/vat-returns/{return_id}/recompute
Recompute a draft VAT return
Recomputes a draft from the current ledger, replacing its stored result and box
lines. Send adjustments to replace the hand-entered amounts, or omit them to keep
the current ones. Answers 409 unless the return is a draft and 422 for an adjustment on
a box the ledger fills or computes, or one that changes the amount payable without an
offset account.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | recompute_v1_vat_returns__return_id__recompute_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | return_id |
string (uuid) | yes | VAT return id. |
Request body
application/json (required): VatRecomputeInput
| Field | Type | Required | Description |
|---|---|---|---|
adjustments |
array of VatAdjustment-Input or null |
Replaces the adjustments; omit to keep them. |
Example request
curl -X POST "$ERP_URL/v1/vat-returns/3fa85f64-5717-4562-b3fc-2c963f66afa6/recompute" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410"
}
]
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | VatReturnOut |
| 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)
{
"filing_status": "not_submitted",
"submitted_on": "2026-09-21",
"submission_reference": "string",
"submitted_by": {},
"submitted_at": "2026-09-21T09:30:00Z",
"settled_amount": "string",
"paid_amount": "string",
"payment_difference": "string",
"payments": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_number": "string",
"paid_on": "2026-09-21",
"amount": "string",
"linked_by": {},
"linked_at": "2026-09-21T09:30:00Z"
}
],
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"kind": "annual_summary",
"reference": "string",
"form_id": "string",
"form_version": "string",
"method": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"status": "string",
"currency": "string",
"payable": "string",
"amends_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_number": "string",
"errors": 1,
"warnings": 1,
"computed_at": "2026-09-21T09:30:00Z",
"finalized_at": "2026-09-21T09:30:00Z",
"finalized_by": {},
"result": {
"kind": "annual_summary",
"form": {
"id": "string",
"jurisdiction": "",
"name": "string",
"kind": "annual_summary",
"method": null,
"version": "string",
"currency": "string",
"base_precision": 1,
"tax_precision": 2,
"notes": [
null
],
"limitations": [
null
],
"sources": [
null
]
},
"entity": "string",
"entity_name": "string",
"vat_number": "string",
"tax_number": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"currency": "string",
"method": "string",
"boxes": [
{
"id": null,
"code": null,
"line": null,
"label": null,
"section": null,
"unit": null,
"rate": null,
"amount": null,
"tax": null,
"computed": null,
"lines": null,
"adjustable": null,
"adjusted": null,
"filled": null,
"xml": null,
"by_rate": null
}
],
"rows": [
{
"country_code": null,
"vat_id": null,
"party": null,
"box": null,
"amount": null
}
],
"oss_rows": [
{
"member_state": null,
"supply": null,
"rate": null,
"rate_type": null,
"base": null,
"vat": null
}
],
"oss_corrections": [
{
"period": null,
"member_state": null,
"vat": null
}
],
"activities": [
{
"activity_id": null,
"series": null,
"activity": null,
"rate": null,
"turnover": null,
"tax": null
}
],
"payable": "string",
"reconciliation": {
"accounts": null,
"ledger": null,
"reported": null,
"other_period": null,
"payable": null,
"adjustments": null,
"computation_difference": null,
"tax_fx_difference": null,
"whole_unit_rounding": null,
"unexplained": null,
"settlement": null,
"reconciling_items": null
},
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"issues": [
{
"severity": null,
"code": null,
"message": null,
"details": null
}
],
"lines": 1,
"dataset_version": "string",
"tax_fx": [
{
"series": null,
"currency": null,
"period": null,
"rate": null,
"quotation": null,
"lines": null,
"looked_up": null
}
],
"payment_code": "string",
"payment_due_date": "2026-09-21"
},
"explanation": "string",
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"test_copy": false
}
POST /v1/vat-returns/{return_id}/submitted
Record that a VAT return was submitted
Records that a final return (or one being amended) was submitted to the tax
administration (ESTV, Finanzamt, HMRC) on submitted_on, with the administration's reference
if one was given, and who recorded it. Nothing is sent: the application files no return
electronically, and in a test copy (test_copy) the record is local only. From then on the
period counts as filed (due_status filed) and the reminders of its due date stop. Answers
409 unless the return is final or amending, or when a submission is already recorded
(withdraw it first); 422 for a day in the future or before the period ends.
A person's act: needs the approval policy action finalize_vat_return, which agents are
always denied (403).
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | submitted_v1_vat_returns__return_id__submitted_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | return_id |
string (uuid) | yes | VAT return id. |
Request body
application/json (required): VatSubmitInput
| Field | Type | Required | Description |
|---|---|---|---|
submitted_on |
string (date) | yes | The day the return was submitted to the tax administration: not before the period ends and not in the future. |
submission_reference |
string or null | The tax administration's reference or confirmation number. Optional. (max length 200) |
Example request
curl -X POST "$ERP_URL/v1/vat-returns/3fa85f64-5717-4562-b3fc-2c963f66afa6/submitted" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"submission_reference": "MWST-2026-123",
"submitted_on": "2026-10-20"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | VatReturnOut |
| 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)
{
"filing_status": "not_submitted",
"submitted_on": "2026-09-21",
"submission_reference": "string",
"submitted_by": {},
"submitted_at": "2026-09-21T09:30:00Z",
"settled_amount": "string",
"paid_amount": "string",
"payment_difference": "string",
"payments": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_number": "string",
"paid_on": "2026-09-21",
"amount": "string",
"linked_by": {},
"linked_at": "2026-09-21T09:30:00Z"
}
],
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"kind": "annual_summary",
"reference": "string",
"form_id": "string",
"form_version": "string",
"method": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"status": "string",
"currency": "string",
"payable": "string",
"amends_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_number": "string",
"errors": 1,
"warnings": 1,
"computed_at": "2026-09-21T09:30:00Z",
"finalized_at": "2026-09-21T09:30:00Z",
"finalized_by": {},
"result": {
"kind": "annual_summary",
"form": {
"id": "string",
"jurisdiction": "",
"name": "string",
"kind": "annual_summary",
"method": null,
"version": "string",
"currency": "string",
"base_precision": 1,
"tax_precision": 2,
"notes": [
null
],
"limitations": [
null
],
"sources": [
null
]
},
"entity": "string",
"entity_name": "string",
"vat_number": "string",
"tax_number": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"currency": "string",
"method": "string",
"boxes": [
{
"id": null,
"code": null,
"line": null,
"label": null,
"section": null,
"unit": null,
"rate": null,
"amount": null,
"tax": null,
"computed": null,
"lines": null,
"adjustable": null,
"adjusted": null,
"filled": null,
"xml": null,
"by_rate": null
}
],
"rows": [
{
"country_code": null,
"vat_id": null,
"party": null,
"box": null,
"amount": null
}
],
"oss_rows": [
{
"member_state": null,
"supply": null,
"rate": null,
"rate_type": null,
"base": null,
"vat": null
}
],
"oss_corrections": [
{
"period": null,
"member_state": null,
"vat": null
}
],
"activities": [
{
"activity_id": null,
"series": null,
"activity": null,
"rate": null,
"turnover": null,
"tax": null
}
],
"payable": "string",
"reconciliation": {
"accounts": null,
"ledger": null,
"reported": null,
"other_period": null,
"payable": null,
"adjustments": null,
"computation_difference": null,
"tax_fx_difference": null,
"whole_unit_rounding": null,
"unexplained": null,
"settlement": null,
"reconciling_items": null
},
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"issues": [
{
"severity": null,
"code": null,
"message": null,
"details": null
}
],
"lines": 1,
"dataset_version": "string",
"tax_fx": [
{
"series": null,
"currency": null,
"period": null,
"rate": null,
"quotation": null,
"lines": null,
"looked_up": null
}
],
"payment_code": "string",
"payment_due_date": "2026-09-21"
},
"explanation": "string",
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"test_copy": false
}
POST /v1/vat-returns/{return_id}/submitted/withdraw
Withdraw the recorded submission of a VAT return
Withdraws a submission recorded by mistake, with the reason (kept in the history; the
record is not deleted). The period is not filed any more until a submission is recorded
again. Answers 409 when no submission is recorded or payments are linked (unlink them
first).
A person's act: needs the approval policy action finalize_vat_return, which agents are
always denied (403).
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | withdraw_submitted_v1_vat_returns__return_id__submitted_withdraw_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | return_id |
string (uuid) | yes | VAT return id. |
Request body
application/json (required): VatReasonInput
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | yes | Why, in a few words; kept in the history. (min length 1; max length 500) |
Example request
curl -X POST "$ERP_URL/v1/vat-returns/3fa85f64-5717-4562-b3fc-2c963f66afa6/submitted/withdraw" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"reason": "Recorded on the wrong return"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | VatReturnOut |
| 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)
{
"filing_status": "not_submitted",
"submitted_on": "2026-09-21",
"submission_reference": "string",
"submitted_by": {},
"submitted_at": "2026-09-21T09:30:00Z",
"settled_amount": "string",
"paid_amount": "string",
"payment_difference": "string",
"payments": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entry_number": "string",
"paid_on": "2026-09-21",
"amount": "string",
"linked_by": {},
"linked_at": "2026-09-21T09:30:00Z"
}
],
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"kind": "annual_summary",
"reference": "string",
"form_id": "string",
"form_version": "string",
"method": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"status": "string",
"currency": "string",
"payable": "string",
"amends_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"settlement_entry_number": "string",
"errors": 1,
"warnings": 1,
"computed_at": "2026-09-21T09:30:00Z",
"finalized_at": "2026-09-21T09:30:00Z",
"finalized_by": {},
"result": {
"kind": "annual_summary",
"form": {
"id": "string",
"jurisdiction": "",
"name": "string",
"kind": "annual_summary",
"method": null,
"version": "string",
"currency": "string",
"base_precision": 1,
"tax_precision": 2,
"notes": [
null
],
"limitations": [
null
],
"sources": [
null
]
},
"entity": "string",
"entity_name": "string",
"vat_number": "string",
"tax_number": "string",
"period": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"currency": "string",
"method": "string",
"boxes": [
{
"id": null,
"code": null,
"line": null,
"label": null,
"section": null,
"unit": null,
"rate": null,
"amount": null,
"tax": null,
"computed": null,
"lines": null,
"adjustable": null,
"adjusted": null,
"filled": null,
"xml": null,
"by_rate": null
}
],
"rows": [
{
"country_code": null,
"vat_id": null,
"party": null,
"box": null,
"amount": null
}
],
"oss_rows": [
{
"member_state": null,
"supply": null,
"rate": null,
"rate_type": null,
"base": null,
"vat": null
}
],
"oss_corrections": [
{
"period": null,
"member_state": null,
"vat": null
}
],
"activities": [
{
"activity_id": null,
"series": null,
"activity": null,
"rate": null,
"turnover": null,
"tax": null
}
],
"payable": "string",
"reconciliation": {
"accounts": null,
"ledger": null,
"reported": null,
"other_period": null,
"payable": null,
"adjustments": null,
"computation_difference": null,
"tax_fx_difference": null,
"whole_unit_rounding": null,
"unexplained": null,
"settlement": null,
"reconciling_items": null
},
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"issues": [
{
"severity": null,
"code": null,
"message": null,
"details": null
}
],
"lines": 1,
"dataset_version": "string",
"tax_fx": [
{
"series": null,
"currency": null,
"period": null,
"rate": null,
"quotation": null,
"lines": null,
"looked_up": null
}
],
"payment_code": "string",
"payment_due_date": "2026-09-21"
},
"explanation": "string",
"adjustments": [
{
"account": "1020",
"amount": "120.00",
"box": "410",
"note": "Import VAT paid to customs"
}
],
"test_copy": false
}
Schemas
VatAccountReconciliation
| Field | Type | Required | Description |
|---|---|---|---|
account |
string | yes | VAT account code. |
name |
string | yes | Account name. |
role |
string or null | yes | Account role: vat_output, vat_reverse_charge or vat_input. |
ledger |
string | yes | Posted movement in the period, credit minus debit, without settlement entries. |
reported |
string | yes | What the return takes of it, as on the return: foreign-currency VAT at the official VAT exchange rate. ledger = reported - tax_fx_difference + other_period + unmapped + untagged. |
tax_fx_difference |
string | What the official VAT exchange rates add to the VAT booked on this account (the return at the official rate less the ledger at the booking rate). The differences of the output and input tax accounts can cancel in the amount payable (acquisition tax) while each box differs from its account. (default "0.00") |
|
other_period |
string | VAT booked in this period whose tax point is in another period, less VAT of this period's tax points booked in another period: the date of supply moved it (goods delivered in September and invoiced in October are on the September return, in the October ledger), or the cash basis (an invoice booked in September is on the return of the period it is paid in, pro rata). Explained, not part of unexplained. (default "0.00") |
|
unmapped |
string | yes | Tax-tagged amount that no box takes. |
untagged |
string | yes | Movement without a tax code (for example manual postings). |
VatActivity
Net tax rate method: turnover (gross) and tax per approved activity.
| Field | Type | Required | Description |
|---|---|---|---|
activity_id |
string | yes | The ESTV's five-character activity code. |
series |
string | yes | Net tax rate series. |
activity |
string | yes | Name of the activity. |
rate |
string | yes | Net tax rate in percent. |
turnover |
string | yes | Gross turnover of the activity. |
tax |
string | yes | Tax at the net tax rate. |
VatAdjustment-Input
An amount entered by hand for a box the ledger does not fill (e.g. import VAT).
| Field | Type | Required | Description |
|---|---|---|---|
box |
string | yes | Id of the box to adjust, as in the return's boxes[].id (a Ziffer such as 410 or a Kennzahl). Only boxes that the ledger neither fills nor computes can be adjusted (adjustable is true); others are refused (422). |
amount |
decimal string | yes | Amount to add to the box, as a decimal string in the entity's functional currency, e.g. "120.00". Negative reduces the box. |
account |
string or null | Offset account in the settlement; required for boxes that change the amount payable (e.g. import VAT paid to customs). | |
note |
string or null | Free text explaining the adjustment. Optional. | |
automatic |
boolean | Filled in by the return itself (CA: the Quick Method 1% credit, instalments paid; ES: amounts carried forward, the Modelo 390 from the 303s); replaced on every recompute, so only adjustments entered by hand are kept. (default false) |
VatAdjustment-Output
An amount entered by hand for a box the ledger does not fill (e.g. import VAT).
| Field | Type | Required | Description |
|---|---|---|---|
box |
string | yes | Id of the box to adjust, as in the return's boxes[].id (a Ziffer such as 410 or a Kennzahl). Only boxes that the ledger neither fills nor computes can be adjusted (adjustable is true); others are refused (422). |
amount |
string | yes | Amount to add to the box, as a decimal string in the entity's functional currency, e.g. "120.00". Negative reduces the box. (pattern ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$) |
account |
string or null | Offset account in the settlement; required for boxes that change the amount payable (e.g. import VAT paid to customs). | |
note |
string or null | Free text explaining the adjustment. Optional. | |
automatic |
boolean | Filled in by the return itself (CA: the Quick Method 1% credit, instalments paid; ES: amounts carried forward, the Modelo 390 from the 303s); replaced on every recompute, so only adjustments entered by hand are kept. (default false) |
VatBox
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | Box id, as used in adjustments[].box and .../boxes/{box}/lines, e.g. 303 or kz81. |
code |
string or null | yes | The number printed on the form (Ziffer, Kennzahl); null if unnumbered. |
line |
string or null | yes | Line number on the form, if any. |
label |
string | yes | The official wording, abridged. |
section |
string or null | yes | Section of the form the box is in. |
unit |
string | yes | base (turnover or taxable amount), tax or amount. |
rate |
string or null | yes | The rate printed next to the box, in percent. |
amount |
string | yes | The box's amount in the return's currency, as a decimal string. |
tax |
string or null | yes | For boxes that compute tax, the tax on the amount; else null. |
computed |
boolean | yes | True when the form derives the box from other boxes. |
lines |
integer | yes | Number of journal lines that fill the box (drill down with .../boxes/{box}/lines). |
adjustable |
boolean | yes | True when the box can take a hand-entered adjustment. |
adjusted |
boolean | True when an adjustment is part of the amount. (default false) |
|
filled |
boolean | True when the amount comes from the company's earlier returns rather than from the ledger (Italy: the previous period's credit or carried debt, the interest on quarterly payments, the year's periodic VAT on the annual return). (default false) |
|
xml |
string or null | Element of the eCH-0217 file the box is written to, if any. | |
by_rate |
array of VatRatePart |
Boxes that compute tax: amount and tax per rate. |
VatBoxLine
| Field | Type | Required | Description |
|---|---|---|---|
journal_line_id |
string (uuid) | yes | Id of the journal line that fills the box. |
entry_id |
string (uuid) | yes | Id of its journal entry (GET /v1/journal-entries/{id}). |
entry_number |
string or null | yes | Number of the journal entry. |
entry_date |
string (date) | yes | Posting date of the entry (ISO 8601 date). |
account |
string | yes | Account code of the line. |
account_name |
string | yes | Account name. |
description |
string or null | yes | The line's description. |
document |
string or null | yes | For receivable and payable postings, the document number. |
counterparty |
string or null | yes | Name of the customer or vendor on the line. |
row_key |
string or null | yes | Recapitulative statement: the customer VAT number (country code and id) the line belongs to. |
rule |
string or null | yes | Id of the form rule that routed the line into the box. |
amount |
string | yes | The line's contribution to the box in the return's currency, as a decimal string. |
VatFinalizeInput
| Field | Type | Required | Description |
|---|---|---|---|
posting_date |
string (date) or null | Date of the settlement entry; default the period's last day, or the first open day after it. | |
explanation |
string or null | Required when the VAT accounts hold amounts the return does not explain. (max length 2000) |
VatFinalizeOut
| Field | Type | Required | Description |
|---|---|---|---|
vat_return |
VatReturnOut |
yes | The return after the call: final, or pending_approval when the settlement entry needs a second person. |
settlement |
SubmitResult or null |
yes | The settlement entry's submission: its id, status, number, the policy decision (allow, require_approval, deny) and the approval id when one was requested. Null when nothing needed settling. |
VatForm
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | Form id, e.g. ch.estv.mwst.effective or de.bmf.ustva.2026. |
jurisdiction |
string | Country the form is filed in, e.g. IT. (default "") |
|
name |
string | yes | Official name of the form. |
kind |
"annual_summary" | "oss_return" | "pst_return_bc" | "pst_return_sk" | "qst_return" | "recapitulative_statement" | "rst_return_mb" | "vat_return" |
yes | vat_return, recapitulative_statement (ZM, Modelo 349), qst_return (Quebec), pst_return_bc, pst_return_sk, rst_return_mb, annual_summary (Modelo 390, Dichiarazione IVA) or oss_return (EU One-Stop Shop, union scheme). |
method |
string or null | yes | Filing method the form is for, or null. |
version |
string | yes | Edition of the form or file format it follows. |
currency |
string | yes | Currency the form is filed in; the entity must book in it. |
base_precision |
integer | yes | Decimals of base amounts: 0 means whole units. |
tax_precision |
integer | Decimals of tax amounts and totals: 0 means whole units (FR, IT). (default 2) |
|
notes |
array of string | yes | How the form computes and rounds. |
limitations |
array of string | yes | What the form does not derive from the ledger (enter it as adjustments). |
sources |
array of object | yes | Legal and technical sources of the form. |
VatIssue
| Field | Type | Required | Description |
|---|---|---|---|
severity |
"error" | "info" | "warning" |
yes | error (blocks finalising), warning (review it) or info. |
code |
string | yes | Stable code (erp.contracts.words.VAT_ISSUES lists them all), e.g. unmapped_line, unexplained_difference, computation_difference (with computation_difference_fx when official VAT exchange rates add to it), tax_fx_not_published, missing_uid, missing_account, missing_settlement_account, period_differs_from_settings, earlier_period_unfiled (an earlier period has no final return yet: filing this one is allowed), annual_differs (vs. the final returns). |
message |
string | yes | What was found, in English. |
details |
map of string | The values the message names, for the screens to word it, e.g. {"periods": "2026-Q1, 2026-Q2"} for earlier_period_unfiled. |
VatOssCorrection
A correction in a One-Stop Shop return of the VAT declared for an earlier quarter.
| Field | Type | Required | Description |
|---|---|---|---|
period |
string | yes | The quarter corrected (YYYY-Qn). |
member_state |
string | yes | Member state of consumption (ISO 3166-1 alpha-2). |
vat |
string | yes | Change of the VAT of that quarter in euros (negative: less). |
VatOssRow
A row of the One-Stop Shop return: one member state of consumption, kind of supply and VAT rate.
| Field | Type | Required | Description |
|---|---|---|---|
member_state |
string | yes | Member state of consumption (ISO 3166-1 alpha-2). |
supply |
"goods" | "services" |
yes | goods or services. |
rate |
string | yes | VAT rate of that member state in percent. |
rate_type |
string or null | yes | standard, reduced, ... or null. |
base |
string | yes | Taxable amount in euros. |
vat |
string | yes | VAT due in that member state in euros. |
VatPaymentCandidate
| Field | Type | Required | Description |
|---|---|---|---|
journal_entry_id |
string (uuid) | yes | Id of the posted entry. |
entry_number |
string or null | yes | Its number. |
entry_date |
string (date) | yes | Its date (ISO 8601 date). |
description |
string | yes | Its description. |
source |
string | yes | Its source, such as bank or manual. |
amount |
string | yes | What it puts on the settlement account against the settlement, as a positive decimal string. |
VatPaymentInput
| Field | Type | Required | Description |
|---|---|---|---|
journal_entry_id |
string (uuid) | yes | The posted entry that paid the VAT (or received the refund): of the return's company, dated on or after the settlement, with a line on the settlement account on the side opposite the settlement (GET .../payment-candidates). |
VatPaymentOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | Id of the link (the {payment_id} to unlink it). |
journal_entry_id |
string (uuid) | yes | Id of the posted entry that paid. |
entry_number |
string or null | yes | Number of that entry. |
paid_on |
string (date) | yes | The entry's date: the day it paid (ISO 8601 date). |
amount |
string | yes | What the entry put on the settlement account against the settlement, as a positive decimal string: VAT paid, or for a refund the refund received. |
linked_by |
object | yes | Who linked it (type and id). |
linked_at |
string (date-time) | yes | When it was linked (ISO 8601 timestamp, UTC). |
VatPeriod
| Field | Type | Required | Description |
|---|---|---|---|
filing_status |
"not_submitted" | "paid" | "submitted" |
yes | not_submitted (not final yet, or final but not recorded as submitted), submitted (a person recorded the submission; a return with nothing to pay or refund stays here) or paid (submitted, and the linked payments cover the settled amount). |
submitted_on |
string (date) or null | yes | The day the return was submitted to the tax administration, as a person recorded it (ISO 8601 date); null when not submitted. |
submission_reference |
string or null | yes | The tax administration's reference or confirmation number, or null. |
submitted_by |
object or null | yes | Who recorded the submission (type and id); null when not submitted. |
submitted_at |
string (date-time) or null | yes | When the submission was recorded (ISO 8601 timestamp, UTC), or null. |
settled_amount |
string or null | yes | What the posted settlement put on the settlement account: positive is VAT to pay, negative a refund to receive (a correction: only its difference). Null when nothing was settled (not final yet, nothing to pay, a recapitulative statement). |
paid_amount |
string | yes | What the linked payments cover, signed like settled_amount. |
payment_difference |
string or null | yes | settled_amount minus paid_amount: what is still to pay (or receive); negative when more was paid. Null when nothing was settled. |
payments |
array of VatPaymentOut |
yes | The posted entries linked as paying the return, oldest first. |
period |
string | yes | Period label: 2026-Q3, 2026-08, 2026-H2 or 2026. |
start |
string (date) | yes | First day of the period (ISO 8601 date). |
end |
string (date) | yes | Last day of the period (ISO 8601 date). |
kind |
"annual_summary" | "oss_return" | "pst_return_bc" | "pst_return_sk" | "qst_return" | "recapitulative_statement" | "rst_return_mb" | "vat_return" |
yes | vat_return, recapitulative_statement (ZM, Modelo 349), qst_return (Quebec), pst_return_bc, pst_return_sk, rst_return_mb, annual_summary (Modelo 390, Dichiarazione IVA) or oss_return (EU One-Stop Shop, union scheme). |
return_id |
string (uuid) or null | yes | Id of the period's live return, or null when there is none. |
reference |
string or null | yes | Reference of that return, or null. |
status |
string | yes | The return's status (draft, pending_approval, final, amending) or open when the period has no live return. |
payable |
string or null | yes | The return's amount payable as a decimal string, or null. |
due_date |
string (date) or null | When the return is due to be filed and paid (ISO 8601 date), by the country's rule (CH: 60 days after the period; DE: the 10th of the next month, a month later with the permanent extension, a recapitulative statement on the 25th day, moved to the Monday when it falls on a weekend; GB: one month and seven days, two months after the year under the Annual Accounting Scheme); null when no rule is known for the country or period. | |
payment_due_date |
string (date) or null | When the period's VAT is due to be paid (ISO 8601 date): the due_date, except in Italy, where it is paid before the return is filed (the 16th of the next month; quarterly 16 May, 20 August, 16 November and, with the annual balance, 16 March); null when nothing is paid on the return (an annual return) or no rule is known. |
|
due_status |
"due_soon" | "filed" | "overdue" | "unknown" | "upcoming" |
filed (a return of the period was recorded as submitted to the tax administration; final alone is not filed), upcoming, due_soon (due within 14 days), overdue (past the due date and not filed) or unknown (no due date known). (default "unknown") |
|
payment_overdue |
boolean | true when the due date has passed and the live return's VAT to pay is not covered by linked payments (shown as a warning; due_status follows the filing only). (default false) |
VatRatePart
| Field | Type | Required | Description |
|---|---|---|---|
rate |
string | yes | Tax rate in percent. |
amount |
string | yes | Turnover at that rate. |
tax |
string | yes | Tax on that turnover. |
VatReasonInput
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | yes | Why, in a few words; kept in the history. (min length 1; max length 500) |
VatRecomputeInput
| Field | Type | Required | Description |
|---|---|---|---|
adjustments |
array of VatAdjustment-Input or null |
Replaces the adjustments; omit to keep them. |
VatReconciliation
Movements on the VAT accounts (credit minus debit) against the return.
| Field | Type | Required | Description |
|---|---|---|---|
accounts |
array of VatAccountReconciliation |
yes | Per VAT account: ledger movement and what the return took. |
ledger |
string | yes | Total ledger movement of the VAT accounts, credit minus debit. |
reported |
string | yes | Total of the VAT booked that the return reports (at the booking rate: the per-account reported less its tax_fx_difference), which the settlement clears. |
other_period |
string | Total of the accounts' other_period. VAT booked in this period whose tax point is in another period, less VAT of this period's tax points booked in another period: the date of supply moved it (goods delivered in September and invoiced in October are on the September return, in the October ledger), or the cash basis (an invoice booked in September is on the return of the period it is paid in, pro rata). Explained, not part of unexplained. (default "0.00") |
|
payable |
string | yes | The return's amount payable. |
adjustments |
string | yes | Effect of the hand-entered adjustments on the amount payable. |
computation_difference |
string | yes | Amount payable minus adjustments minus reported: the form computes tax from the turnover while the ledger holds each document's VAT (under the net tax rate method, the net tax rate difference). The settlement clears it to the account with the role rounding or vat_net_rate_difference. |
tax_fx_difference |
string | Part of the computational difference from foreign-currency VAT: the return takes it at the official VAT exchange rate, the ledger at the booking rate. (default "0.00") |
|
whole_unit_rounding |
string | Part of the computational difference from declaring amounts in whole currency units: the amount payable minus the same return computed to the cent. Germany's UStVA drops the cents of each base and computes the tax from it; France's CA3 and Italy's Dichiarazione IVA round every amount to the euro. Zero for a form in cents (Switzerland, Spain, Canada, the Italian LIPE). (default "0.00") |
|
unexplained |
string | yes | Unmapped plus untagged amounts. Finalising needs it to be zero or an explanation. |
settlement |
array of map of string | Implied settlement lines: account, signed debit. | |
reconciling_items |
array of ReconcilingItem |
Revenue and expenses in the ledger that no turnover box of the return contains because the lines are outside the scope of VAT (a labelled reconciling item between ledger revenue and the VAT turnover). Empty when there are none. |
VatReturnInput
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Entity code, e.g. CH01 or DE01 (GET /v1/entities). |
kind |
"annual_summary" | "oss_return" | "pst_return_bc" | "pst_return_sk" | "qst_return" | "recapitulative_statement" | "rst_return_mb" | "vat_return" |
vat_return, recapitulative_statement (ZM, Modelo 349), qst_return (Quebec), pst_return_bc, pst_return_sk, rst_return_mb, annual_summary (Modelo 390, Dichiarazione IVA) or oss_return (EU One-Stop Shop, union scheme). Default vat_return; annual_summary takes the period YYYY. (default "vat_return") |
|
period |
string | yes | '2026-08', '2026-Q3', '2026-H2' or '2026'. |
adjustments |
array of VatAdjustment-Input |
Hand-entered amounts for boxes the ledger cannot fill (import VAT, Einlageentsteuerung and similar). Default: none. |
VatReturnOut
| Field | Type | Required | Description |
|---|---|---|---|
filing_status |
"not_submitted" | "paid" | "submitted" |
yes | not_submitted (not final yet, or final but not recorded as submitted), submitted (a person recorded the submission; a return with nothing to pay or refund stays here) or paid (submitted, and the linked payments cover the settled amount). |
submitted_on |
string (date) or null | yes | The day the return was submitted to the tax administration, as a person recorded it (ISO 8601 date); null when not submitted. |
submission_reference |
string or null | yes | The tax administration's reference or confirmation number, or null. |
submitted_by |
object or null | yes | Who recorded the submission (type and id); null when not submitted. |
submitted_at |
string (date-time) or null | yes | When the submission was recorded (ISO 8601 timestamp, UTC), or null. |
settled_amount |
string or null | yes | What the posted settlement put on the settlement account: positive is VAT to pay, negative a refund to receive (a correction: only its difference). Null when nothing was settled (not final yet, nothing to pay, a recapitulative statement). |
paid_amount |
string | yes | What the linked payments cover, signed like settled_amount. |
payment_difference |
string or null | yes | settled_amount minus paid_amount: what is still to pay (or receive); negative when more was paid. Null when nothing was settled. |
payments |
array of VatPaymentOut |
yes | The posted entries linked as paying the return, oldest first. |
id |
string (uuid) | yes | Return id (uuid); the {return_id} of the other operations. |
entity |
string | yes | Code of the entity the return is for. |
kind |
"annual_summary" | "oss_return" | "pst_return_bc" | "pst_return_sk" | "qst_return" | "recapitulative_statement" | "rst_return_mb" | "vat_return" |
yes | vat_return, recapitulative_statement (ZM, Modelo 349), qst_return (Quebec), pst_return_bc, pst_return_sk, rst_return_mb, annual_summary (Modelo 390, Dichiarazione IVA) or oss_return (EU One-Stop Shop, union scheme). |
reference |
string | yes | Human-readable reference, e.g. CH01-VAT-2026-Q3; a correction appends /n. |
form_id |
string | yes | Id of the official form the return was computed with, e.g. ch.estv.mwst.effective. |
form_version |
string | yes | Version of that form (the ESTV or BMF edition it follows). |
method |
string or null | yes | Filing method of the form (effective, net_tax_rate) or null. |
period |
string | yes | Period label: 2026-08, 2026-Q3, 2026-H2 or 2026. |
start |
string (date) | yes | First day of the period (ISO 8601 date). |
end |
string (date) | yes | Last day of the period (ISO 8601 date). |
status |
string | yes | draft (computed, can be recomputed, discarded or finalised), pending_approval (the settlement entry waits for a second person), final (settled, the period's VAT postings are locked), amending (a final return whose correction is open), superseded (replaced by a final correction) or discarded. |
currency |
string | yes | Currency of the return and its amounts: the entity's functional currency. |
payable |
string or null | yes | Amount payable to the tax authority as a decimal string; negative is a refund (credit). Null for a recapitulative statement. |
amends_id |
string (uuid) or null | yes | For a correction, the id of the final return it corrects; else null. |
settlement_entry_id |
string (uuid) or null | yes | Id of the settlement journal entry, once finalising created it; else null. |
settlement_entry_number |
string or null | yes | Number of the settlement entry once it is posted; else null. |
errors |
integer | yes | Number of issues with severity error; a return with errors cannot be finalised. |
warnings |
integer | yes | Number of issues with severity warning. |
computed_at |
string (date-time) | yes | When the return was last computed (ISO 8601 timestamp, UTC). |
finalized_at |
string (date-time) or null | yes | When the return became final (ISO 8601 timestamp, UTC); null before. |
finalized_by |
object or null | yes | The actor that finalised it, as stored in the audit trail (type and id); null before. |
result |
VatReturnResult |
yes | The computed return: form, boxes, rows, reconciliation, issues. |
explanation |
string or null | yes | The explanation given when finalising, or null. |
adjustments |
array of VatAdjustment-Output |
yes | The hand-entered adjustments the return was computed with. |
test_copy |
boolean | true in a test copy (POST /v1/test-copies): recording a submission there is a local record only, and nothing is ever filed with a tax administration. (default false) |
VatReturnResult
| Field | Type | Required | Description |
|---|---|---|---|
kind |
"annual_summary" | "oss_return" | "pst_return_bc" | "pst_return_sk" | "qst_return" | "recapitulative_statement" | "rst_return_mb" | "vat_return" |
yes | vat_return, recapitulative_statement (ZM, Modelo 349), qst_return (Quebec), pst_return_bc, pst_return_sk, rst_return_mb, annual_summary (Modelo 390, Dichiarazione IVA) or oss_return (EU One-Stop Shop, union scheme). |
form |
VatForm |
yes | The official form the return follows. |
entity |
string | yes | Entity code. |
entity_name |
string | yes | Entity name. |
vat_number |
string or null | yes | The entity's VAT number (Switzerland: UID). |
tax_number |
string or null | yes | Germany: the Steuernummer from the VAT settings. |
period |
string | yes | Period label, e.g. 2026-Q3. |
start |
string (date) | yes | First day of the period (ISO 8601 date). |
end |
string (date) | yes | Last day of the period (ISO 8601 date). |
currency |
string | yes | Currency of all amounts: the entity's functional currency. |
method |
string or null | yes | Filing method of the form, or null. |
boxes |
array of VatBox |
yes | Every box of the form with its amount, in form order. |
rows |
array of VatStatementRow |
Recapitulative statement: one row per customer VAT number and kind of supply. Empty for a VAT return. | |
oss_rows |
array of VatOssRow |
One-Stop Shop return: taxable amount and VAT per member state of consumption, kind of supply and rate. Empty for other returns. | |
oss_corrections |
array of VatOssCorrection |
One-Stop Shop return: corrections of earlier quarters (credit notes of sales of those quarters), per quarter and member state, apart from the quarter's supplies (Directive 2006/112/EC art. 369g(4)); they count in the VAT payable. | |
activities |
array of VatActivity |
Net tax rate method: turnover and tax per approved activity. | |
payable |
string or null | yes | Amount payable as a decimal string; negative is a credit. Null for a recapitulative statement. |
reconciliation |
VatReconciliation or null |
yes | The VAT accounts against the return; null for a recapitulative statement. |
adjustments |
array of VatAdjustment-Output |
yes | The hand-entered adjustments that were applied. |
issues |
array of VatIssue |
yes | Findings of the computation. A return with an error issue cannot be finalised. |
lines |
integer | yes | Number of distinct journal lines the return draws on. |
dataset_version |
string | yes | Version of the reference data (forms, rates) used. |
tax_fx |
array of VatTaxFxRate |
Official VAT exchange rates of the foreign-currency lines (CH: ESTV/BAZG, DE: BMF). | |
payment_code |
string or null | Italy: the F24 tax code (codice tributo) the amount payable is paid with: 6001 to 6012 for a month, 6031 to 6033 for a quarter, 6099 for the fourth quarter paid with the annual balance; null elsewhere or when nothing is to pay. | |
payment_due_date |
string (date) or null | Italy: the day the payment_code payment is due (ISO 8601 date), before the return is filed; null when there is no payment code. |
VatReturnSummary
| Field | Type | Required | Description |
|---|---|---|---|
filing_status |
"not_submitted" | "paid" | "submitted" |
yes | not_submitted (not final yet, or final but not recorded as submitted), submitted (a person recorded the submission; a return with nothing to pay or refund stays here) or paid (submitted, and the linked payments cover the settled amount). |
submitted_on |
string (date) or null | yes | The day the return was submitted to the tax administration, as a person recorded it (ISO 8601 date); null when not submitted. |
submission_reference |
string or null | yes | The tax administration's reference or confirmation number, or null. |
submitted_by |
object or null | yes | Who recorded the submission (type and id); null when not submitted. |
submitted_at |
string (date-time) or null | yes | When the submission was recorded (ISO 8601 timestamp, UTC), or null. |
settled_amount |
string or null | yes | What the posted settlement put on the settlement account: positive is VAT to pay, negative a refund to receive (a correction: only its difference). Null when nothing was settled (not final yet, nothing to pay, a recapitulative statement). |
paid_amount |
string | yes | What the linked payments cover, signed like settled_amount. |
payment_difference |
string or null | yes | settled_amount minus paid_amount: what is still to pay (or receive); negative when more was paid. Null when nothing was settled. |
payments |
array of VatPaymentOut |
yes | The posted entries linked as paying the return, oldest first. |
id |
string (uuid) | yes | Return id (uuid); the {return_id} of the other operations. |
entity |
string | yes | Code of the entity the return is for. |
kind |
"annual_summary" | "oss_return" | "pst_return_bc" | "pst_return_sk" | "qst_return" | "recapitulative_statement" | "rst_return_mb" | "vat_return" |
yes | vat_return, recapitulative_statement (ZM, Modelo 349), qst_return (Quebec), pst_return_bc, pst_return_sk, rst_return_mb, annual_summary (Modelo 390, Dichiarazione IVA) or oss_return (EU One-Stop Shop, union scheme). |
reference |
string | yes | Human-readable reference, e.g. CH01-VAT-2026-Q3; a correction appends /n. |
form_id |
string | yes | Id of the official form the return was computed with, e.g. ch.estv.mwst.effective. |
form_version |
string | yes | Version of that form (the ESTV or BMF edition it follows). |
method |
string or null | yes | Filing method of the form (effective, net_tax_rate) or null. |
period |
string | yes | Period label: 2026-08, 2026-Q3, 2026-H2 or 2026. |
start |
string (date) | yes | First day of the period (ISO 8601 date). |
end |
string (date) | yes | Last day of the period (ISO 8601 date). |
status |
string | yes | draft (computed, can be recomputed, discarded or finalised), pending_approval (the settlement entry waits for a second person), final (settled, the period's VAT postings are locked), amending (a final return whose correction is open), superseded (replaced by a final correction) or discarded. |
currency |
string | yes | Currency of the return and its amounts: the entity's functional currency. |
payable |
string or null | yes | Amount payable to the tax authority as a decimal string; negative is a refund (credit). Null for a recapitulative statement. |
amends_id |
string (uuid) or null | yes | For a correction, the id of the final return it corrects; else null. |
settlement_entry_id |
string (uuid) or null | yes | Id of the settlement journal entry, once finalising created it; else null. |
settlement_entry_number |
string or null | yes | Number of the settlement entry once it is posted; else null. |
errors |
integer | yes | Number of issues with severity error; a return with errors cannot be finalised. |
warnings |
integer | yes | Number of issues with severity warning. |
computed_at |
string (date-time) | yes | When the return was last computed (ISO 8601 timestamp, UTC). |
finalized_at |
string (date-time) or null | yes | When the return became final (ISO 8601 timestamp, UTC); null before. |
finalized_by |
object or null | yes | The actor that finalised it, as stored in the audit trail (type and id); null before. |
VatSettingsOut
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Entity code. |
country |
string | yes | Entity country (ISO 3166-1 alpha-2); decides which methods and forms apply. |
vat_number |
string or null | yes | The entity's VAT number (Switzerland: the UID, CHE-123.456.788 MWST, which the eCH-0217 file needs). |
subdivision |
string or null | The entity's state or province (CA-QC). |
|
configured |
boolean | yes | False until the settings are saved: defaults apply. |
registered_from |
string (date) or null | The day the VAT registration takes effect: no return is due for a period that ends before it. Null: registered since the books start. | |
method |
"effective" | "net_tax_rate" | "quick" | "standard" |
yes | effective, net_tax_rate (Switzerland), quick (Canada) or standard. |
period |
"half_year" | "month" | "quarter" | "two_months" | "year" |
yes | Return period: month, two_months (the Norwegian termin), quarter, half_year or year. |
basis |
"cash" | "invoice" |
When VAT is due: invoice (with the invoice, or the date of supply where the law says so) or cash (when paid: the part of a document a payment, credit note or cash discount settles is reported in the period of the settlement, pro rata). (default "invoice") |
|
cash_basis_law |
string or null | The law that allows the cash basis in the entity's country (CH, DE, FR, GB, ES, IT); null where it is not offered. | |
cash_basis_condition |
string or null | The law's condition to use the cash basis (a turnover limit, an application), in the law's language; the company checks it, the ERP does not. | |
cash_basis_purchases |
boolean | Under the cash basis input VAT is reclaimed when the bill is paid too (CH, GB, ES, IT); false: it stays with the bill (DE § 15 UStG, FR; in France the VAT on services bought from a French supplier is deducted when paid on either basis, CGI art. 271). (default false) |
|
cash_basis_open |
integer | How many posted documents on the cash basis are not settled in full: their VAT is reported when they are, whatever the basis is changed to (a change of basis settles nothing; the law may ask for the outstanding VAT to be accounted for on leaving the cash basis, e.g. UK VAT Regulations 1995 reg. 64, which is booked by hand). French services bills, deducted when paid on either basis, do not count. (default 0) |
|
recapitulative_period |
"month" | "quarter" or null |
yes | Period of the recapitulative statement (ZM, 349): month, quarter or null. |
tax_number |
string or null | yes | Germany: the Steuernummer, or null. |
net_tax_rates |
array of VatNetTaxRate |
yes | The approved net tax rates (net tax rate method), else empty. |
settlement_account |
string or null | yes | Code of the settlement account named in the settings; null means the account with the role vat_settlement is used. |
tax_fx |
"daily" | "ecb_daily" | "monthly" or null |
Method for official VAT exchange rates (None: not applicable). | |
tax_fx_booking |
boolean | True when foreign-currency documents are booked at the official VAT rate. (default false) |
|
filing_extension |
boolean | DE: true when the tax office granted the permanent extension (Dauerfristverlängerung): each advance return is due one month later. (default false) |
|
registrations |
array of string | CA: the provinces besides its own whose sales tax (QST, PST, RST) the entity collects, e.g. CA-QC (which adds the QST return). |
|
provincial_periods |
map of "half_year" | "month" | "quarter" | "two_months" | "year" |
CA: the filing period of each province's sales tax return where it differs from period, e.g. {"CA-BC": "month"}. |
|
quick_method |
"resale" | "services" or null |
CA, method quick: the remittance rate of a business buying goods for resale (resale) or of a service provider (services); else null. |
|
oss_available |
boolean | The company is established in the EU, so it can register for the One-Stop Shop (oss, oss_destination). (default false) |
|
oss |
boolean | EU: the company is registered for the One-Stop Shop (OSS) union scheme: it files the quarterly OSS return (kind oss_return) for its sales to consumers in other member states. (default false) |
|
oss_from |
string (date) or null | EU: the day the OSS registration takes effect (Implementing Regulation 282/2011 art. 57d); no OSS return is due for a quarter that ends before it. | |
oss_destination |
boolean | EU: the company opted to charge the customer's VAT on distance sales of goods and electronic services to consumers in other member states even below the EUR 10,000 threshold (Directive 2006/112/EC art. 59c(3)). (default false) |
VatStatementRow
A recapitulative statement row: one customer VAT number and kind of supply.
| Field | Type | Required | Description |
|---|---|---|---|
country_code |
string | yes | Country prefix of the customer's VAT number (two letters). |
vat_id |
string | yes | The customer's VAT number without the country prefix. |
party |
string or null | yes | Name of the customer with that VAT number, or null. |
box |
string | yes | Box id of the supply kind: goods, services or triangulation. |
amount |
string | yes | Total supplies as a decimal string (whole euros on the ZM); 0 cancels a row in a ZM correction, while a 349 sustitutiva repeats every row. |
VatSubmitInput
| Field | Type | Required | Description |
|---|---|---|---|
submitted_on |
string (date) | yes | The day the return was submitted to the tax administration: not before the period ends and not in the future. |
submission_reference |
string or null | The tax administration's reference or confirmation number. Optional. (max length 200) |
VatTaxFxRate
An official VAT exchange rate the return converted foreign-currency lines at.
| Field | Type | Required | Description |
|---|---|---|---|
series |
string | yes | Official rate series, e.g. de-vat-monthly. |
currency |
string | yes | Foreign currency the rate converts. |
period |
string or null | yes | Month of a monthly rate, or the daily rate's date. |
rate |
string | yes | The rate as a decimal string. |
quotation |
string | yes | How the rate is quoted (units of which currency per unit of which). |
lines |
integer | yes | Number of journal lines converted with it. |
looked_up |
boolean | yes | The rate was not published when the documents were posted; the return looked it up when it was computed. |