---
title: "Generate Opening Entries"
method: POST
path: "/companies/{company_id}/opening-entries"
tags: ["accounting-settings"]
---

# Generate Opening Entries

`POST /companies/{company_id}/opening-entries`

## Request

- Base URL: `https://app.zeno-works.ch` (the document declares no server; this is the origin it was published from)
- URL: `https://app.zeno-works.ch/companies/{company_id}/opening-entries`
- Auth: none declared

Generate Eröffnungsbuchungen for ``target_fiscal_year`` (accountant).

Orchestration (COMPANY_SETUP_FEATURE_PLAN §4.4):

1. **txn 1 (short)** — load settings + chart rows (target FY) + the **accepted**
   balance-sheet snapshot for ``target_fiscal_year - 1`` (+ its lines) + the
   optional ``FiscalYearSummary`` + VAT rates; build the accountant request via the
   pure builder. **409** if there is no accepted prior-year snapshot (when one is
   held for acceptance, the message names its change set and residual); **422** if the
   builder refuses (needs_review lines / currency mismatch / unbalanced sheet).
2. accountant call — ``await`` the proposal **without holding any DB transaction**;
   **503** if accountant is unreachable/failed.
3. **txn 2 (short)** — persist the ``OpeningEntryProposal`` (request_json,
   request_hash, response, accountant_metadata, status, source_snapshot_id,
   opening_posting_date, generated_by) and return it.

**Creating the required prior-year snapshot** (ZET-159) — three paths, all
consent-aware (first sheet for the FY auto-accepted when Σdebit and Σcredit agree
within 1 ct; an unbalanced first sheet, or a conflicting one, lands ``proposed`` + a
pending change set, consented via ``POST .../change-sets/{set_id}/apply``):

* **document upload** — send the prior year's closing balance sheet through the
  accounting-documents intake (``upload_intake`` / ``POST
  .../accounting-documents``); the analyzer extracts and persists it;
* **structured figures** — ``POST /companies/{company_id}/balance-sheets``
  (``create_balance_sheet``) with the closing balances as JSON lines, for the
  established tenant with no closing-sheet document; file them under the year
  being **closed** (``target_fiscal_year - 1``);
* **from the ledger** — ``POST .../balance-sheets/from-gl`` for a year that ran
  inside Zeno and is sealed.

**A newly founded company** (ZCT-843) opens its first year from its **founding
balance sheet**: ``create_balance_sheet`` with ``founding=true``, ``fiscal_year`` = the
founding year itself and ``as_of_date`` = the founding date. An uploaded (or onboarded)
sheet dated inside the company's first year before its end is held with the question
``summary.founding_question`` instead of being filed as a closing sheet (ZET-395) —
answer it with ``POST .../change-sets/{set_id}/founding`` (or ``.../apply`` for the
closing sheet). In any other year a sheet dated before the year's end is held with
``summary.interim_question`` — ``.../interim`` keeps it as an interim sheet that never
opens a year, ``.../apply`` makes it the closing sheet (owner ruling 2026-09-30). A
founding sheet is its own kind — it never competes with that year's
closing sheet, and no other year's opening reads it. When the target year is the
company's first fiscal year and such a sheet exists, generation uses it automatically,
and the posting date defaults to its date (an explicit ``opening_posting_date`` wins);
the posted entries carry ``source_action='founding'``. With no prior year to carry
accounts from, the sheet's accounts must already be in the target year's chart.

**Opening balances on VAT control accounts are not taxable events.** The
generated proposal carries them (e.g. 1170/2200) untagged by design and the
``POST /opening-entries/{proposal_id}/post`` path books them without VAT codes.
Only a **hand-built** manual opening JE hits the composition guard — there,
``body.not_vat=true`` is the intended treatment, not a workaround.

## Path parameters

- `company_id` string, required

## Headers

- `authorization` string, nullable

## Cookies

- `access_token` string, nullable

## Request body

- OpeningEntryGenerateIn: ``POST /companies/{id}/opening-entries`` body (admin) — generate Eröffnungsbuchungen.
  - `target_fiscal_year` integer, required
  - `opening_posting_date` string, date, nullable
  - `include_appropriation` boolean

## Response `201`

Successful Response

- OpeningEntryProposalOut: A generated Eröffnungsbuchung proposal (the accountant ``ProposalResponse``). ``is_stale`` / ``stale_reasons`` / ``pending_newer`` are **computed** (never stored — see ``app.accounting.services.opening_staleness``): the list/get endpoints compare the current accepted prior-FY snapshot + semantic chart/VAT fingerprints against what the proposal was generated from. ``stale_reasons`` values: ``snapshot_replaced`` | ``chart_changed`` | ``vat_changed``. ``pending_newer`` flags newer uploaded data (proposed snapshot / open change set) awaiting consent — distinct from ``is_stale``. Defaults (fresh = not stale) apply where the fields are not computed, e.g. the just-generated proposal returned by POST.
  - `id` string, required
  - `company_id` string, required
  - `target_fiscal_year` integer, required
  - `source_snapshot_id` string, nullable
  - `opening_posting_date` string, date, required
  - `status` string, required
  - `request_hash` string, required
  - `response` object, nullable
  - `accountant_metadata` object, nullable
  - `generated_by` string, nullable
  - `created_at` string, date-time, required
  - `is_stale` boolean
  - `stale_reasons` string[]
  - `pending_newer` boolean
  - `is_posted` boolean
  - `posted_at` string, date-time, nullable
  - `risk_flags` RiskFlagOut[]
    - `severity` string, nullable
    - `code` string, nullable
    - `message` string, nullable
  - `unresolved_questions` string[]

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/zeno-works/apis/zeno-api.md) · [All operations](https://skmtc.dev/zeno-works/apis/zeno-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zeno-works/apis/zeno-api/revisions/1eb93dde146c?raw)
