StabisoDocs

API reference

Year-end close

Close a fiscal year into the annual-result account, lock it, reopen it by reversal, book the shareholders' resolution on the result (appropriation) and catch up the years booked before the books were closed.

Operation Summary Scope Idempotency-Key
GET /v1/entities/{entity}/appropriations List the appropriations of an entity read
POST /v1/entities/{entity}/appropriations Appropriate the result of a closed year draft required
POST /v1/entities/{entity}/appropriations/{appropriation_id}/reverse Reverse an appropriation draft required
GET /v1/entities/{entity}/fiscal-years/{year}/close Get the year-end close of a year read
POST /v1/entities/{entity}/fiscal-years/{year}/close Propose the year-end close draft required
GET /v1/entities/{entity}/fiscal-years/{year}/close-preview Preview the year-end close read
POST /v1/entities/{entity}/fiscal-years/{year}/close/cancel Cancel a proposed year-end close draft
GET /v1/entities/{entity}/fiscal-years/{year}/opening-balances Get the opening balances of a fiscal year read
POST /v1/entities/{entity}/fiscal-years/{year}/reopen-close Reopen a closed fiscal year approve
POST /v1/entities/{entity}/year-close/catch-up Close the years booked before the books were closed draft required

GET /v1/entities/{entity}/appropriations

List the appropriations of an entity

The posted appropriations, oldest first, with their destinations and whether a later reversal cancels them. An appropriation that waits for approval is in the approval queue (GET /v1/approvals) until it posts.

Required scope read (admin implies every scope)
Operation id list_appropriations_v1_entities__entity__appropriations_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 resolutions on the result of this fiscal year.

Example request

curl "$ERP_URL/v1/entities/CH01/appropriations" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK array of AppropriationOut
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_close_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "fiscal_year": 1,
    "entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "entry_number": "string",
    "resolution_date": "2026-09-21",
    "reference": "string",
    "amount": "string",
    "reverses_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "reversed": true,
    "destinations": [
      {}
    ],
    "created_by": {},
    "created_at": "2026-09-21T09:30:00Z"
  }
]

POST /v1/entities/{entity}/appropriations

Appropriate the result of a closed year

Book the shareholders' resolution on the result of a closed year as one entry dated resolution_date (source appropriation): for a profit Dr annual_result / Cr the destinations (retained earnings, legal profit reserve, voluntary reserve, dividend payable), for a loss the mirror image into the loss carried forward. A human (close_fiscal_year); by default a second person approves the entry. The sum of the destinations may not exceed what is left of the result (409 appropriation_exceeds_remainder, also refused by a database trigger); a partial appropriation leaves the rest visible in remainder. For a Swiss entity the answer carries warnings and withholding_tax: a dividend bears 35 % withholding tax, which the company must deduct, booked by hand for now. Needs an Idempotency-Key.

Required scope draft (admin implies every scope)
Idempotency-Key required
Operation id appropriate_v1_entities__entity__appropriations_post

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).

Request body

application/json (required): AppropriationInput

Field Type Required Description
fiscal_year integer yes The fiscal year whose closed result is appropriated.
resolution_date string (date) yes The date of the resolution, ISO 8601; the entry is dated then, in an open period (normally in the new year). A dividend is a liability only from this date.
reference string yes The resolution or minutes it rests on. (min length 1; max length 500)
destinations array of Destination yes Where the result goes. Their sum may not exceed what is left of the result; a partial appropriation leaves the rest visible. (min items 1)

Example request

curl -X POST "$ERP_URL/v1/entities/CH01/appropriations" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "destinations": [
    {
      "account": "2950",
      "amount": "3175.00",
      "kind": "legal_profit_reserve"
    },
    {
      "account": "2261",
      "amount": "30000.00",
      "kind": "dividend_payable"
    },
    {
      "amount": "30325.00",
      "kind": "retained_earnings"
    }
  ],
  "fiscal_year": 2026,
  "reference": "Minutes of the general meeting of 20 May 2027, item 3",
  "resolution_date": "2027-05-20"
}'

Responses

Status Meaning Body
201 Created AppropriationResult
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)

{
  "submit": {
    "entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "status": "string",
    "number": "string",
    "policy": {
      "decision": "allow",
      "reason": "string",
      "rule_id": "string",
      "table_id": "string"
    },
    "approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "reason": "string"
  },
  "amount": "string",
  "remainder_after": "string",
  "withholding_tax": "string",
  "withholding_tax_due": "2026-09-21",
  "warnings": [
    "string"
  ]
}

