Setup
Legal entities, charts of accounts, fiscal years and periods.
| Operation | Summary | Scope | Idempotency-Key |
|---|---|---|---|
GET /v1/charts |
List chart templates | read |
|
GET /v1/entities |
List Entities | read |
|
POST /v1/entities |
Create an entity | admin |
required |
GET /v1/entities/{entity} |
Get Entity | read |
|
PATCH /v1/entities/{entity} |
Update Entity | admin |
|
GET /v1/entities/{entity}/accounts |
List Accounts | read |
|
POST /v1/entities/{entity}/accounts |
Add an account | admin |
|
PATCH /v1/entities/{entity}/accounts/{code} |
Change an account | admin |
|
GET /v1/entities/{entity}/brand |
Read a company's brand | read |
|
PATCH /v1/entities/{entity}/brand |
Change a company's colours | admin |
|
GET /v1/entities/{entity}/brand/logo |
Download a company's logo | read |
|
POST /v1/entities/{entity}/brand/logo |
Upload a company's logo | admin |
|
DELETE /v1/entities/{entity}/brand/logo |
Remove a company's logo | admin |
|
GET /v1/entities/{entity}/fiscal-years |
List Fiscal Years | read |
|
POST /v1/entities/{entity}/fiscal-years |
Open a fiscal year | admin |
|
GET /v1/entities/{entity}/periods |
List Periods | read |
|
POST /v1/entities/{entity}/role-accounts |
Add Role Accounts | admin |
required |
GET /v1/industries |
List the industries a company can choose | read |
|
GET /v1/setup |
Tell whether the organization waits for its setup | read |
|
POST /v1/setup |
Set up the organization: Production, then Test | admin |
|
POST /v1/setup/companies |
Add a further company from the setup wizard's answers | admin |
required |
GET /v1/setup/options |
List what the setup wizard offers per country | read |
GET /v1/charts
List chart templates
The chart-of-accounts templates a new entity can start from: id is the value of
chart in POST /v1/entities (e.g. ch_kmu, the Swiss KMU chart; ifrs_generic) and
countries those whose standard chart it is (marked standard for country; provisioning
starts an entity from it). POST /v1/entities without chart uses ifrs_generic.
| Required scope | read (admin implies every scope) |
| Operation id | charts_v1_charts_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| query | country |
string or null | ISO country code. (pattern ^[A-Z]{2}$) |
Example request
curl "$ERP_URL/v1/charts" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of ChartTemplate |
| 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)
[
{
"id": "string",
"name": "string",
"countries": [
"string"
],
"standard": false
}
]
GET /v1/entities
List Entities
All legal entities of the organization, ordered by code. The code is what the other
endpoints take as {entity}.
| Required scope | read (admin implies every scope) |
| Operation id | list_entities_v1_entities_get |
Example request
curl "$ERP_URL/v1/entities" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of EntityOut |
| 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)
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"code": "string",
"name": "string",
"country": "string",
"subdivision": "string",
"functional_currency": "string",
"fiscal_year_start_month": 1,
"framework": "string",
"vat_number": "string",
"street": "string",
"building_number": "string",
"postal_code": "string",
"town": "string",
"contact_name": "string",
"contact_phone": "string",
"contact_email": "string",
"electronic_address": "string",
"electronic_address_scheme": "string",
"posting_approval_limit": "string",
"industry": "general",
"time_zone": "string"
}
]
POST /v1/entities
Create an entity
Create a legal entity with its chart of accounts, and optionally its fiscal years.
The accounts are copied from the chart template chart (see GET /v1/charts, which marks
the country's standard chart); the template must carry every role the services need.
fiscal_years lists the years whose periods are opened at once, on fiscal_calendar.
Refused with 409 when the entity code is taken, in a test copy (company_in_test_copy: it
takes its companies from Production; add the company there and make a new copy), and in an
organization provisioned without a company, which waits for its administrator's setup
(setup_not_done: POST /v1/setup makes the first company, then Test); 422 for an unknown
chart or an invalid calendar, and 403 when the approval policy denies manage_accounts
(always for agents).
| Required scope | admin (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | create_entity_v1_entities_post |
Request body
application/json (required): EntityInput
| Field | Type | Required | Description |
|---|---|---|---|
street |
string or null | Street of the entity's address, at most 70 characters. (max length 70) | |
building_number |
string or null | Building number of the address, at most 16 characters. (max length 16) | |
postal_code |
string or null | Postal code of the address, at most 16 characters. (max length 16) | |
town |
string or null | Town of the address, at most 35 characters. (max length 35) | |
contact_name |
string or null | Seller contact (BT-41). | |
contact_phone |
string or null | Seller contact phone (BT-42). | |
contact_email |
string or null | Seller contact email (BT-43). | |
electronic_address |
string or null | Seller electronic address (BT-34); defaults to contact_email. | |
electronic_address_scheme |
string or null | EAS code, e.g. EM (email), 9930 (German VAT id), 0204. | |
code |
string | yes | Short unique code, 2 to 20 characters of A-Z, 0-9, _ and -, e.g. CH01; it names the entity in every URL. (pattern ^[A-Z0-9_-]{2,20}$) |
name |
string | yes | Legal name, e.g. Demo AG. |
country |
string | yes | ISO 3166-1 alpha-2 country code, e.g. CH or DE; decides the VAT rules. (pattern ^[A-Z]{2}$) |
subdivision |
string or null | ISO 3166-2 code of the state or canton, e.g. DE-BY; null when omitted. (pattern ^[A-Z]{2}-[A-Z0-9]{1,3}$) |
|
functional_currency |
string or null | ISO 4217 code of the currency the books are kept in, e.g. CHF. Cannot be changed later. Omitted: the currency of its country (422 entity_currency_needed where that is not known). (pattern ^[A-Z]{3}$) |
|
fiscal_year_start_month |
integer | Month the fiscal year starts, 1 to 12; default 1 (January). (min 1; max 12; default 1) |
|
framework |
"ifrs" | "local" | "us_gaap" |
Accounting framework: ifrs (default), us_gaap or local. (default "ifrs") |
|
vat_number |
string or null | The entity's VAT number, e.g. CHE-123.456.788 MWST or DE123456788; null when omitted. For a Swiss or German entity its format is checked (422); it is printed on invoices and is required on them when the entity is VAT-registered. |
|
chart |
string | Chart template id (GET /v1/charts), e.g. ch_kmu; default ifrs_generic. GET /v1/charts?country= marks the country's standard chart, which the operator's provisioning uses. (default "ifrs_generic") |
|
fiscal_years |
array of integer | Years to open periods for. | |
fiscal_calendar |
FiscalCalendar or null |
Calendar for those years (default: twelve calendar months). | |
industry |
"ecommerce" | "general" | "hospitality" | "manufacturing" | "retail" | "services" | "trade" |
The company's industry (GET /v1/industries): general, hospitality, services, trade, retail, ecommerce or manufacturing. It puts the screens of the industry first in the app's menu and on its Home; it switches nothing off, and every operation works the same in every industry. Default general. (default "general") |
Example request
curl -X POST "$ERP_URL/v1/entities" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"building_number": "10",
"chart": "ch_kmu",
"code": "CH02",
"country": "CH",
"fiscal_years": [
2026
],
"functional_currency": "CHF",
"name": "Demo Services AG",
"postal_code": "8001",
"street": "Bahnhofstrasse",
"town": "Zurich",
"vat_number": "CHE-123.456.788 MWST"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | EntityOut |
| 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)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"code": "string",
"name": "string",
"country": "string",
"subdivision": "string",
"functional_currency": "string",
"fiscal_year_start_month": 1,
"framework": "string",
"vat_number": "string",
"street": "string",
"building_number": "string",
"postal_code": "string",
"town": "string",
"contact_name": "string",
"contact_phone": "string",
"contact_email": "string",
"electronic_address": "string",
"electronic_address_scheme": "string",
"posting_approval_limit": "string",
"industry": "general",
"time_zone": "string"
}
GET /v1/entities/{entity}
Get Entity
One entity with its legal data, address, contact and e-invoicing address, its posting
approval limit and a request to raise or remove it that waits (posting_limit_request). The
path takes the entity code or its id.
| Required scope | read (admin implies every scope) |
| Operation id | get_entity_v1_entities__entity__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" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | EntityDetailOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"code": "string",
"name": "string",
"country": "string",
"subdivision": "string",
"functional_currency": "string",
"fiscal_year_start_month": 1,
"framework": "string",
"vat_number": "string",
"street": "string",
"building_number": "string",
"postal_code": "string",
"town": "string",
"contact_name": "string",
"contact_phone": "string",
"contact_email": "string",
"electronic_address": "string",
"electronic_address_scheme": "string",
"posting_approval_limit": "string",
"industry": "general",
"posting_limit_request": {
"approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"current": "string",
"proposed": "string",
"requested_by": {},
"requested_at": "2026-09-21T09:30:00Z"
},
"time_zone": "string"
}
PATCH /v1/entities/{entity}
Update Entity
Change an entity's name, VAT number, address, contact or e-invoicing address (the
seller on XRechnung). Only the fields sent with a value change; null or omitted leaves a
field as it is. Code, country, currency and framework cannot be changed. A Swiss or German
VAT number must have the format of a UID or USt-IdNr (422). Refused with 403
when the approval policy denies the action manage_accounts. Audited as entity.update.
posting_approval_limit is the exception: null removes it. Only a person with the admin
scope changes it; a lower limit applies at once, while raising or removing it waits for a
second person (policy action change_posting_limit): the answer then keeps the old limit and
names the request in posting_limit_request, approved under Approvals.
| Required scope | admin (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | update_entity_v1_entities__entity__patch |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
Request body
application/json (required): EntityUpdate
| Field | Type | Required | Description |
|---|---|---|---|
street |
string or null | Street of the address, at most 70 characters; omit or null to keep it. (max length 70) | |
building_number |
string or null | Building number, at most 16 characters; omit or null to keep it. (max length 16) | |
postal_code |
string or null | Postal code, at most 16 characters; omit or null to keep it. (max length 16) | |
town |
string or null | Town, at most 35 characters; omit or null to keep it. (max length 35) | |
contact_name |
string or null | Seller contact (BT-41). | |
contact_phone |
string or null | Seller contact phone (BT-42). | |
contact_email |
string or null | Seller contact email (BT-43). | |
electronic_address |
string or null | Seller electronic address (BT-34); defaults to contact_email. | |
electronic_address_scheme |
string or null | EAS code, e.g. EM (email), 9930 (German VAT id), 0204. | |
name |
string or null | New legal name; omit or null to keep it. | |
vat_number |
string or null | New VAT number (format checked for a Swiss or German entity, 422); omit or null to keep it. | |
industry |
"ecommerce" | "general" | "hospitality" | "manufacturing" | "retail" | "services" | "trade" or null |
The company's industry (GET /v1/industries): general, hospitality, services, trade, retail, ecommerce or manufacturing. It puts the screens of the industry first in the app's menu and on its Home; it switches nothing off, and every operation works the same in every industry. Omit or null to keep it; audited with the other changes. |
|
posting_approval_limit |
decimal string or null | The posting approval limit in the functional currency: whatever a person posts (a manual journal, a bill, an invoice, a payment, a refund, a VAT settlement, a revaluation, a depreciation run) whose functional amount is above it waits for a second person, and so does its reversal. A new company starts at about CHF 100,000 in its currency as a round figure (100000.00 CHF, EUR, USD, GBP or CAD, 1000000.00 SEK, 15000000 JPY). Omit it to keep the limit; null removes it (a one-person company). Only a person with the admin scope changes it (403 posting_limit_admin_only). A lower limit applies at once; raising or removing it waits for a second person (policy action change_posting_limit): the answer keeps the old limit and names the request in posting_limit_request (409 posting_limit_change_waiting while one waits, until it is decided or its requester withdraws it with POST /v1/approvals/{approval_id}/withdraw). A person alone in the organization (one active person, nobody else disabled or demoted in the last 30 days) changes it at once. Audited as entity.posting_limit with the value before and after (and alone when it applied so). (greater than 0) |
Example request
curl -X PATCH "$ERP_URL/v1/entities/CH01" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"building_number": "5",
"street": "Seestrasse",
"town": "Zurich"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | EntityDetailOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"code": "string",
"name": "string",
"country": "string",
"subdivision": "string",
"functional_currency": "string",
"fiscal_year_start_month": 1,
"framework": "string",
"vat_number": "string",
"street": "string",
"building_number": "string",
"postal_code": "string",
"town": "string",
"contact_name": "string",
"contact_phone": "string",
"contact_email": "string",
"electronic_address": "string",
"electronic_address_scheme": "string",
"posting_approval_limit": "string",
"industry": "general",
"posting_limit_request": {
"approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"current": "string",
"proposed": "string",
"requested_by": {},
"requested_at": "2026-09-21T09:30:00Z"
},
"time_zone": "string"
}
GET /v1/entities/{entity}/accounts
List Accounts
The entity's chart of accounts ordered by code, inactive accounts included
(active). Control accounts (is_control) take postings only from their subledger.
| Required scope | read (admin implies every scope) |
| Operation id | list_accounts_v1_entities__entity__accounts_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| query | q |
string or null | Keep accounts whose code starts with this text or whose name, or a translation of it, contains it (case-insensitive); omit for the whole chart. |
Example request
curl "$ERP_URL/v1/entities/CH01/accounts" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of AccountOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"code": "string",
"name": "string",
"type": "string",
"report_line": "string",
"role": "string",
"is_control": true,
"currency": "string",
"active": true,
"vat_recovery_percent": "string"
}
]
POST /v1/entities/{entity}/accounts
Add an account
Add an account to the entity's chart. Refused with 409 when the code is taken, with 422
when the number is not one word, the name is empty or more than one line, or the report line
is unknown or holds another type of account (pl.opex.other holds expense accounts, never
revenue), and with 403 when the approval policy denies the action manage_accounts (always
for agents). An
account with a role is the account the services use for that purpose, e.g. bank.
Audited as account.create.
| Required scope | admin (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | add_account_v1_entities__entity__accounts_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
Request body
application/json (required): AccountInput
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | yes | Account number, unique in the entity, e.g. 6510. |
name |
string | yes | Account name, e.g. Telefon und Internet. |
type |
"asset" | "equity" | "expense" | "liability" | "revenue" |
yes | asset, liability, equity, revenue or expense; decides the normal balance side and which report it appears in. |
report_line |
string | yes | Where the account appears in the statements, as a dotted key such as pl.opex.other, pl.revenue or bs.assets.current.cash. Use a line the chart already uses for the same type: each line holds one type of account (422 otherwise). |
role |
string or null | The purpose the services use the account for, e.g. bank, ar_control, vat_input, default_expense; null for an ordinary account. |
|
control |
boolean | True for a control account, which takes postings only from its subledger (receivables, payables, bank, fixed assets); default false. (default false) |
|
currency |
string or null | ISO 4217 code when the account only accepts one currency, e.g. EUR; null (default) accepts any. |
|
vat_recovery_percent |
decimal string or null | How much of the input VAT of a bill line booked to the account the company may deduct, in percent (0 to 100), when the line states none (ADR-0079): e.g. 0 on an account for gifts whose VAT the law excludes, 50 for a car used half privately. The rest of the VAT is booked as part of the cost on this account. Null: all of it. (min 0; max 100) |
Example request
curl -X POST "$ERP_URL/v1/entities/CH01/accounts" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"code": "6510",
"name": "Telefon und Internet",
"report_line": "pl.opex.other",
"type": "expense"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | AccountOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"code": "string",
"name": "string",
"type": "string",
"report_line": "string",
"role": "string",
"is_control": true,
"currency": "string",
"active": true,
"vat_recovery_percent": "string"
}
PATCH /v1/entities/{entity}/accounts/{code}
Change an account
Rename an account, give it a role, take it away, change its statement line or deactivate
it. Only the fields sent change; role: null takes the role away. A chart is the customer's
own (imported), so an existing account must be able to take the role a service looks for.
A role says which account a service posts to, so who holds it is controlled. The role must
be one the services know and fit the account's type (422); only one account of the entity
holds it (409); a control role (bank, ar_control, ap_control, fixed_asset_cost,
accumulated_depreciation, annual_result, ...) is refused (409) on an account that already
has postings from outside its subledger. A change to or from a control or required role is a
request for a second person (the approval policy's change_required_account_role, default
require_approval): the answer is 202 with status pending_approval and the
approval_id, and the account changes when another person approves it
(POST /v1/approvals/{approval_id}/approve). The role of an ordinary account changes at
once; a rename alone always does. An account cannot be deactivated (409, naming why) while it
holds a role, is a control account, carries a balance (a P&L account: a result in a fiscal year
not closed to zero), is named by unfinished work (an entry, invoice, bill, purchase order,
payment or recurring invoice), or while a fixed asset, product, subscription, contract, party,
bank account or setting posts to it. The report
line must be one that holds the account's type (422).
The number, type and currency are fixed. Every change is audited with the previous value
(account.role.change, account.update); a request, a rejection and an approval are audited
too. Repeating a change that has been applied changes nothing. Refused with 403 when the
policy denies manage_accounts (always for agents), 404 for an unknown account, 409 for the
refusals above and for a second pending request on the account.
| Required scope | admin (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | update_account_v1_entities__entity__accounts__code__patch |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| path | code |
string | yes | Account code in the entity's chart, e.g. 6510. |
Request body
application/json (required): AccountUpdate
| Field | Type | Required | Description |
|---|---|---|---|
role |
string or null | The purpose the services use the account for, e.g. retained_earnings or deferred_tax_asset (GET /v1/entities/{entity}/accounts shows the roles in use). Send null to take the role away; omit to leave it. The role must be one the services know and fit the account's type (422), and no other account of the entity may hold it (409). A control role is refused (409) on an account that already has postings from outside its subledger. Changing a control or required role needs a second person: the answer is 202 with a pending approval. (max length 64; pattern ^[a-z][a-z0-9_]*$) |
|
name |
string or null | A new name for the account, e.g. Telefon und Internet; the number, type and postings stay as they are. Omit to leave it. (min length 1; max length 200) |
|
report_line |
string or null | The statement line the account is grouped under, e.g. pl.opex.other; each line holds one type of account, so an expense account goes on an expense line, never on a revenue or balance-sheet one (422). Omit to leave it. (max length 120; pattern `^(bs |
|
active |
boolean or null | False stops postings to the account, true allows them again. Refused (409, naming why) while the account holds a role, is a control account, carries a balance (a P&L account: a result in a year not closed to zero), is named by unfinished work (an entry, invoice, bill, order, payment or recurring invoice), or while an asset, product, subscription, contract, party or setting posts to it. Omit to leave it. | |
vat_recovery_percent |
decimal string or null | How much of the input VAT of a bill line booked to the account the company may deduct, in percent (0 to 100), when the line states none (ADR-0079): e.g. 0 on an account for gifts whose VAT the law excludes, 50 for a car used half privately. The rest of the VAT is booked as part of the cost on this account. Null: all of it. Send null to take it away; omit to leave it. Bills already entered keep theirs. (min 0; max 100) |
|
reason |
string or null | Why; kept in the audit trail and shown to the second person who decides a change of a control or required role. (min length 3; max length 500) |
Example request
curl -X PATCH "$ERP_URL/v1/entities/CH01/accounts/6510" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "Customer chart: 2979 holds the year'\''s result",
"role": "annual_result"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | AccountChange |
| 202 | Waits for a second person. | AccountChange |
| 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)
{
"account": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"code": "string",
"name": "string",
"type": "string",
"report_line": "string",
"role": "string",
"is_control": true,
"currency": "string",
"active": true,
"vat_recovery_percent": "string"
},
"status": "applied",
"changed": [
"string"
],
"approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
GET /v1/entities/{entity}/brand
Read a company's brand
The company's logo, brand and accent colours and the theme of the app, with the contrast of the colours in use against white. A company that set nothing has the product's look: no logo, the deep teal, the light theme.
| Required scope | read (admin implies every scope) |
| Operation id | get_brand_v1_entities__entity__brand_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/brand" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | BrandOut |
| 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",
"theme": "dark",
"colour": "string",
"accent": "string",
"effective_colour": "string",
"effective_accent": "string",
"logo": {
"url": "string",
"width": 1,
"height": 1,
"filename": "string",
"sha256": "string"
},
"checks": [
{
"name": "accent",
"colour": "string",
"ratio": 1.5,
"minimum": 1.5,
"passes": true
}
],
"updated_at": "2026-09-21T09:30:00Z",
"updated_by": "string"
}
PATCH /v1/entities/{entity}/brand
Change a company's colours
Change the theme (light, dark, system), the brand colour or the accent colour; only
the fields sent change, and null sets a colour back to the default. The brand colour is the
app's buttons and links and the title of every invoice, quote and reminder: it must reach
4.5:1 against white (422 brand_colour_contrast, with the ratio). The accent colour draws
the rules of the documents and the band of the e-mails: 3:1 (422 brand_accent_contrast).
422 brand_colour for what is not a colour. Audited as brand.update.
| Required scope | admin (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | update_brand_v1_entities__entity__brand_patch |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
Request body
application/json (required): BrandUpdate
| Field | Type | Required | Description |
|---|---|---|---|
theme |
"dark" | "light" | "system" or null |
light, dark or system. |
|
colour |
string or null | #rrggbb (or #rgb); at least 4.5:1 against white. null: the default. (max length 7) |
|
accent |
string or null | #rrggbb (or #rgb); at least 3:1 against white. null: the brand colour. (max length 7) |
Example request
curl -X PATCH "$ERP_URL/v1/entities/CH01/brand" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"colour": "#1d4ed8",
"theme": "system"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | BrandOut |
| 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",
"theme": "dark",
"colour": "string",
"accent": "string",
"effective_colour": "string",
"effective_accent": "string",
"logo": {
"url": "string",
"width": 1,
"height": 1,
"filename": "string",
"sha256": "string"
},
"checks": [
{
"name": "accent",
"colour": "string",
"ratio": 1.5,
"minimum": 1.5,
"passes": true
}
],
"updated_at": "2026-09-21T09:30:00Z",
"updated_by": "string"
}
GET /v1/entities/{entity}/brand/logo
Download a company's logo
The stored PNG. Its address in GET /v1/entities/{entity}/brand changes with the file, so
a browser keeps it for a day.
| Required scope | read (admin implies every scope) |
| Operation id | get_logo_v1_entities__entity__brand_logo_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/brand/logo" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | The logo as a PNG. | image/png |
| 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 | brand_no_logo: the company has no logo. |
|
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
POST /v1/entities/{entity}/brand/logo
Upload a company's logo
Send multipart/form-data with the image in the field file. It is read by its bytes,
reduced to at most 1200 x 480 pixels keeping its proportions and kept as a PNG without
metadata; it replaces the company's logo on the app's top bar, on its PDFs, its e-mails to
customers, the customer's page of a quote and its website forms. 422 brand_logo_empty,
brand_logo_too_large, brand_logo_svg or brand_logo_invalid. Audited as brand.logo
with the PNG's SHA-256; sending the same file again changes nothing that shows.
| Required scope | admin (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | upload_logo_v1_entities__entity__brand_logo_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
Request body
multipart/form-data (required): Body_upload_logo_v1_entities__entity__brand_logo_post
| Field | Type | Required | Description |
|---|---|---|---|
file |
string | yes | A PNG, JPEG, WebP or GIF image, at most 2 MB. SVG is refused. |
Example request
curl -X POST "$ERP_URL/v1/entities/CH01/brand/logo" \
-H "Authorization: Bearer $ERP_KEY" \
-F "file=@statement.xml"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | BrandOut |
| 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",
"theme": "dark",
"colour": "string",
"accent": "string",
"effective_colour": "string",
"effective_accent": "string",
"logo": {
"url": "string",
"width": 1,
"height": 1,
"filename": "string",
"sha256": "string"
},
"checks": [
{
"name": "accent",
"colour": "string",
"ratio": 1.5,
"minimum": 1.5,
"passes": true
}
],
"updated_at": "2026-09-21T09:30:00Z",
"updated_by": "string"
}
DELETE /v1/entities/{entity}/brand/logo
Remove a company's logo
The company's documents and pages show its name alone again. Audited as
brand.logo_remove; removing a logo that is not there changes nothing.
| Required scope | admin (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | delete_logo_v1_entities__entity__brand_logo_delete |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
Example request
curl -X DELETE "$ERP_URL/v1/entities/CH01/brand/logo" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | BrandOut |
| 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",
"theme": "dark",
"colour": "string",
"accent": "string",
"effective_colour": "string",
"effective_accent": "string",
"logo": {
"url": "string",
"width": 1,
"height": 1,
"filename": "string",
"sha256": "string"
},
"checks": [
{
"name": "accent",
"colour": "string",
"ratio": 1.5,
"minimum": 1.5,
"passes": true
}
],
"updated_at": "2026-09-21T09:30:00Z",
"updated_by": "string"
}
GET /v1/entities/{entity}/fiscal-years
List Fiscal Years
The entity's fiscal years, oldest first, each with its calendar, its first and last day,
its periods (regular periods, then adjustment periods) and its year-end close (close: status,
result and what is left to appropriate). unappropriated=true keeps the years whose general
meeting is overdue while part of the result is unappropriated.
| Required scope | read (admin implies every scope) |
| Operation id | list_fiscal_years_v1_entities__entity__fiscal_years_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| query | unappropriated |
boolean | Only the years whose result is still unappropriated when the general meeting is overdue (a Swiss entity, six months after the year end, Art. 699 para. 2 OR): the list the Role Center cue counts. (default false) |
|
| query | as_of |
string (date) or null | The day the overdue rule is judged on, ISO 8601; default: today where the entity is. |
Example request
curl "$ERP_URL/v1/entities/CH01/fiscal-years" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of FiscalYearOut |
| 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)
[
{
"fiscal_year": 1,
"calendar": "string",
"year_end": "string",
"end_weekday": "string",
"weeks": 1,
"adjustment_periods": 1,
"start_date": "2026-09-21",
"end_date": "2026-09-21",
"periods": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fiscal_year": 1,
"number": 1,
"kind": "string",
"start_date": "2026-09-21",
"end_date": "2026-09-21",
"status": "string",
"closed_at": null,
"closed_by": null
}
],
"close": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"kind": "catch_up",
"status": "cancelled",
"result": "string",
"appropriated": "string",
"remainder": "string",
"appropriation_overdue": true
}
}
]
POST /v1/entities/{entity}/fiscal-years
Open a fiscal year
Open a fiscal year by creating its periods: monthly, 4-4-5 / 4-5-4 / 5-4-4, or 13 four-week periods, optionally with one or two adjustment periods on the last day.
A year directly follows the last year (it starts the day after it ends) or directly
precedes the first. Without calendar the year uses the calendar of the latest earlier
year, else twelve calendar months. Returns the new periods, all open. Refused with 409
when the year exists or overlaps another, or when a year would be missing in between
(fiscal_year_gap, with missing: the year to open first), 422 when the calendar does not
give a year of about 12 months.
| Required scope | admin (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | open_fiscal_year_v1_entities__entity__fiscal_years_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
Request body
application/json (required): FiscalYearRequest
| Field | Type | Required | Description |
|---|---|---|---|
fiscal_year |
integer | yes | The fiscal year to open, 2000 to 2100, e.g. 2027. It is the calendar year in which the year ends. (min 2000; max 2100) |
calendar |
FiscalCalendar or null |
How to divide the year into periods. Default: the calendar of the previous year opened with one, else twelve calendar months. |
Example request
curl -X POST "$ERP_URL/v1/entities/CH01/fiscal-years" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"calendar": {
"adjustment_periods": 1,
"calendar": "4-4-5"
},
"fiscal_year": 2027
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | array of PeriodOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fiscal_year": 1,
"number": 1,
"kind": "string",
"start_date": "2026-09-21",
"end_date": "2026-09-21",
"status": "string",
"closed_at": "2026-09-21T09:30:00Z",
"closed_by": "string"
}
]
GET /v1/entities/{entity}/periods
List Periods
The entity's posting periods ordered by fiscal year and number, with their status
(open or closed). Use the id of a period in the close endpoints.
| Required scope | read (admin implies every scope) |
| Operation id | list_periods_v1_entities__entity__periods_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| query | fiscal_year |
integer or null | Only the periods of this fiscal year, e.g. 2026. | |
| query | on |
string (date) or null | Only the periods containing this date (ISO 8601, e.g. 2026-09-15); a regular period comes before an adjustment period on the same day. |
Example request
curl "$ERP_URL/v1/entities/CH01/periods" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of PeriodOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fiscal_year": 1,
"number": 1,
"kind": "string",
"start_date": "2026-09-21",
"end_date": "2026-09-21",
"status": "string",
"closed_at": "2026-09-21T09:30:00Z",
"closed_by": "string"
}
]
POST /v1/entities/{entity}/role-accounts
Add Role Accounts
Add the accounts of required roles the entity lacks (e.g. the cash discount roles of
entities created before they existed) from its chart template. Refused, naming the
codes, when a template code is already in use; repeating it adds nothing. Answers 201
with the lists added and present (empty added when nothing was missing).
| Required scope | admin (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | add_role_accounts_v1_entities__entity__role_accounts_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| query | chart |
string or null | Chart template id to take the accounts from (GET /v1/charts); default: the template the entity was created from. |
Example request
curl -X POST "$ERP_URL/v1/entities/CH01/role-accounts" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)"
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | RoleAccounts |
| 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",
"chart": "string",
"added": [
"string"
],
"present": [
"string"
]
}
GET /v1/industries
List the industries a company can choose
The industries a company may say it is in (its industry, set in POST /v1/setup, POST /v1/entities or PATCH /v1/entities/{entity}), each with how far it is built (ready,
partial, planned), what the product does for it and the feature modules that serve it.
The industry puts its screens first in the app's menu and on its Home; it switches nothing
off. Needs the read scope.
| Required scope | read (admin implies every scope) |
| Operation id | list_industries_v1_industries_get |
Example request
curl "$ERP_URL/v1/industries" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of IndustryOut |
| 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 |
Example response (shape)
[
{
"key": "ecommerce",
"name": "string",
"status": "partial",
"summary": "string",
"modules": [
{
"name": "string",
"title": "string"
}
]
}
]
GET /v1/setup
Tell whether the organization waits for its setup
needed while the organization has no company (and is not a test organization): the web
UI then shows its first administrator the setup wizard. Needs the read scope.
| Required scope | read (admin implies every scope) |
| Operation id | setup_status_v1_setup_get |
Example request
curl "$ERP_URL/v1/setup" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | SetupStatus |
| 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)
{
"needed": true,
"can_complete": true
}
POST /v1/setup
Set up the organization: Production, then Test
Create the organization's first company from the setup wizard's answers, then its Test environment.
Production is the organization itself: the company (code: the country and 01, e.g.
CA01) with the chart of accounts of the chosen standard (GET /v1/setup/options), the
periods of the fiscal year that holds today (a year is named by the calendar year it ends
in) and the tax return period chosen, in one commit. Test is then made as a test copy of
Production (POST /v1/test-copies), named after the organization with (Test); when it
cannot be made, Production stands, test_environment_problem says why, and an administrator
makes it from Settings, Test copies.
Refused with 409 once the organization has a company (a second request makes nothing) and in
a test organization; 422 for an answer the country does not allow. Needs the admin scope
and a person: an agent key is refused (403). Recorded in the audit trail (org.setup,
entity.create, org.test_copy).
| Required scope | admin (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | complete_setup_v1_setup_post |
Request body
application/json (required): SetupInput
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | The company's legal name. (min length 1; max length 200) |
country |
string | yes | Where it is registered (GET /v1/setup/options). (pattern ^[A-Z]{2}$) |
subdivision |
string or null | The state (US) or province (Canada), ISO 3166-2; required there, refused elsewhere. (pattern ^[A-Z]{2}-[A-Z0-9]{1,3}$) |
|
standard |
string or null | The accounting standard's id, one of the country's. Omitted: the country's first (GET /v1/setup/options lists them, the default first). |
|
currency |
string or null | The currency of the books, one the country allows. Cannot be changed later. Omitted: the country's currency. (pattern ^[A-Z]{3}$) |
|
fiscal_year_start_month |
integer | The month the fiscal year starts, 1 to 12. (min 1; max 12; default 1) |
|
tax_period |
string or null | The tax return period, one of the country's; default: its first. | |
tax_number |
string or null | The tax registration number, when asked. (max length 40) | |
street |
string or null | Street. (max length 70) | |
building_number |
string or null | Number. (max length 16) | |
postal_code |
string or null | Postal code. (max length 16) | |
town |
string or null | Town. (max length 35) | |
industry |
"ecommerce" | "general" | "hospitality" | "manufacturing" | "retail" | "services" | "trade" |
The company's industry (GET /v1/industries): general, hospitality, services, trade, retail, ecommerce or manufacturing. It puts the screens of the industry first in the app's menu and on its Home; it switches nothing off, and every operation works the same in every industry. Default general; it can be changed later. (default "general") |
Example request
curl -X POST "$ERP_URL/v1/setup" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"building_number": "100",
"country": "CA",
"currency": "CAD",
"fiscal_year_start_month": 1,
"name": "Maple Analytics Inc.",
"postal_code": "M5X 1A9",
"standard": "aspe",
"street": "King Street West",
"subdivision": "CA-ON",
"tax_number": "123456789RT0001",
"tax_period": "quarter",
"town": "Toronto"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | SetupDone |
| 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)
{
"entity": "string",
"standard": "string",
"currency": "string",
"chart": "string",
"framework": "string",
"fiscal_year": 1,
"tax_settings": false,
"test_environment": {
"org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "string",
"created_at": "2026-09-21T09:30:00Z",
"created_by": {},
"masked": true,
"cloned_at": "2026-09-21T09:30:00Z",
"refresh_schedule": "off",
"refresh_problem": "refresh_failed",
"refresh_problem_at": "2026-09-21T09:30:00Z",
"remove_requested_at": "2026-09-21T09:30:00Z"
},
"test_environment_problem": "string"
}
POST /v1/setup/companies
Add a further company from the setup wizard's answers
Add a company to an organization that has one, made exactly as POST /v1/setup makes
the first: the chart of accounts of the chosen standard, the periods of the fiscal year that
holds today and the tax settings of the return period chosen (tax_settings), in one commit,
under the code given. Unlike POST /v1/entities, the answers are checked against what the
country allows (GET /v1/setup/options).
Refused with 409 when the code is taken, 422 for an answer the country does not allow, and
403 for an agent key or a key without the admin scope. No Test is made: the organization's
existing test copies keep what they were copied from. Recorded in the audit trail
(org.company_add, entity.create). A retry with the same Idempotency-Key returns the
first answer.
| Required scope | admin (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | add_company_v1_setup_companies_post |
Request body
application/json (required): CompanyInput
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | The company's legal name. (min length 1; max length 200) |
country |
string | yes | Where it is registered (GET /v1/setup/options). (pattern ^[A-Z]{2}$) |
subdivision |
string or null | The state (US) or province (Canada), ISO 3166-2; required there, refused elsewhere. (pattern ^[A-Z]{2}-[A-Z0-9]{1,3}$) |
|
standard |
string or null | The accounting standard's id, one of the country's. Omitted: the country's first (GET /v1/setup/options lists them, the default first). |
|
currency |
string or null | The currency of the books, one the country allows. Cannot be changed later. Omitted: the country's currency. (pattern ^[A-Z]{3}$) |
|
fiscal_year_start_month |
integer | The month the fiscal year starts, 1 to 12. (min 1; max 12; default 1) |
|
tax_period |
string or null | The tax return period, one of the country's; default: its first. | |
tax_number |
string or null | The tax registration number, when asked. (max length 40) | |
street |
string or null | Street. (max length 70) | |
building_number |
string or null | Number. (max length 16) | |
postal_code |
string or null | Postal code. (max length 16) | |
town |
string or null | Town. (max length 35) | |
industry |
"ecommerce" | "general" | "hospitality" | "manufacturing" | "retail" | "services" | "trade" |
The company's industry (GET /v1/industries): general, hospitality, services, trade, retail, ecommerce or manufacturing. It puts the screens of the industry first in the app's menu and on its Home; it switches nothing off, and every operation works the same in every industry. Default general; it can be changed later. (default "general") |
|
code |
string | yes | Short unique code, 2 to 20 characters of A-Z, 0-9, _ and -, e.g. CH02; it names the company in every URL. (pattern ^[A-Z0-9_-]{2,20}$) |
Example request
curl -X POST "$ERP_URL/v1/setup/companies" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"code": "CH02",
"country": "CH",
"currency": "CHF",
"fiscal_year_start_month": 1,
"name": "Muster Services AG",
"standard": "ch_co",
"tax_number": "CHE-123.456.788 MWST",
"tax_period": "quarter"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | CompanyDone |
| 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)
{
"entity": "string",
"standard": "string",
"currency": "string",
"chart": "string",
"framework": "string",
"fiscal_year": 1,
"tax_settings": false
}
GET /v1/setup/options
List what the setup wizard offers per country
The countries served (the EU, the United Kingdom, Switzerland and Liechtenstein, the
United States and Canada), each with its currency, the currencies its books may be kept in,
the accounting standards a company there may report under (the default first) and the chart
each starts from, its indirect tax, the return periods allowed, what its tax registration
number is called and the states or provinces to choose from. Needs the read scope.
| Required scope | read (admin implies every scope) |
| Operation id | setup_options_v1_setup_options_get |
Example request
curl "$ERP_URL/v1/setup/options" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | SetupOptions |
| 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 |
Example response (shape)
{
"countries": [
{
"country": "string",
"name": "string",
"region": "ca",
"currency": "string",
"currencies": [
"string"
],
"standards": [
null
],
"tax": "gst_hst",
"tax_periods": [
"string"
],
"tax_number_label": "string",
"vat_return_form": true,
"subdivisions": [
null
]
}
]
}
Schemas
AccountChange
| Field | Type | Required | Description |
|---|---|---|---|
account |
AccountOut |
yes | The account as it is now. While a change waits for a second person it is unchanged. |
status |
"applied" | "pending_approval" |
yes | applied (the account has changed, or nothing needed to change) or pending_approval (a second person decides; approval_id). |
changed |
array of string | yes | The fields that changed (name, role, report_line, active, vat_recovery_percent); with pending_approval, the ones that will change once approved. |
approval_id |
string (uuid) or null | The approval request of a change that waits for a second person; decide it with POST /v1/approvals/{approval_id}/approve or /reject. |
AccountInput
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | yes | Account number, unique in the entity, e.g. 6510. |
name |
string | yes | Account name, e.g. Telefon und Internet. |
type |
"asset" | "equity" | "expense" | "liability" | "revenue" |
yes | asset, liability, equity, revenue or expense; decides the normal balance side and which report it appears in. |
report_line |
string | yes | Where the account appears in the statements, as a dotted key such as pl.opex.other, pl.revenue or bs.assets.current.cash. Use a line the chart already uses for the same type: each line holds one type of account (422 otherwise). |
role |
string or null | The purpose the services use the account for, e.g. bank, ar_control, vat_input, default_expense; null for an ordinary account. |
|
control |
boolean | True for a control account, which takes postings only from its subledger (receivables, payables, bank, fixed assets); default false. (default false) |
|
currency |
string or null | ISO 4217 code when the account only accepts one currency, e.g. EUR; null (default) accepts any. |
|
vat_recovery_percent |
decimal string or null | How much of the input VAT of a bill line booked to the account the company may deduct, in percent (0 to 100), when the line states none (ADR-0079): e.g. 0 on an account for gifts whose VAT the law excludes, 50 for a car used half privately. The rest of the VAT is booked as part of the cost on this account. Null: all of it. (min 0; max 100) |
AccountOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | Id (uuid) of the account. |
code |
string | yes | Account number, unique in the entity. |
name |
string | yes | The account's name, the one the company keeps its books under; what official documents and filings print. Clients show it in their reader's language. |
type |
string | yes | asset, liability, equity, revenue or expense. |
report_line |
string | yes | The statement line the account is grouped under, e.g. pl.opex.other. |
role |
string or null | yes | The purpose the services use the account for, e.g. bank; null for an ordinary account. |
is_control |
boolean | yes | True for a control account, which only its subledger posts to. |
currency |
string or null | yes | The only currency the account accepts (ISO 4217); null accepts any. |
active |
boolean | yes | False for an account that no longer accepts postings. |
vat_recovery_percent |
string or null | How much of the input VAT of a bill line booked to the account the company may deduct, in percent (0 to 100), when the line states none (ADR-0079): e.g. 0 on an account for gifts whose VAT the law excludes, 50 for a car used half privately. The rest of the VAT is booked as part of the cost on this account. Null: all of it. |
AccountUpdate
Changes to an account of the chart. Only the fields sent change; send role: null to
take a role away. The number, type and currency of an account are fixed.
| Field | Type | Required | Description |
|---|---|---|---|
role |
string or null | The purpose the services use the account for, e.g. retained_earnings or deferred_tax_asset (GET /v1/entities/{entity}/accounts shows the roles in use). Send null to take the role away; omit to leave it. The role must be one the services know and fit the account's type (422), and no other account of the entity may hold it (409). A control role is refused (409) on an account that already has postings from outside its subledger. Changing a control or required role needs a second person: the answer is 202 with a pending approval. (max length 64; pattern ^[a-z][a-z0-9_]*$) |
|
name |
string or null | A new name for the account, e.g. Telefon und Internet; the number, type and postings stay as they are. Omit to leave it. (min length 1; max length 200) |
|
report_line |
string or null | The statement line the account is grouped under, e.g. pl.opex.other; each line holds one type of account, so an expense account goes on an expense line, never on a revenue or balance-sheet one (422). Omit to leave it. (max length 120; pattern `^(bs |
|
active |
boolean or null | False stops postings to the account, true allows them again. Refused (409, naming why) while the account holds a role, is a control account, carries a balance (a P&L account: a result in a year not closed to zero), is named by unfinished work (an entry, invoice, bill, order, payment or recurring invoice), or while an asset, product, subscription, contract, party or setting posts to it. Omit to leave it. | |
vat_recovery_percent |
decimal string or null | How much of the input VAT of a bill line booked to the account the company may deduct, in percent (0 to 100), when the line states none (ADR-0079): e.g. 0 on an account for gifts whose VAT the law excludes, 50 for a car used half privately. The rest of the VAT is booked as part of the cost on this account. Null: all of it. Send null to take it away; omit to leave it. Bills already entered keep theirs. (min 0; max 100) |
|
reason |
string or null | Why; kept in the audit trail and shown to the second person who decides a change of a control or required role. (min length 3; max length 500) |
Body_upload_logo_v1_entities__entity__brand_logo_post
| Field | Type | Required | Description |
|---|---|---|---|
file |
string | yes | A PNG, JPEG, WebP or GIF image, at most 2 MB. SVG is refused. |
BrandOut
A company's brand as the app, its documents and its pages show it.
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | The company's code. |
theme |
"dark" | "light" | "system" |
yes | How the app looks in this company: light, dark or system (the device's setting). |
colour |
string or null | yes | The brand colour, #rrggbb; null: the product's own. |
accent |
string or null | yes | The accent colour, #rrggbb; null: the brand colour. |
effective_colour |
string | yes | The brand colour in use (the default when none). |
effective_accent |
string | yes | The accent colour in use. |
logo |
LogoOut or null |
yes | The logo; null: none. |
checks |
array of ContrastOut |
yes | The contrast of the colours in use. |
updated_at |
string (date-time) or null | yes | When it was last changed; null: never. |
updated_by |
string or null | yes | Who changed it last. |
BrandUpdate
Only the fields sent change; null sets a colour back to the default.
| Field | Type | Required | Description |
|---|---|---|---|
theme |
"dark" | "light" | "system" or null |
light, dark or system. |
|
colour |
string or null | #rrggbb (or #rgb); at least 4.5:1 against white. null: the default. (max length 7) |
|
accent |
string or null | #rrggbb (or #rgb); at least 3:1 against white. null: the brand colour. (max length 7) |
CompanyDone
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | The code of the company created. |
standard |
string | yes | The accounting standard it reports under. |
currency |
string | yes | The currency of its books. |
chart |
string | yes | The chart template its accounts were copied from. |
framework |
string | yes | Its accounting framework. |
fiscal_year |
integer | yes | The fiscal year whose periods were opened. |
tax_settings |
boolean | True when its tax settings (method and return period) were saved; false where the country has none to set (US sales tax) or keeps the defaults (Liechtenstein). (default false) |
CompanyInput
The setup wizard's answers for a further company of an organization that has one.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | The company's legal name. (min length 1; max length 200) |
country |
string | yes | Where it is registered (GET /v1/setup/options). (pattern ^[A-Z]{2}$) |
subdivision |
string or null | The state (US) or province (Canada), ISO 3166-2; required there, refused elsewhere. (pattern ^[A-Z]{2}-[A-Z0-9]{1,3}$) |
|
standard |
string or null | The accounting standard's id, one of the country's. Omitted: the country's first (GET /v1/setup/options lists them, the default first). |
|
currency |
string or null | The currency of the books, one the country allows. Cannot be changed later. Omitted: the country's currency. (pattern ^[A-Z]{3}$) |
|
fiscal_year_start_month |
integer | The month the fiscal year starts, 1 to 12. (min 1; max 12; default 1) |
|
tax_period |
string or null | The tax return period, one of the country's; default: its first. | |
tax_number |
string or null | The tax registration number, when asked. (max length 40) | |
street |
string or null | Street. (max length 70) | |
building_number |
string or null | Number. (max length 16) | |
postal_code |
string or null | Postal code. (max length 16) | |
town |
string or null | Town. (max length 35) | |
industry |
"ecommerce" | "general" | "hospitality" | "manufacturing" | "retail" | "services" | "trade" |
The company's industry (GET /v1/industries): general, hospitality, services, trade, retail, ecommerce or manufacturing. It puts the screens of the industry first in the app's menu and on its Home; it switches nothing off, and every operation works the same in every industry. Default general; it can be changed later. (default "general") |
|
code |
string | yes | Short unique code, 2 to 20 characters of A-Z, 0-9, _ and -, e.g. CH02; it names the company in every URL. (pattern ^[A-Z0-9_-]{2,20}$) |
ContrastOut
| Field | Type | Required | Description |
|---|---|---|---|
name |
"accent" | "colour" |
yes | Which colour is checked. |
colour |
string | yes | The colour checked, #rrggbb (the default when none is set). |
ratio |
number | yes | Its contrast ratio against white (WCAG 2.2), 1 to 21. |
minimum |
number | yes | What it must reach: 4.5 for the brand colour (text on white, white on it), 3 for the accent (rules and bands). |
passes |
boolean | yes | Whether it reaches the minimum. |
CountryOption
| Field | Type | Required | Description |
|---|---|---|---|
country |
string | yes | ISO 3166-1 alpha-2 code. |
name |
string | yes | The country's name in English. |
region |
"ca" | "ch" | "eu" | "no" | "uk" | "us" |
yes | eu, no (Norway), uk, ch (Switzerland and Liechtenstein), us or ca. |
currency |
string | yes | The country's currency, the default of the books. |
currencies |
array of string | yes | The currencies the books may be kept in. |
standards |
array of FrameworkOption |
yes | The accounting standards a company there may report under, the default first. |
tax |
"gst_hst" | "sales_tax" | "vat" |
yes | The indirect tax: vat, sales_tax (US, per state) or gst_hst (Canada). |
tax_periods |
array of string | yes | The return periods allowed, the default first (month, two_months, quarter, half_year, year); empty when none is asked. |
tax_number_label |
string or null | yes | What the tax registration number is called; null when none is asked. |
vat_return_form |
boolean | yes | Whether the VAT return is prepared on the official form; false: VAT is booked and reported per rate, and the return is filed outside the books for now. |
subdivisions |
array of Subdivision |
yes | The states or provinces, one of which must be chosen; empty when none is. |
EntityDetailOut
One entity, with what waits for a second person about it.
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | Id (uuid) of the entity. |
code |
string | yes | The entity code, e.g. CH01; the {entity} of other operations. |
name |
string | yes | Legal name. |
country |
string | yes | ISO 3166-1 alpha-2 country code. |
subdivision |
string or null | yes | ISO 3166-2 code of the state or canton, e.g. DE-BY; null if none. |
functional_currency |
string | yes | ISO 4217 code of the currency the books are kept in. |
fiscal_year_start_month |
integer | yes | Month the fiscal year starts, 1 to 12. |
framework |
string | yes | Accounting framework: ifrs, us_gaap or local. |
vat_number |
string or null | yes | The entity's VAT number; null if not set. |
street |
string or null | Street of the address; null if not set. | |
building_number |
string or null | Building number of the address; null if not set. | |
postal_code |
string or null | Postal code of the address; null if not set. | |
town |
string or null | Town of the address; null if not set. | |
contact_name |
string or null | Seller contact name on e-invoices; null if not set. | |
contact_phone |
string or null | Seller contact phone on e-invoices; null if not set. | |
contact_email |
string or null | Seller contact email on e-invoices; null if not set. | |
electronic_address |
string or null | Seller electronic address on e-invoices; null if not set. | |
electronic_address_scheme |
string or null | Code of the electronic address scheme (EAS), e.g. EM; null if not set. |
|
posting_approval_limit |
string or null | The posting approval limit in the functional currency: whatever a person posts (a manual journal, a bill, an invoice, a payment, a refund, a VAT settlement, a revaluation, a depreciation run) whose functional amount is above it waits for a second person, and so does its reversal. A new company starts at about CHF 100,000 in its currency as a round figure (100000.00 CHF, EUR, USD, GBP or CAD, 1000000.00 SEK, 15000000 JPY). Null: no limit. | |
industry |
"ecommerce" | "general" | "hospitality" | "manufacturing" | "retail" | "services" | "trade" |
The company's industry (GET /v1/industries): general, hospitality, services, trade, retail, ecommerce or manufacturing. It puts the screens of the industry first in the app's menu and on its Home; it switches nothing off, and every operation works the same in every industry. (default "general") |
|
posting_limit_request |
PostingLimitRequest or null |
The request to raise or remove posting_approval_limit that waits for a second person; null when none waits. |
|
time_zone |
string | yes | The IANA time zone the entity's days are counted in, e.g. Europe/Zurich: today, the default date of its documents and the latest day a check accepts are the day it is there (its state's or province's in the United States and Canada). |
EntityInput
| Field | Type | Required | Description |
|---|---|---|---|
street |
string or null | Street of the entity's address, at most 70 characters. (max length 70) | |
building_number |
string or null | Building number of the address, at most 16 characters. (max length 16) | |
postal_code |
string or null | Postal code of the address, at most 16 characters. (max length 16) | |
town |
string or null | Town of the address, at most 35 characters. (max length 35) | |
contact_name |
string or null | Seller contact (BT-41). | |
contact_phone |
string or null | Seller contact phone (BT-42). | |
contact_email |
string or null | Seller contact email (BT-43). | |
electronic_address |
string or null | Seller electronic address (BT-34); defaults to contact_email. | |
electronic_address_scheme |
string or null | EAS code, e.g. EM (email), 9930 (German VAT id), 0204. | |
code |
string | yes | Short unique code, 2 to 20 characters of A-Z, 0-9, _ and -, e.g. CH01; it names the entity in every URL. (pattern ^[A-Z0-9_-]{2,20}$) |
name |
string | yes | Legal name, e.g. Demo AG. |
country |
string | yes | ISO 3166-1 alpha-2 country code, e.g. CH or DE; decides the VAT rules. (pattern ^[A-Z]{2}$) |
subdivision |
string or null | ISO 3166-2 code of the state or canton, e.g. DE-BY; null when omitted. (pattern ^[A-Z]{2}-[A-Z0-9]{1,3}$) |
|
functional_currency |
string or null | ISO 4217 code of the currency the books are kept in, e.g. CHF. Cannot be changed later. Omitted: the currency of its country (422 entity_currency_needed where that is not known). (pattern ^[A-Z]{3}$) |
|
fiscal_year_start_month |
integer | Month the fiscal year starts, 1 to 12; default 1 (January). (min 1; max 12; default 1) |
|
framework |
"ifrs" | "local" | "us_gaap" |
Accounting framework: ifrs (default), us_gaap or local. (default "ifrs") |
|
vat_number |
string or null | The entity's VAT number, e.g. CHE-123.456.788 MWST or DE123456788; null when omitted. For a Swiss or German entity its format is checked (422); it is printed on invoices and is required on them when the entity is VAT-registered. |
|
chart |
string | Chart template id (GET /v1/charts), e.g. ch_kmu; default ifrs_generic. GET /v1/charts?country= marks the country's standard chart, which the operator's provisioning uses. (default "ifrs_generic") |
|
fiscal_years |
array of integer | Years to open periods for. | |
fiscal_calendar |
FiscalCalendar or null |
Calendar for those years (default: twelve calendar months). | |
industry |
"ecommerce" | "general" | "hospitality" | "manufacturing" | "retail" | "services" | "trade" |
The company's industry (GET /v1/industries): general, hospitality, services, trade, retail, ecommerce or manufacturing. It puts the screens of the industry first in the app's menu and on its Home; it switches nothing off, and every operation works the same in every industry. Default general. (default "general") |
EntityOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | Id (uuid) of the entity. |
code |
string | yes | The entity code, e.g. CH01; the {entity} of other operations. |
name |
string | yes | Legal name. |
country |
string | yes | ISO 3166-1 alpha-2 country code. |
subdivision |
string or null | yes | ISO 3166-2 code of the state or canton, e.g. DE-BY; null if none. |
functional_currency |
string | yes | ISO 4217 code of the currency the books are kept in. |
fiscal_year_start_month |
integer | yes | Month the fiscal year starts, 1 to 12. |
framework |
string | yes | Accounting framework: ifrs, us_gaap or local. |
vat_number |
string or null | yes | The entity's VAT number; null if not set. |
street |
string or null | Street of the address; null if not set. | |
building_number |
string or null | Building number of the address; null if not set. | |
postal_code |
string or null | Postal code of the address; null if not set. | |
town |
string or null | Town of the address; null if not set. | |
contact_name |
string or null | Seller contact name on e-invoices; null if not set. | |
contact_phone |
string or null | Seller contact phone on e-invoices; null if not set. | |
contact_email |
string or null | Seller contact email on e-invoices; null if not set. | |
electronic_address |
string or null | Seller electronic address on e-invoices; null if not set. | |
electronic_address_scheme |
string or null | Code of the electronic address scheme (EAS), e.g. EM; null if not set. |
|
posting_approval_limit |
string or null | The posting approval limit in the functional currency: whatever a person posts (a manual journal, a bill, an invoice, a payment, a refund, a VAT settlement, a revaluation, a depreciation run) whose functional amount is above it waits for a second person, and so does its reversal. A new company starts at about CHF 100,000 in its currency as a round figure (100000.00 CHF, EUR, USD, GBP or CAD, 1000000.00 SEK, 15000000 JPY). Null: no limit. | |
industry |
"ecommerce" | "general" | "hospitality" | "manufacturing" | "retail" | "services" | "trade" |
The company's industry (GET /v1/industries): general, hospitality, services, trade, retail, ecommerce or manufacturing. It puts the screens of the industry first in the app's menu and on its Home; it switches nothing off, and every operation works the same in every industry. (default "general") |
|
time_zone |
string | yes | The IANA time zone the entity's days are counted in, e.g. Europe/Zurich: today, the default date of its documents and the latest day a check accepts are the day it is there (its state's or province's in the United States and Canada). |
EntityUpdate
Fields that can change after an entity was created (null leaves a field as is, but the posting approval limit, which null removes).
| Field | Type | Required | Description |
|---|---|---|---|
street |
string or null | Street of the address, at most 70 characters; omit or null to keep it. (max length 70) | |
building_number |
string or null | Building number, at most 16 characters; omit or null to keep it. (max length 16) | |
postal_code |
string or null | Postal code, at most 16 characters; omit or null to keep it. (max length 16) | |
town |
string or null | Town, at most 35 characters; omit or null to keep it. (max length 35) | |
contact_name |
string or null | Seller contact (BT-41). | |
contact_phone |
string or null | Seller contact phone (BT-42). | |
contact_email |
string or null | Seller contact email (BT-43). | |
electronic_address |
string or null | Seller electronic address (BT-34); defaults to contact_email. | |
electronic_address_scheme |
string or null | EAS code, e.g. EM (email), 9930 (German VAT id), 0204. | |
name |
string or null | New legal name; omit or null to keep it. | |
vat_number |
string or null | New VAT number (format checked for a Swiss or German entity, 422); omit or null to keep it. | |
industry |
"ecommerce" | "general" | "hospitality" | "manufacturing" | "retail" | "services" | "trade" or null |
The company's industry (GET /v1/industries): general, hospitality, services, trade, retail, ecommerce or manufacturing. It puts the screens of the industry first in the app's menu and on its Home; it switches nothing off, and every operation works the same in every industry. Omit or null to keep it; audited with the other changes. |
|
posting_approval_limit |
decimal string or null | The posting approval limit in the functional currency: whatever a person posts (a manual journal, a bill, an invoice, a payment, a refund, a VAT settlement, a revaluation, a depreciation run) whose functional amount is above it waits for a second person, and so does its reversal. A new company starts at about CHF 100,000 in its currency as a round figure (100000.00 CHF, EUR, USD, GBP or CAD, 1000000.00 SEK, 15000000 JPY). Omit it to keep the limit; null removes it (a one-person company). Only a person with the admin scope changes it (403 posting_limit_admin_only). A lower limit applies at once; raising or removing it waits for a second person (policy action change_posting_limit): the answer keeps the old limit and names the request in posting_limit_request (409 posting_limit_change_waiting while one waits, until it is decided or its requester withdraws it with POST /v1/approvals/{approval_id}/withdraw). A person alone in the organization (one active person, nobody else disabled or demoted in the last 30 days) changes it at once. Audited as entity.posting_limit with the value before and after (and alone when it applied so). (greater than 0) |
FiscalYearOut
| Field | Type | Required | Description |
|---|---|---|---|
fiscal_year |
integer | yes | The fiscal year, the calendar year in which it ends. |
calendar |
string | yes | monthly, 4-4-5, 4-5-4, 5-4-4 or 13x4. |
year_end |
string | yes | month_end, last_weekday or nearest_weekday. |
end_weekday |
string or null | yes | The weekday a week-based year ends on; null on a monthly calendar. |
weeks |
integer or null | yes | Number of weeks (52 or 53) of a week-based year; null on a monthly calendar. |
adjustment_periods |
integer | yes | Number of one-day adjustment periods on the last day (0 to 2). |
start_date |
string (date) | yes | First day of the year (ISO 8601). |
end_date |
string (date) | yes | Last day of the year (ISO 8601). |
periods |
array of PeriodOut |
yes | The periods of the year: regular ones, then adjustment periods. |
close |
YearCloseSummary or null |
The year-end close of the year (the one holding it, else the newest): status, result, what is appropriated and what is left, and whether the general meeting is overdue; null when the year was never proposed for closing. |
FiscalYearRequest
| Field | Type | Required | Description |
|---|---|---|---|
fiscal_year |
integer | yes | The fiscal year to open, 2000 to 2100, e.g. 2027. It is the calendar year in which the year ends. (min 2000; max 2100) |
calendar |
FiscalCalendar or null |
How to divide the year into periods. Default: the calendar of the previous year opened with one, else twelve calendar months. |
FrameworkOption
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | The standard's id, the standard of POST /v1/setup. |
label |
string | yes | Its name, e.g. US GAAP. |
framework |
"ifrs" | "local" | "us_gaap" |
yes | The entity's accounting framework it is recorded as. |
chart |
string | yes | The chart template the company starts from. |
IndustryModuleOut
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | The feature module's name, e.g. hospitality. |
title |
string | yes | Its title, e.g. Hospitality. |
IndustryOut
| Field | Type | Required | Description |
|---|---|---|---|
key |
"ecommerce" | "general" | "hospitality" | "manufacturing" | "retail" | "services" | "trade" |
yes | The industry's key, the industry of a company. |
name |
string | yes | Its name in English; the app shows its own translation. |
status |
"partial" | "planned" | "ready" |
yes | How far it is built: ready (its own screens exist), partial (the general screens serve it, some of its own are missing) or planned (nothing of its own yet: the general layout). |
summary |
string | yes | What the product does for it, and what it does not yet. |
modules |
array of IndustryModuleOut |
yes | The feature modules that serve it, whose screens come first in it. |
LogoOut
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | yes | Where the PNG is (GET, the read scope); changes with the file. |
width |
integer | yes | Its width in pixels. |
height |
integer | yes | Its height in pixels. |
filename |
string or null | yes | The name of the file it was uploaded as. |
sha256 |
string | yes | SHA-256 of the stored PNG. |
PostingLimitRequest
A request to raise or remove the posting approval limit that waits for a second person.
| Field | Type | Required | Description |
|---|---|---|---|
approval_id |
string (uuid) | yes | Id (uuid) of the approval request; a second person approves it (POST /v1/approvals/{approval_id}/approve), or the person who asked withdraws it (POST /v1/approvals/{approval_id}/withdraw). |
current |
string or null | yes | The limit now, which holds until then; null: none. |
proposed |
string or null | yes | The limit once approved; null: no limit. |
requested_by |
object | yes | Who asked (type, id, name). |
requested_at |
string (date-time) | yes | When it was asked (UTC). |
RoleAccounts
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code of the entity. |
chart |
string | yes | The chart template the accounts were taken from. |
added |
array of string | yes | The accounts added: code and role. |
present |
array of string | yes | Required roles the entity already had. |
SetupDone
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | The code of the company created in Production. |
standard |
string | yes | The accounting standard it reports under. |
currency |
string | yes | The currency of its books. |
chart |
string | yes | The chart template its accounts were copied from. |
framework |
string | yes | Its accounting framework. |
fiscal_year |
integer | yes | The fiscal year whose periods were opened. |
tax_settings |
boolean | True when its tax settings (method and return period) were saved; false where the country has none to set (US sales tax) or keeps the defaults (Liechtenstein). (default false) |
|
test_environment |
CopyOut or null |
Test, the test copy made of Production; null if it failed. | |
test_environment_problem |
string or null | Why Test could not be made (Production stands); make it from Settings, Test copies. |
SetupInput
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | The company's legal name. (min length 1; max length 200) |
country |
string | yes | Where it is registered (GET /v1/setup/options). (pattern ^[A-Z]{2}$) |
subdivision |
string or null | The state (US) or province (Canada), ISO 3166-2; required there, refused elsewhere. (pattern ^[A-Z]{2}-[A-Z0-9]{1,3}$) |
|
standard |
string or null | The accounting standard's id, one of the country's. Omitted: the country's first (GET /v1/setup/options lists them, the default first). |
|
currency |
string or null | The currency of the books, one the country allows. Cannot be changed later. Omitted: the country's currency. (pattern ^[A-Z]{3}$) |
|
fiscal_year_start_month |
integer | The month the fiscal year starts, 1 to 12. (min 1; max 12; default 1) |
|
tax_period |
string or null | The tax return period, one of the country's; default: its first. | |
tax_number |
string or null | The tax registration number, when asked. (max length 40) | |
street |
string or null | Street. (max length 70) | |
building_number |
string or null | Number. (max length 16) | |
postal_code |
string or null | Postal code. (max length 16) | |
town |
string or null | Town. (max length 35) | |
industry |
"ecommerce" | "general" | "hospitality" | "manufacturing" | "retail" | "services" | "trade" |
The company's industry (GET /v1/industries): general, hospitality, services, trade, retail, ecommerce or manufacturing. It puts the screens of the industry first in the app's menu and on its Home; it switches nothing off, and every operation works the same in every industry. Default general; it can be changed later. (default "general") |
SetupOptions
| Field | Type | Required | Description |
|---|---|---|---|
countries |
array of CountryOption |
yes | The countries served, by name. |
SetupStatus
| Field | Type | Required | Description |
|---|---|---|---|
needed |
boolean | yes | True while the organization has no company and is not a test organization: its first administrator is asked to set it up. |
can_complete |
boolean | yes | True when the caller may set it up (an administrator, a person). |
Subdivision
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | yes | ISO 3166-2 code, e.g. US-NY. |
name |
string | yes | Its name, e.g. New York. |
YearCloseSummary
The close of a fiscal year as the list of fiscal years shows it.
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | Id of the year close. |
kind |
"catch_up" | "year_end" |
yes | year_end or catch_up. |
status |
"cancelled" | "closed" | "pending" | "reopened" |
yes | pending, closed, reopened or cancelled. |
result |
string | yes | The result of the year, profit positive. |
appropriated |
string | yes | What is appropriated so far, signed like the result. |
remainder |
string | yes | What is left to appropriate. |
appropriation_overdue |
boolean | yes | True when the general meeting is overdue and a remainder is unappropriated. |