---
title: "Create Balance Sheet"
method: POST
path: "/companies/{company_id}/balance-sheets"
tags: ["accounting-settings"]
---

# Create Balance Sheet

`POST /companies/{company_id}/balance-sheets`

## 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}/balance-sheets`
- Auth: none declared

Create a balance-sheet snapshot from structured figures (accountant) — ZET-159.

The figures-in sibling of the two existing snapshot creators — the
accounting-document upload (analyzer-extracted) and ``POST
.../balance-sheets/from-gl`` (Zeno-closed years) — for the **established tenant**
whose prior years closed outside Zeno and who has no closing-balance-sheet
document to upload: it takes the closing balances of ``fiscal_year`` (the year
being **closed**, i.e. the opening target year − 1) as JSON lines, sourced from
the predecessor system of record, and persists them through the same
consent-aware lifecycle as the document path:

* **first sheet** for the fiscal year → inserted ``accepted`` (immediately
  usable by ``POST .../opening-entries`` for ``fiscal_year + 1``) — **if** its
  Σdebit and Σcredit agree within 1 ct. An unbalanced first sheet is not accepted
  unseen: it lands ``proposed`` with a pending ``balances`` change set, exactly
  like a replacement below (ZCT-808);
* a **different** accepted sheet already exists → the new one lands
  ``proposed`` plus a pending ``balances`` change set — consent via ``POST
  .../change-sets/{set_id}/apply`` (find the set with ``GET .../change-sets``);
  the response's ``status`` field tells you which branch you got;
* **identical content** (hash over number/amount/side) already
  accepted/proposed → idempotent: the existing snapshot is returned unchanged
  (still 201; ``equity_roles`` are **not** restamped onto it).

``currency`` must equal the company base currency — 422 otherwise, because an
accepted off-currency sheet would hold the FY's accepted slot and permanently
422 at opening-entry generation. ``equity_roles`` codes must appear among the
submitted lines (422 otherwise) and are stamped only on a snapshot this call
created. Deferred failures are the generator's: an (accepted) unbalanced sheet or
``needs_review`` lines are stored as sent (parity with the document path) and
refused with 422 at ``POST .../opening-entries`` — clear review flags with
``PUT /balance-sheets/{snapshot_id}/lines``.

``founding=true`` (ZCT-843) stores a newly founded company's **founding balance
sheet** instead: filed under the founding year itself, outside the consent lifecycle
above (status ``founding``; a new one replaces the previous one), and refused (422)
unless it is the company's first year, dated inside it, balanced and fully reviewed,
with no closing sheet for the year before. ``generate_opening_entries`` for that year
then opens from it, on its date.

## Path parameters

- `company_id` string, required

## Headers

- `authorization` string, nullable

## Cookies

- `access_token` string, nullable

## Request body

- BalanceSheetCreateIn: ``POST /companies/{company_id}/balance-sheets`` body (accountant) — ZET-159. Structured (figures-in) creation of a balance-sheet snapshot for an established tenant whose prior years closed outside Zeno and for which no closing-balance-sheet document exists. Persists through the same consent-aware lifecycle as the document-upload path (first sheet accepted, conflict proposed + change set, same-content repeat idempotent).
  - `fiscal_year` integer, required: The fiscal year this balance sheet CLOSES. Opening-entry generation for year N reads the accepted sheet of N-1, so file the sheet under N-1 — not under the year being opened. Exception: a founding balance sheet (founding=true) is filed under the year it opens.
  - `founding` boolean: ZCT-843: a newly founded company's founding balance sheet (Gründungsbilanz) instead of a closing sheet. Filed under the founding year itself (its first fiscal year), with as_of_date = the founding date inside that year; it must balance, and no earlier fiscal year or prior-year closing sheet may exist. Stored as its own kind (status 'founding'): it never competes with that year's closing sheet and the next year's opening never reads it. generate_opening_entries for that year then uses it and posts on its date. A new founding sheet replaces the previous one.
  - `as_of_date` string, date, nullable
  - `currency` string, nullable: Must equal the company base currency (422 otherwise); omitted → the base currency.
  - `lines` BalanceSheetLineIn[], required
    - `account_number` string, required
    - `account_name` string, nullable
    - `category` 'asset' | 'liability' | 'equity' | 'income' | 'expense' | 'tax' | 'other' | 'unknown': The account-type axis of a ledger account (Swiss-KMU chart of accounts). Distinct from — but derived from — ``LineRole`` (the journal-line semantic role). ``unknown`` is the conservative default when classification can't decide; ``other`` covers genuinely ambiguous / closing (9xxx) accounts.
    - `side` 'credit' | 'debit', required
    - `amount_cents` integer, required
    - `sign_display` string, nullable
    - `needs_review` boolean
    - `is_contra` boolean
  - `equity_roles` BalanceSheetEquityRolesIn: Optional equity-role stamps for a created snapshot (ZET-159). Typed rather than free-form because the opening-entry builder matches these **exact keys** — a typo'd free-form dict would silently produce no tags and the proposal would fall back to the framework's ``equity_result_accounts`` priority list with zero feedback. Each provided code must appear among the submitted lines (422 otherwise).
    - `carry_forward_account_number` string, nullable: Chart code of the Gewinnvortrag account (e.g. 2970).
    - `annual_result_account_number` string, nullable: Chart code of the Jahresergebnis account (e.g. 2979).

## Response `201`

Successful Response

- BalanceSheetSnapshotOut: An immutable balance-sheet snapshot for a ``(company, fiscal_year)`` + its lines.
  - `id` string, required
  - `company_id` string, required
  - `fiscal_year` integer, required
  - `as_of_date` string, date, nullable
  - `currency` string, required
  - `status` 'accepted' | 'proposed' | 'superseded' | 'founding', required: Lifecycle of a balance-sheet snapshot. ``accepted`` is the single live snapshot per (company, fiscal_year); ``proposed`` is a consent-gated replacement awaiting the user's decision; ``superseded`` is a retired snapshot (replaced by a newer accepted one, or a discarded proposal). ``founding`` (ZCT-843) is a different KIND of sheet, not a lifecycle stage of a closing sheet: the founding balance sheet (Gründungsbilanz) a newly founded company's FIRST fiscal year opens from, filed under that year. It is deliberately a status value: every reader of "the accepted sheet of year N" — the consent lifecycle, the one-accepted-per-year index, the next year's opening, reconciliation, staleness — filters ``accepted``/``proposed``, so none of them can ever take a founding sheet for a closing one. At most one is live per company; a replacement marks it ``superseded``.
  - `source` string, required
  - `source_document_id` string, nullable
  - `totals` object
  - `snapshot_hash` string, required
  - `created_at` string, date-time, required
  - `superseded_at` string, date-time, nullable
  - `resolution_json` object, nullable
  - `equity_roles` object, nullable
  - `lines` BalanceSheetLineOut[]
    - `id` string, required
    - `snapshot_id` string, required
    - `account_number` string, required
    - `account_name` string, nullable
    - `category` 'asset' | 'liability' | 'equity' | 'income' | 'expense' | 'tax' | 'other' | 'unknown', required: The account-type axis of a ledger account (Swiss-KMU chart of accounts). Distinct from — but derived from — ``LineRole`` (the journal-line semantic role). ``unknown`` is the conservative default when classification can't decide; ``other`` covers genuinely ambiguous / closing (9xxx) accounts.
    - `amount_cents` integer, required
    - `sign_display` string, nullable
    - `side` 'credit' | 'debit', required
    - `is_contra` boolean
    - `needs_review` boolean, required
    - `created_at` string, date-time, required

## 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)