POST /v1/entities/{entity}/appropriations/{appropriation_id}/reverse

Reverse an appropriation

Withdraw an appropriation with an entry of its own (the lines swapped, dated on), through the same approval as the appropriation; once posted its amount is free again and the year can be reopened. A human (close_fiscal_year). 404 for an unknown appropriation; 409 for a reversal, or an appropriation that is already reversed. Needs an Idempotency-Key.

Required scope draft (admin implies every scope)
Idempotency-Key required
Operation id reverse_appropriation_v1_entities__entity__appropriations__appropriation_id__reverse_post

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
path appropriation_id string (uuid) yes Id of the appropriation (GET /v1/entities/{entity}/appropriations).

Request body

application/json (required): AppropriationReverseInput

Field Type Required Description
on string (date) yes The date of the reversing entry, in an open period.
reason string yes Why; kept in the audit trail. (min length 3; max length 1000)

Example request

curl -X POST "$ERP_URL/v1/entities/CH01/appropriations/3fa85f64-5717-4562-b3fc-2c963f66afa6/reverse" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "on": "2027-06-30",
  "reason": "Resolution withdrawn"
}'

Responses

Status Meaning Body
201 Created AppropriationResult
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)

{
  "submit": {
    "entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "status": "string",
    "number": "string",
    "policy": {
      "decision": "allow",
      "reason": "string",
      "rule_id": "string",
      "table_id": "string"
    },
    "approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "reason": "string"
  },
  "amount": "string",
  "remainder_after": "string",
  "withholding_tax": "string",
  "withholding_tax_due": "2026-09-21",
  "warnings": [
    "string"
  ]
}

GET /v1/entities/{entity}/fiscal-years/{year}/close

Get the year-end close of a year

The year close that holds the fiscal year (pending or closed), else the newest one (reopened or cancelled), with its result, what is appropriated and what is left (remainder), the closing entry and the approval waiting for a second person, and the resolutions booked against the result. 404 when the year was never proposed.

Required scope read (admin implies every scope)
Operation id get_close_v1_entities__entity__fiscal_years__year__close_get

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
path year integer yes The fiscal year, the calendar year it ends in, e.g. 2026.
query as_of string (date) or null The day appropriation_overdue is judged on, ISO 8601; default: today.

Example request

curl "$ERP_URL/v1/entities/CH01/fiscal-years/2026/close" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK YearCloseOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "entity": "string",
  "fiscal_year": 1,
  "kind": "catch_up",
  "status": "cancelled",
  "result": "string",
  "appropriated": "string",
  "remainder": "string",
  "appropriation_overdue": true,
  "journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "entry_number": "string",
  "entry_status": "string",
  "approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "totals_digest": "string",
  "proposed_by": {},
  "proposed_at": "2026-09-21T09:30:00Z",
  "closed_by": {},
  "closed_at": "2026-09-21T09:30:00Z",
  "cancelled_by": {},
  "cancelled_at": "2026-09-21T09:30:00Z",
  "cancel_reason": "string",
  "reopened_by": {},
  "reopened_at": "2026-09-21T09:30:00Z",
  "reopen_reason": "string",
  "reversal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "detail": {},
  "appropriations": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "fiscal_year_close_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "fiscal_year": 1,
      "entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "entry_number": "string",
      "resolution_date": "2026-09-21",
      "reference": "string",
      "amount": "string",
      "reverses_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "reversed": true,
      "destinations": [
        {}
      ],
      "created_by": {},
      "created_at": "2026-09-21T09:30:00Z"
    }
  ]
}

POST /v1/entities/{entity}/fiscal-years/{year}/close

Propose the year-end close

Close the fiscal year: write the closing entry and submit it through the approval policy.

A human proposes it (the policy action close_fiscal_year denies agents). It is refused with 409 year_close_blocked, listing blockers, while the year holds a draft entry, an entry or a document waiting for approval, or while its last period is not open, and with 409 role_missing while the entity has no account with the role annual_result. Otherwise the year is locked at once: from the proposal until the close is cancelled or reopened, no other entry is posted into the fiscal year, whatever its source (a payment, the FX revaluation and a bank posting wait). By default a second person approves the entry (POST /v1/approvals/{approval_id}/approve, close_entries_need_a_second_person); the approval computes the totals again and refuses with 409 year_changed if they differ from the proposal's. The response's submit says whether the entry is posted or waits. Needs an Idempotency-Key.

Required scope draft (admin implies every scope)
Idempotency-Key required
Operation id propose_close_v1_entities__entity__fiscal_years__year__close_post

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
path year integer yes The fiscal year, the calendar year it ends in, e.g. 2026.

Example request

curl -X POST "$ERP_URL/v1/entities/CH01/fiscal-years/2026/close" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

Responses

Status Meaning Body
201 Created CloseResult
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)

{
  "close": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "entity": "string",
    "fiscal_year": 1,
    "kind": "catch_up",
    "status": "cancelled",
    "result": "string",
    "appropriated": "string",
    "remainder": "string",
    "appropriation_overdue": true,
    "journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "entry_number": "string",
    "entry_status": "string",
    "approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "totals_digest": "string",
    "proposed_by": {},
    "proposed_at": "2026-09-21T09:30:00Z",
    "closed_by": {},
    "closed_at": "2026-09-21T09:30:00Z",
    "cancelled_by": {},
    "cancelled_at": "2026-09-21T09:30:00Z",
    "cancel_reason": "string",
    "reopened_by": {},
    "reopened_at": "2026-09-21T09:30:00Z",
    "reopen_reason": "string",
    "reversal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "detail": {},
    "appropriations": [
      {
        "id": null,
        "fiscal_year_close_id": null,
        "fiscal_year": null,
        "entry_id": null,
        "entry_number": null,
        "resolution_date": null,
        "reference": null,
        "amount": null,
        "reverses_id": null,
        "reversed": null,
        "destinations": null,
        "created_by": null,
        "created_at": null
      }
    ]
  },
  "submit": {
    "entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "status": "string",
    "number": "string",
    "policy": {
      "decision": "allow",
      "reason": "string",
      "rule_id": "string",
      "table_id": "string"
    },
    "approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "reason": "string"
  }
}

GET /v1/entities/{entity}/fiscal-years/{year}/close-preview

Preview the year-end close

What closing the fiscal year would post, computed now, and what stands in the way.

The closing entry brings every profit and loss account of the year to zero against the account with the role annual_result; result is the profit (positive) or loss, lines the entry, totals_digest the fingerprint of the figures it comes from. blockers lists what prevents a proposal (draft entries and entries or documents waiting for approval dated in the year, a last period that is not open, a missing annual-result account, a year that is already held); can_propose is true when there is none. Nothing is stored, so an agent may call it. Closing entries of the year (source close) are left out of the figures, so the preview of a closed year is what it was before the close.

Required scope read (admin implies every scope)
Operation id close_preview_v1_entities__entity__fiscal_years__year__close_preview_get

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
path year integer yes The fiscal year, the calendar year it ends in, e.g. 2026.

Example request

curl "$ERP_URL/v1/entities/CH01/fiscal-years/2026/close-preview" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK ClosePreview
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",
  "fiscal_year": 1,
  "result": "string",
  "result_account": "string",
  "result_account_suggestion": "string",
  "lines": [
    {
      "account": "string",
      "name": "string",
      "debit": "string",
      "credit": "string"
    }
  ],
  "totals_digest": "string",
  "date": "2026-09-21",
  "period": "string",
  "period_status": "string",
  "status": "cancelled",
  "blockers": [
    "string"
  ],
  "can_propose": true,
  "warnings": [
    "string"
  ],
  "blocker_reasons": [
    {
      "words": "string",
      "code": "string",
      "details": {},
      "text": "string"
    }
  ],
  "warning_reasons": [
    {
      "words": "string",
      "code": "string",
      "details": {},
      "text": "string"
    }
  ]
}

POST /v1/entities/{entity}/fiscal-years/{year}/close/cancel

Cancel a proposed year-end close

Withdraw a proposal that is still pending: its approval request and its closing entry are rejected and the lock on the year is lifted. A human only (close_fiscal_year), with a reason, audited as year_close.cancel. A rejected approval does the same. 404 when no proposal is pending; 409 for a closed year, which is reopened instead.

Required scope draft (admin implies every scope)
Idempotency-Key not used
Operation id cancel_close_v1_entities__entity__fiscal_years__year__close_cancel_post

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
path year integer yes The fiscal year, the calendar year it ends in, e.g. 2026.

Request body

application/json (required): erp__features__close__contracts__year_close__ReasonInput

Field Type Required Description
reason string yes Why; kept in the audit trail and on the close. (min length 3; max length 1000)

Example request

curl -X POST "$ERP_URL/v1/entities/CH01/fiscal-years/2026/close/cancel" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "The audit found a missing accrual"
}'

Responses

Status Meaning Body
200 OK YearCloseOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "entity": "string",
  "fiscal_year": 1,
  "kind": "catch_up",
  "status": "cancelled",
  "result": "string",
  "appropriated": "string",
  "remainder": "string",
  "appropriation_overdue": true,
  "journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "entry_number": "string",
  "entry_status": "string",
  "approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "totals_digest": "string",
  "proposed_by": {},
  "proposed_at": "2026-09-21T09:30:00Z",
  "closed_by": {},
  "closed_at": "2026-09-21T09:30:00Z",
  "cancelled_by": {},
  "cancelled_at": "2026-09-21T09:30:00Z",
  "cancel_reason": "string",
  "reopened_by": {},
  "reopened_at": "2026-09-21T09:30:00Z",
  "reopen_reason": "string",
  "reversal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "detail": {},
  "appropriations": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "fiscal_year_close_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "fiscal_year": 1,
      "entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "entry_number": "string",
      "resolution_date": "2026-09-21",
      "reference": "string",
      "amount": "string",
      "reverses_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "reversed": true,
      "destinations": [
        {}
      ],
      "created_by": {},
      "created_at": "2026-09-21T09:30:00Z"
    }
  ]
}

GET /v1/entities/{entity}/fiscal-years/{year}/opening-balances

Get the opening balances of a fiscal year

The balance sheet at the day before the year starts: what the books continue with. Nothing is posted at the start of a year; after a year-end close the profit and loss accounts start at zero and the balance sheet accounts carry their balances, so this equals the closing balance sheet of the year before, account by account. 422 for a year that does not exist.

Required scope read (admin implies every scope)
Operation id opening_balances_v1_entities__entity__fiscal_years__year__opening_balances_get

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
path year integer yes The fiscal year, the calendar year it ends in, e.g. 2026.

Example request

curl "$ERP_URL/v1/entities/CH01/fiscal-years/2026/opening-balances" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK BalanceSheet
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "entity": "string",
  "currency": "string",
  "as_of": "2026-09-21",
  "assets": "string",
  "liabilities": "string",
  "equity": "string",
  "unclosed_earnings": "string",
  "balanced": true,
  "sections": [
    {
      "report_line": "string",
      "amount": "string",
      "accounts": [
        null
      ],
      "comparative": null,
      "budget": null
    }
  ],
  "translation": {
    "functional_currency": "string",
    "currency": "string",
    "source": "string",
    "dataset_version": "string",
    "closing_rate": null,
    "average_rates": [
      null
    ],
    "historical_rates": [
      null
    ],
    "difference": "string",
    "reconciled": true
  },
  "comparative": {
    "as_of": "2026-09-21",
    "assets": {
      "amount": null,
      "change": null,
      "change_pct": null
    },
    "liabilities": {
      "amount": null,
      "change": null,
      "change_pct": null
    },
    "equity": {
      "amount": null,
      "change": null,
      "change_pct": null
    },
    "unclosed_earnings": {
      "amount": null,
      "change": null,
      "change_pct": null
    }
  },
  "notes": [
    {
      "words": "string",
      "code": "string",
      "details": {},
      "text": "string"
    }
  ]
}

POST /v1/entities/{entity}/fiscal-years/{year}/reopen-close

Reopen a closed fiscal year

The decision to reopen a closed year: it reopens the period that holds the closing entry and reverses the closing entry itself (same date, same period), then sets the close reopened; periods reopen as before and the year can be closed again. A human with the approve scope and the policy's reopen_period (agents are denied), a reason, audited as year_close.reopen. The reversal needs no second approval: this decision covers it. Refused with 409, listing them, while a resolution on the result is still posted or waits for approval (reverse it first); 404 when the year has no closed year close. A period of a closed year cannot be reopened by POST /v1/periods/{period_id}/reopen while its close stands.

Required scope approve (admin implies every scope)
Idempotency-Key not used
Operation id reopen_close_v1_entities__entity__fiscal_years__year__reopen_close_post

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
path year integer yes The fiscal year, the calendar year it ends in, e.g. 2026.

Request body

application/json (required): erp__features__close__contracts__year_close__ReasonInput

Field Type Required Description
reason string yes Why; kept in the audit trail and on the close. (min length 3; max length 1000)

Example request

curl -X POST "$ERP_URL/v1/entities/CH01/fiscal-years/2026/reopen-close" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "The audit found a missing accrual"
}'

Responses

Status Meaning Body
200 OK YearCloseOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "entity": "string",
  "fiscal_year": 1,
  "kind": "catch_up",
  "status": "cancelled",
  "result": "string",
  "appropriated": "string",
  "remainder": "string",
  "appropriation_overdue": true,
  "journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "entry_number": "string",
  "entry_status": "string",
  "approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "totals_digest": "string",
  "proposed_by": {},
  "proposed_at": "2026-09-21T09:30:00Z",
  "closed_by": {},
  "closed_at": "2026-09-21T09:30:00Z",
  "cancelled_by": {},
  "cancelled_at": "2026-09-21T09:30:00Z",
  "cancel_reason": "string",
  "reopened_by": {},
  "reopened_at": "2026-09-21T09:30:00Z",
  "reopen_reason": "string",
  "reversal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "detail": {},
  "appropriations": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "fiscal_year_close_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "fiscal_year": 1,
      "entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "entry_number": "string",
      "resolution_date": "2026-09-21",
      "reference": "string",
      "amount": "string",
      "reverses_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "reversed": true,
      "destinations": [
        {}
      ],
      "created_by": {},
      "created_at": "2026-09-21T09:30:00Z"
    }
  ]
}

POST /v1/entities/{entity}/year-close/catch-up

Close the years booked before the books were closed

For an entity that has been posting for years: one entry (source close), dated the first day of first_clean_year, brings the cumulative balance of every profit and loss account of everything before that date to zero against retained_earnings. The most recent year may stay unappropriated (leave_latest_year_unappropriated): its result goes to annual_result and is appropriated later like a closed year's. A human (close_fiscal_year); by default a second person approves; the same lock, digest and cancellation apply as to a year-end close. 409 catch_up_period_not_open names the first period of the year when it is not open (open it or name a later year). Needs an Idempotency-Key.

Required scope draft (admin implies every scope)
Idempotency-Key required
Operation id catch_up_v1_entities__entity__year_close_catch_up_post

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).

Request body

application/json (required): CatchUpInput

Field Type Required Description
first_clean_year integer yes The first fiscal year that starts with its profit and loss accounts at zero. The entry is dated its first day, in its first period, which must be open.
leave_latest_year_unappropriated boolean or null Whether the year before first_clean_year stays in the annual-result account because its general meeting is still ahead (its result is then appropriated later, like a closed year's). Default: true when that year ended less than six months before the first day of first_clean_year. Every earlier year goes to retained earnings.

Example request

curl -X POST "$ERP_URL/v1/entities/CH01/year-close/catch-up" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "first_clean_year": 2027,
  "leave_latest_year_unappropriated": true
}'

Responses

Status Meaning Body
201 Created CatchUpResult
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)

{
  "closes": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "entity": "string",
      "fiscal_year": 1,
      "kind": "catch_up",
      "status": "cancelled",
      "result": "string",
      "appropriated": "string",
      "remainder": "string",
      "appropriation_overdue": true,
      "journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "entry_number": "string",
      "entry_status": "string",
      "approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "totals_digest": "string",
      "proposed_by": {},
      "proposed_at": "2026-09-21T09:30:00Z",
      "closed_by": {},
      "closed_at": "2026-09-21T09:30:00Z",
      "cancelled_by": {},
      "cancelled_at": "2026-09-21T09:30:00Z",
      "cancel_reason": "string",
      "reopened_by": {},
      "reopened_at": "2026-09-21T09:30:00Z",
      "reopen_reason": "string",
      "reversal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "detail": {},
      "appropriations": [
        null
      ]
    }
  ],
  "submit": {
    "entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "status": "string",
    "number": "string",
    "policy": {
      "decision": "allow",
      "reason": "string",
      "rule_id": "string",
      "table_id": "string"
    },
    "approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "reason": "string"
  },
  "warnings": [
    "string"
  ]
}

Schemas

AppropriationInput

Field Type Required Description
fiscal_year integer yes The fiscal year whose closed result is appropriated.
resolution_date string (date) yes The date of the resolution, ISO 8601; the entry is dated then, in an open period (normally in the new year). A dividend is a liability only from this date.
reference string yes The resolution or minutes it rests on. (min length 1; max length 500)
destinations array of Destination yes Where the result goes. Their sum may not exceed what is left of the result; a partial appropriation leaves the rest visible. (min items 1)

AppropriationOut

Field Type Required Description
id string (uuid) yes Id of the appropriation.
fiscal_year_close_id string (uuid) yes The year close it appropriates.
fiscal_year integer yes The fiscal year whose result it appropriates.
entry_id string (uuid) yes The journal entry (source appropriation).
entry_number string or null yes Its number.
resolution_date string (date) yes The date of the shareholders' resolution.
reference string yes The resolution or minutes it rests on.
amount string yes Signed like the result of the year: positive for a profit appropriated, negative for a loss carried forward; the reversal of an appropriation has the opposite sign.
reverses_id string (uuid) or null yes For a reversal, the appropriation it reverses; else null.
reversed boolean yes True when a later reversal cancels this appropriation.
destinations array of object yes Where it went: kind, account code and amount.
created_by object yes Who recorded it, as an actor object.
created_at string (date-time) yes When it was recorded (UTC).

AppropriationResult

Field Type Required Description
submit SubmitResult yes How the entry went through the approval policy (a second person approves it by default).
amount string yes The signed amount appropriated, like the result.
remainder_after string yes What would be left of the result once this and every earlier appropriation is posted.
withholding_tax string or null yes For a Swiss entity with a dividend destination: the 35 % withholding tax the entry books as payable (dividends under the notification procedure excluded). Null otherwise.
withholding_tax_due string (date) or null When the withholding tax (or the notification) is due: 30 days after the dividend falls due, taken as the resolution date (Art. 16 para. 1 lit. c VStG). Null without a Swiss dividend.
warnings array of string yes Things to do by hand: the Swiss withholding tax on a dividend and the legal profit reserve.

AppropriationReverseInput

Field Type Required Description
on string (date) yes The date of the reversing entry, in an open period.
reason string yes Why; kept in the audit trail. (min length 3; max length 1000)

CatchUpInput

Field Type Required Description
first_clean_year integer yes The first fiscal year that starts with its profit and loss accounts at zero. The entry is dated its first day, in its first period, which must be open.
leave_latest_year_unappropriated boolean or null Whether the year before first_clean_year stays in the annual-result account because its general meeting is still ahead (its result is then appropriated later, like a closed year's). Default: true when that year ended less than six months before the first day of first_clean_year. Every earlier year goes to retained earnings.

CatchUpResult

Field Type Required Description
closes array of YearCloseOut yes The year closes the entry belongs to (kind catch_up): the earlier years carried forward, and the latest year when it stays unappropriated.
submit SubmitResult yes How the entry went through the approval policy.
warnings array of string yes Things to know, e.g. a year left unappropriated.

CloseLine

Field Type Required Description
account string yes Account code.
name string yes Account name.
debit string yes Debit in the functional currency; zero on a credit line.
credit string yes Credit in the functional currency; zero on a debit line.

ClosePreview

What closing a fiscal year would post, computed now; nothing is stored.

Field Type Required Description
entity string yes Entity code.
fiscal_year integer yes The fiscal year.
result string yes The result of the year in the functional currency: a profit is positive, a loss negative. It is what the closing entry puts on the annual-result account.
result_account string or null yes Code of the account with the role annual_result; null when the entity has none (a proposal is refused until it has).
result_account_suggestion string or null yes When result_account is null: the code the entity's chart template gives the annual-result account (2979 in the Swiss chart, 3199 in the generic one), to add with POST /v1/entities/{entity}/accounts and the role annual_result (one person); null when the entity has the account or its template is not known.
lines array of CloseLine yes The closing entry: every profit and loss account with a balance brought to zero, and the result on the annual-result account.
totals_digest string yes SHA-256 of the account totals the lines come from; the approval checks it again.
date string (date) or null yes The date of the entry: the last day of the year; null for a year that has no periods.
period string or null yes The period the entry is posted in, YYYY-NN: the year's last adjustment period when it has any, else its last regular period.
period_status string or null yes open or closed; the period must be open.
status "cancelled" | "closed" | "pending" | "reopened" or null yes The state of the close that holds the year (pending or closed), or null when nothing holds it.
blockers array of string yes What prevents a proposal now: drafts and entries or documents waiting for approval dated in the year, a last period that is not open, a missing annual-result account, and the period-end runs (depreciation, FX revaluation, deferred tax) of the year's last regular period that are due and not done. Empty when the year can be proposed.
can_propose boolean yes True when blockers is empty and the year is not held.
warnings array of string yes Things to know that do not prevent the proposal.
blocker_reasons array of Reason blockers by code, in the same order (erp.contracts.words.YEAR_END): the screens word each as yearEnd.obstacle.<code> from its details.
warning_reasons array of Reason warnings by code, in the same order.

CloseResult

Field Type Required Description
close YearCloseOut yes The year close as stored.
submit SubmitResult yes How the closing entry went through the approval policy: posted for an organization whose policy lets one person post it, else pending_approval with the approval_id a second person decides.

Destination

Field Type Required Description
kind "dividend_payable" | "group_dividend" | "legal_profit_reserve" | "retained_earnings" | "voluntary_reserve" yes retained_earnings (profit or loss carried forward; the account defaults to the one with the role retained_earnings), legal_profit_reserve or voluntary_reserve (equity accounts you name), dividend_payable (a liability account you name; a profit only) or group_dividend (a dividend to a shareholder that is another entity of the organization: the line goes to the account with the role ic_other_payable, and when the entry posts the shareholder gets a draft entry to accept, ADR-0025; a profit only).
account string or null Account code in the entity. Required except for retained_earnings and group_dividend (which takes none). It must be active and not a control account.
shareholder string or null For group_dividend only: the code (or id) of the shareholder entity, which needs an active intercompany relationship with this one.
amount decimal string yes The amount, positive, in the functional currency (a decimal string). For a dividend the gross amount. (greater than 0)
withholding_tax "deduct" | "notification" or null A dividend of a Swiss entity: deduct (the default) books 35 % of the gross dividend as Swiss withholding tax payable (role withholding_tax_payable) and the rest as the dividend payable; notification (a group_dividend to a Swiss shareholder that holds at least 10 %, Art. 26a VStV) books the gross dividend and the tax is declared, not paid. Empty for anything else.

YearCloseOut

Field Type Required Description
id string (uuid) yes Id of the year close.
entity string yes Entity code.
fiscal_year integer yes The fiscal year it closes.
kind "catch_up" | "year_end" yes year_end (the closing entry of the year) or catch_up (the entry that closes the years booked before the books were closed).
status "cancelled" | "closed" | "pending" | "reopened" yes pending (the closing entry waits for a second person; the year is locked), closed (posted; the year is locked), reopened (reversed by a reopening) or cancelled (withdrawn or rejected; the lock is lifted).
result string yes The result of the year, profit positive.
appropriated string yes What shareholders' resolutions have appropriated so far, signed like the result.
remainder string yes result less appropriated: what is left to resolve.
appropriation_overdue boolean yes True for a Swiss entity whose general meeting is due (six months after the year end, Art. 699 para. 2 OR) while a remainder is unappropriated.
journal_entry_id string (uuid) yes The closing entry.
entry_number string or null yes Its number once posted.
entry_status string yes pending_approval, posted or rejected.
approval_id string (uuid) or null yes The approval request of a closing entry that waits for a second person: POST /v1/approvals/{approval_id}/approve (not by the person who proposed it).
totals_digest string yes SHA-256 of the account totals it was computed from.
proposed_by object yes Who proposed it, as an actor object.
proposed_at string (date-time) yes When it was proposed (UTC).
closed_by object or null yes Who posted it; null until closed.
closed_at string (date-time) or null yes When it was posted; null until closed.
cancelled_by object or null yes Who cancelled it, if cancelled.
cancelled_at string (date-time) or null yes When it was cancelled, if cancelled.
cancel_reason string or null yes Why it was cancelled, if cancelled.
reopened_by object or null yes Who reopened it, if reopened.
reopened_at string (date-time) or null yes When it was reopened, if reopened.
reopen_reason string or null yes Why it was reopened, if reopened.
reversal_entry_id string (uuid) or null yes The reversal of the closing entry that a reopening posted.
detail object yes For a catch-up, the result of each earlier year; empty for a year-end close.
appropriations array of AppropriationOut yes The resolutions booked against its result, oldest first.

erp__features__close__contracts__year_close__ReasonInput

Field Type Required Description
reason string yes Why; kept in the audit trail and on the close. (min length 3; max length 1000)
Stabiso, the Enterprise System of Record · generated from the code and its guides · release 20b7dd5b0c4d