{"openapi":"3.1.2","info":{"title":"/dev/accounting API","version":"0.1.0","summary":"GoBD-compliant German double-entry accounting as an API.","description":"API-first, GoBD-compliant German accounting platform by /dev/null GmbH.\n\n## Machine authentication\n\nA deployment selects one exclusive trust boundary. Public machine deployments\nuse `AUTH_MODE=machine`: send `Authorization: DPoP <access-token>`, a signed\n`DPoP` proof and the target public `ten_` handle in `X-Accounting-Tenant`.\nThe issuer is `https://auth.d0.eu`; the OAuth resource and sole token audience\nare `https://api.d0.eu/accounting`. This resource is distinct from the canonical\nHTTP base `https://api.d0.eu/accounting/v1` and silent alias\n`https://api.accounting.d0.eu/v1`. Sign each proof for the actual public host,\nmethod and path; query and fragment do not participate in the proof URI match.\nAccess tokens use PS256 and DPoP proofs use ES256. Client authentication at the\nissuer uses `private_key_jwt`, with a separate key from the DPoP proof key.\n\nA `401 use_dpop_nonce` response supplies `DPoP-Nonce`. Retry with that nonce\nand a newly signed proof with a fresh `jti`; retain the same Idempotency-Key\nwhen retrying a mutation. Never reuse a proof, including across API instances.\nEach operation requires the intersection of token scopes, current identity\ngrants and local membership permissions. Revocation and permission checks\nprecede idempotent response replay.\n\nEach operation publishes its required machine scope in\n`x-d0-machine-scopes`. A contract test compares this value with the actual\nauthorization table; it is independent of the legacy assertion deployment.\n\nAuthentication failures are `401 invalid_token` or `401 invalid_dpop_proof`;\nmissing permissions are `403 insufficient_scope`. These responses carry a\nDPoP `WWW-Authenticate` challenge. An expired authorization checkpoint fails\nclosed with `503 security_projection_stale`; unavailable authorization state\nor verification keys produce `503 security_projection_unavailable`.\nLegacy gateway assertion deployments explicitly select `AUTH_MODE=assertion`\nand retain the bearerAuth contract. Machine mode never falls back to Bearer\ncredentials or client-supplied internal assertion headers. The security\nalternatives below describe deployment modes, not interchangeable credentials.\n\n## Isolated customer test environment\n\nThe separate test deployment uses the sole OAuth resource and audience\n`https://api.test.d0.eu/accounting`, the canonical API base\n`https://api.test.d0.eu/accounting/v1`, and a different database, runtime\nlogin, authorization snapshots, and HMAC keys. Production tokens and clients\ncannot cross into test or vice versa. The test API exposes only the synthetic\naccount, open-period, unposted-draft, and trial-balance operations listed in\n`docs/accounting-test-environment.md`; no posting, archive, export, webhook,\nprovider, payment, or mail side effect is available there.\n\n## Accounting Console human BFF\n\nThe separate human management surface is\n`https://api.d0.eu/accounting/console/v1`. It accepts only PS256 Bearer access\ntokens for client `developers.accounting.d0.eu`, sole audience\n`https://api.d0.eu/accounting`, and the current signed human membership\nroster. `GET /status` and owner-only `POST /activation` establish a tenant;\n`GET /limits` reports shared organization and client quota state. With\n`X-Accounting-Tenant: ten_...`, the selected tenant, membership, and webhook\nendpoint management routes use the same bodies and schemas as the paths\nbelow. Local webhook-manager delegation, redacted audit, delivery\ndiagnostics and guarded replay are specified in\n`docs/accounting-console.md`. Human console requests never accept machine\nDPoP credentials or client-supplied organization authority.\n\nOn the test deployment, the BFF uses client\n`developers.accounting.test.d0.eu`, sole audience\n`https://api.test.d0.eu/accounting`, and\n`https://api.test.d0.eu/accounting/console/v1`. The test Console has\nactivation, status, limits, tenant and membership management, local\nwebhook-manager delegation and redacted audit. Its closed\n`/explorer` subpath maps only the synthetic operations in\n`docs/accounting-test-environment.md` to the same request/response schemas\nbelow, under the current human roster and Accounting-local permissions.\n\n## Money\n\nAmounts are exchanged as an object of an exact integer string in **minor\nunits** and an ISO-4217 currency — `{\"amount\": \"150000\", \"currency\": \"EUR\"}`\nis 1500,00 €. Never parse an amount into a binary floating-point type.\n\n## Idempotency\n\nEvery mutating request MUST carry an `Idempotency-Key`. Replaying a key with\nthe same complete request replays the original response verbatim; reusing it\nwith a different body media type (`Content-Type`), body, query, or conditional\n`If-Match` validator is rejected with `422 idempotency-key-reused`. A request\nwhose key is still in flight gets `409 request-in-progress` and may be retried.\n\n## Errors\n\nFailures are `application/problem+json` (RFC 9457). The stable, machine-readable\ndiscriminator is `type`; `title` and `detail` are prose and may change.\n\n## Events\n\nPosting a Buchung emits a durable `journal_entry.posted` event. Terminal\ndocument archive, preservation, validation, and ZUGFeRD generation results\nemit `devacc.async-event.v1` envelopes. Subscribe with `eventTypes: []` to\nreceive these and future topics; explicit topic filters currently select only\n`journal_entry.posted`. Their matching,\nactive endpoint recipients and each recipient's routing URL are snapshotted\nwhen the event is committed. Delivery attempts are signed with HMAC-SHA256\nand may be repeated (see the `webhooks` section). Consumers MUST dedupe on\n`event_id`; only journal events carry `resource.seq` for ordering.\n","contact":{"name":"/dev/null GmbH","url":"https://d0.eu","email":"api@d0.eu"},"license":{"name":"Proprietary","identifier":"LicenseRef-devnull-proprietary"}},"servers":[{"url":"https://api.d0.eu/accounting/v1"}],"paths":{"/tax-rules":{"get":{"operationId":"listTaxRules","x-d0-machine-scopes":["accounting.read"],"summary":"List published optional tax rules","description":"Returns immutable global reference rules. An empty list means no qualified rules are published; ordinary consumer-owned taxKey remains valid.","tags":["tax"],"parameters":[{"$ref":"#/components/parameters/Limit"},{"name":"after","in":"query","description":"Rule ID returned as nextCursor by the previous page.","schema":{"type":"string","maxLength":128}}],"responses":{"200":{"description":"Bounded page of rules, ordered by ID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxRulePage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/tax-calculations":{"post":{"operationId":"calculateTax","x-d0-machine-scopes":["accounting.read"],"summary":"Calculate tax from an explicitly selected published rule","description":"Calculates an exact amount using the selected rule and its effective dates. It makes no posting or legal classification decision.","tags":["tax"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"description":"Explicit rule, booking date and net amount; no classification is inferred.","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxCalculationInput"}}}},"responses":{"200":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"Exact calculation and immutable rule evidence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxCalculation"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/reports/financial-statements":{"get":{"operationId":"getFinancialStatements","x-d0-machine-scopes":["accounting.read"],"summary":"Read mapped Bilanz and GuV for one fiscal year","description":"Requires accounting.read. An explicit immutable mapping classifies every\naccount with posted activity in the selected fiscal-year/currency/asOf view. Unknown or foreign year\nand mapping IDs return 404; missing account classification returns\n422 incomplete-account-mapping, including accounts with zero net activity.\nAmounts use only booking dates inside the year and the inclusive asOf\nposting cutoff. Earlier years are excluded: opening balances must be\nordinary evidence-backed entries or carryforward in this year.\nAutomated fiscal-year closing entries are excluded so they cannot erase\nGuV movements or count the result twice in equity. Other posted entries,\nincluding opening and Storno, contribute normally. A stale asOf yields a\nhistorical report, not an assertion that finalization has occurred.\n\nMappings and labels are supplied by the integrator, never inferred from\naccount numbers or tax keys. Asset and expense amounts are debit minus\ncredit; liability, equity and income use the opposite sign. result is\nincome minus expense; balanceDifference is assets minus liabilities,\nequity and result. Each currency is separate. This is an accounting\nreport under the chosen mapping, not statutory filing validation.\n\nData sorts by classification, reportCode and currency; account drilldowns\nsort by the mapping's frozen accountNo then accountId. Follow accountId\nto its statement with the response dates and asOf. That statement also\ncontains closing entries; use closingEntryIds to reconcile their exclusion.\nEmpty views return empty arrays. More than 10000 account/currency rows\nreturns 413 report-too-large; any signed-int64 amount overflow fails the\nwhole response with 422 balance-overflow.\n","tags":["accounts"],"parameters":[{"name":"fiscalYearId","in":"query","required":true,"description":"Fiscal year whose inclusive dates delimit posted activity.","schema":{"$ref":"#/components/schemas/FiscalYearId"}},{"name":"mappingRevisionId","in":"query","required":true,"description":"Immutable consumer-owned account classification.","schema":{"$ref":"#/components/schemas/AccountMappingId"}},{"$ref":"#/components/parameters/PostingSnapshot"},{"$ref":"#/components/parameters/ReportCurrency"}],"responses":{"200":{"description":"Bilanz and GuV sections with account drilldowns and currency totals.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancialStatements"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/fiscal-years":{"get":{"operationId":"listFiscalYears","x-d0-machine-scopes":["accounting.read"],"summary":"List fiscal years","description":"Returns fiscal years in start-date order for the calling tenant.","tags":["fiscal-years"],"parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/FiscalYearCursor"}],"responses":{"200":{"description":"One page of fiscal years.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FiscalYearPage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]},"post":{"operationId":"createFiscalYear","x-d0-machine-scopes":["accounting.write"],"summary":"Create a fiscal year","description":"Creates an open fiscal year with inclusive dates; tenant years cannot overlap.","tags":["fiscal-years"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"required":true,"description":"Consumer-selected fiscal-year dates and label.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FiscalYearCreate"}}}},"responses":{"201":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"The created fiscal year.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FiscalYear"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/fiscal-years/{yearId}":{"parameters":[{"$ref":"#/components/parameters/FiscalYearId"}],"get":{"operationId":"getFiscalYear","x-d0-machine-scopes":["accounting.read"],"summary":"Fetch one fiscal year","description":"Returns one year of the calling tenant.","tags":["fiscal-years"],"responses":{"200":{"description":"The fiscal year.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FiscalYear"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/fiscal-years/{yearId}/finalize":{"parameters":[{"$ref":"#/components/parameters/FiscalYearId"}],"post":{"operationId":"finalizeFiscalYear","x-d0-machine-scopes":["accounting.periods.close"],"summary":"Finalize a fiscal year","description":"Requires exact coverage by contained, festgeschrieben periods and no\nunposted drafts in the year. Finalization is irreversible. A fresh-key\nreplay returns the original finalization time. P&L closing is optional;\ncarryforward incorporates any remaining income or expense balance.\n","tags":["fiscal-years"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"responses":{"200":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"The finalized fiscal year.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FiscalYear"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/fiscal-years/{yearId}/close":{"parameters":[{"$ref":"#/components/parameters/FiscalYearId"}],"post":{"operationId":"closeFiscalYear","x-d0-machine-scopes":["accounting.periods.close"],"summary":"Close income and expense accounts","description":"Derives every currency's income and expense balance from posted entries\ninside this year using an immutable caller-selected mapping. Posts inverse\nlines against an explicit equity result account with one archived Beleg,\nthen seals the closing period in the same transaction. All other periods\nmust already be sealed. If all P&L balances are zero, the period is sealed\nand entries is empty. A repeat with identical inputs returns the prior result.\n","tags":["fiscal-years"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"required":true,"description":"Closing period, mapping, result equity account and archived Beleg.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FiscalYearClose"}}}},"responses":{"200":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"Posted closing entries and sealed period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FiscalYearCloseResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/fiscal-years/{yearId}/carryforward":{"parameters":[{"$ref":"#/components/parameters/FiscalYearId"}],"post":{"operationId":"carryforwardFiscalYear","x-d0-machine-scopes":["accounting.write"],"summary":"Post an opening balance into the next fiscal year","description":"Source year must be finalized; target starts on the following day. For\none currency, derives source-year posted balances under the selected\nimmutable mapping. Asset, liability and equity balances carry to the same\naccounts; remaining income and expense net goes to the explicit equity\nresult account. The target year must have no earlier posted entries in\nthat currency. One archived Beleg is attached to the ordinary journal\npost. A fresh-key retry with identical inputs returns the existing entry;\na different transfer for either source or target year conflicts.\n","tags":["fiscal-years"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"required":true,"description":"Target, immutable mapping, result account, currency and Beleg.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FiscalYearCarryforward"}}}},"responses":{"200":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"Posted opening entry or its existing representation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntry"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/reports/trial-balance":{"get":{"operationId":"getTrialBalance","x-d0-machine-scopes":["accounting.read"],"summary":"Read a complete Summen- und Saldenliste at one posting cutoff","description":"Requires accounting.read. Returns posted opening, debit, credit and closing\namounts per account and currency, plus currency totals.\nUses the same asOf and inclusive date filters as account balances. Drafts\nnever contribute; Storno contributes its ordinary opposite lines. accountNo comes from the latest participating posted line by seq/lineNo,\nincluding opening history. Renumbering without a new posting cannot alter\na report, and all numbers of one account aggregate together. Rows sort by accountNo,\naccountId and currency; totals sort by currency. Unused accounts/currencies\nare omitted, including with a currency filter; empty views use empty arrays.\nPositive balances are debit, negative credit. This is cumulative history,\nnot a fiscal-year report; use financial-statements for year-scoped reporting.\nA maximum of 10000 rows including totals is returned atomically; exceeding\nthe bound is 413 report-too-large. Every amount must fit signed int64 minor\nunits, including gross currency totals; otherwise 422 balance-overflow.\nNo partial or rounded report is returned.\n","tags":["accounts"],"parameters":[{"$ref":"#/components/parameters/PostingSnapshot"},{"$ref":"#/components/parameters/ReportCurrency"},{"$ref":"#/components/parameters/ReportDateFrom"},{"$ref":"#/components/parameters/ReportDateTo"}],"responses":{"200":{"description":"Complete account rows and per-currency control totals.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrialBalance"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/account-mappings":{"get":{"operationId":"listAccountMappings","x-d0-machine-scopes":["accounting.read"],"summary":"List immutable account mapping revisions","description":"Returns published mapping revisions in ascending version order for this tenant.","tags":["accounts"],"parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/AccountMappingCursor"}],"responses":{"200":{"description":"Mapping revisions in version order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountMappingPage"}}}},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]},"post":{"operationId":"publishAccountMapping","x-d0-machine-scopes":["accounting.write"],"summary":"Publish an immutable account classification","description":"Freezes explicit consumer classifications and account labels for reproducible reports. Every reportCode must have one classification and name.","tags":["accounts"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"description":"Complete consumer-owned account classification and report labels.","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountMappingCreate"}}}},"responses":{"201":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"Immutable mapping revision.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountMapping"}}}},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/account-mappings/{mappingId}":{"parameters":[{"$ref":"#/components/parameters/AccountMappingId"}],"get":{"operationId":"getAccountMapping","x-d0-machine-scopes":["accounting.read"],"summary":"Fetch an immutable account mapping","description":"Returns the complete frozen mapping if it belongs to this tenant.","tags":["accounts"],"responses":{"200":{"description":"Immutable mapping revision with its complete account items.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountMapping"}}}},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/accounts/{accountId}/retire":{"parameters":[{"$ref":"#/components/parameters/AccountId"}],"post":{"operationId":"retireAccount","x-d0-machine-scopes":["accounting.write"],"summary":"Retire an account for future ordinary postings","description":"Retirement is irreversible and serializes with posting. Historical reads and exact Storno remain available; fresh-key retirement retries preserve the original timestamp.","tags":["accounts"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"responses":{"200":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"Retired account (stable on repeated retirement).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/exports/tenant":{"get":{"operationId":"downloadTenantExport","x-d0-machine-scopes":["accounting.exports.read"],"summary":"Download a complete tenant accounting snapshot","description":"Requires explicit exports.read permission and accounting.exports.read\nmachine scope. Returns a devacc.tenant-export.v1 ZIP containing versioned\nJSON datasets, a manifest with SHA-256 checksums, and available archived\ndocument originals verified against their immutable evidence. Includes\ndiscarded drafts, pending declarations, and public archive disposition\nreceipts. A confirmed original deletion retains its document metadata and\nreceipt but excludes its bytes. A deletion in progress returns retryable\n409 before ZIP bytes are sent.\nAll metadata comes from one tenant-scoped database snapshot. Original\nversions are pinned by that snapshot and verified before any ZIP bytes\nare sent. Stored journal hashes and sequence links are evidence; the\npublic projection does not contain the private canonical hashing payload.\n\nThe synchronous export is limited to 100000 records, 32 MiB of metadata,\n512 MiB of original bytes and two minutes of preparation (the authorization\nlease may expire sooner). Exceeding a size limit returns 413 export-too-large;\nno successful truncated export is returned. Unavailable originals return\n503 and integrity mismatches return 502. Range and conditional headers do\nnot select a partial result. Verify Content-Length and Content-Digest after\ndownload; interruption or authorization expiry may truncate transport.\n","tags":["exports"],"responses":{"200":{"description":"The complete verified ZIP, downloaded as an attachment.","headers":{"Content-Disposition":{"description":"Attachment with a generated tenant export filename.","schema":{"type":"string"}},"Content-Digest":{"description":"RFC 9530 SHA-256 digest of the complete ZIP bytes.","schema":{"type":"string"}},"Content-Length":{"description":"Exact byte length of the ZIP.","schema":{"type":"integer","format":"int64","minimum":1}}},"content":{"application/zip":{"schema":{"type":"string","format":"binary"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/Problem"},"502":{"$ref":"#/components/responses/Problem"},"503":{"$ref":"#/components/responses/Problem"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/tenants":{"get":{"operationId":"listTenants","x-d0-machine-scopes":["accounting.access.manage"],"summary":"List partner tenants","description":"Requires explicit access.manage in the bound tenant. This grants administration across that tenant's authoritative partner. Ordered by public identifier; after is an exclusive public identifier boundary, not a snapshot.\n","tags":["access"],"parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/TenantCursor"}],"responses":{"200":{"description":"The requested access resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantPage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]},"post":{"operationId":"createTenant","x-d0-machine-scopes":["accounting.access.manage"],"summary":"Create an isolated tenant","description":"Requires explicit access.manage in the bound tenant. This grants administration across that tenant's authoritative partner. Creates a tenant and inherits the administrator membership's explicit permissions for the same identity.\n","tags":["access"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"required":true,"description":"Explicit access provisioning fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantCreate"}}}},"responses":{"201":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"The requested access resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tenant"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/tenants/{tenantId}/memberships":{"parameters":[{"$ref":"#/components/parameters/TenantId"}],"get":{"operationId":"listMemberships","x-d0-machine-scopes":["accounting.access.manage"],"summary":"List tenant memberships","description":"Requires explicit access.manage in the bound tenant. This grants administration across that tenant's authoritative partner. Ordered by public identifier; after is an exclusive public identifier boundary, not a snapshot.\n","tags":["access"],"parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/MembershipCursor"}],"responses":{"200":{"description":"The requested access resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MembershipPage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]},"post":{"operationId":"createMembership","x-d0-machine-scopes":["accounting.access.manage"],"summary":"Grant an external identity tenant access","description":"Requires explicit access.manage in the bound tenant. This grants administration across that tenant's authoritative partner. The subject belongs to the configured trusted issuer; permissions must be supplied explicitly.\n","tags":["access"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"required":true,"description":"Explicit access provisioning fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MembershipCreate"}}}},"responses":{"201":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"The requested access resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Membership"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/tenants/{tenantId}/memberships/{membershipId}":{"parameters":[{"$ref":"#/components/parameters/TenantId"},{"$ref":"#/components/parameters/MembershipId"}],"put":{"operationId":"replaceMembership","x-d0-machine-scopes":["accounting.access.manage"],"summary":"Replace or revoke tenant access","description":"Requires explicit access.manage in the bound tenant. This grants administration across that tenant's authoritative partner. Uses expectedRevision to prevent lost updates; inactive memberships cannot access the tenant.\n","tags":["access"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"required":true,"description":"Explicit access provisioning fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MembershipReplace"}}}},"responses":{"200":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"The requested access resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Membership"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/accounts":{"get":{"operationId":"listAccounts","x-d0-machine-scopes":["accounting.read"],"summary":"List accounts","description":"Accounts of the calling tenant, ordered by Kontonummer.","tags":["accounts"],"parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/AccountCursor"}],"responses":{"200":{"description":"A page of accounts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountPage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]},"post":{"operationId":"createAccount","x-d0-machine-scopes":["accounting.write"],"summary":"Create an account","description":"Adds a Konto to the tenant's chart of accounts. `accountNo` is unique per\ntenant; creating a duplicate is `409 account-already-exists`.\n","tags":["accounts"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"required":true,"description":"The Konto to add.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountCreate"}}}},"responses":{"201":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"The created account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/accounts/{accountId}":{"parameters":[{"$ref":"#/components/parameters/AccountId"}],"get":{"operationId":"getAccount","x-d0-machine-scopes":["accounting.read"],"summary":"Fetch one account","description":"Returns the account if it belongs to the calling tenant, else 404.","tags":["accounts"],"responses":{"200":{"description":"The account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/accounts/{accountId}/statement":{"parameters":[{"$ref":"#/components/parameters/AccountId"}],"get":{"operationId":"getAccountStatement","x-d0-machine-scopes":["accounting.read"],"summary":"Read a posted Kontoblatt at a fixed booking state","description":"Requires accounting.read. Returns individual posted lines for this account\nin ascending Buchungsnummer (seq), then lineNo order. Drafts are excluded.\nMultiple lines in the same Buchung remain distinct. Date bounds are inclusive;\ncurrency filters never convert amounts. accountNo is the frozen number as\nbooked, not current chart metadata. Follow entryId to the journal entry or\nGET /documents?journalEntryId={entryId} for its immutable Beleg links;\ndocument reads require documents.read separately.\n\nWithout asOf, captures the latest committed tenant-wide posted entry. The\nresponse's asOf can be reused across statements and balances. Later posts,\nincluding backdated entries, newly posted old drafts and later Storno, are\nexcluded. This freezes posted evidence, not mutable chart data or a complete\ntenant export. The literal empty fixes an empty ledger even after later posts.\n\nPass nextCursor as after and keep date/currency filters unchanged. The cursor\ncarries the original snapshot, so asOf may be omitted on subsequent pages.\nAn explicit conflicting asOf or a malformed, foreign or nonmatching cursor\nis 400 invalid-cursor. Unknown, foreign or draft asOf entries are\n400 invalid-snapshot. Any committed posted entry can define a historical\ncutoff, even if it belongs to another account or currency in this tenant.\n","tags":["accounts"],"parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/PostingSnapshot"},{"$ref":"#/components/parameters/ReportCurrency"},{"$ref":"#/components/parameters/ReportDateFrom"},{"$ref":"#/components/parameters/ReportDateTo"},{"name":"after","in":"query","description":"Opaque nextCursor from this account and the same date/currency filters.","schema":{"$ref":"#/components/schemas/AccountStatementCursor"}}],"responses":{"200":{"description":"A page of immutable posted account movements.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountStatementPage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/accounts/{accountId}/balances":{"parameters":[{"$ref":"#/components/parameters/AccountId"}],"get":{"operationId":"getAccountBalances","x-d0-machine-scopes":["accounting.read"],"summary":"Read exact account totals and balances by currency","description":"Requires accounting.read. Only posted lines contribute, including Storno\nthrough their normal opposite sides. Uses the same asOf cutoff and inclusive\ndate/currency filters as the account statement. Omitted asOf captures the\nlatest committed tenant-wide posted entry; reuse the returned value to keep\nsubsequent reads fixed. Unknown, foreign or draft snapshots are\n400 invalid-snapshot. The literal empty always returns an empty ledger view.\n\nopeningBalance is debit minus credit strictly before bookingDateFrom, or\nzero without a lower bound. debitTotal and creditTotal cover the selected\ndate range. closingBalance is openingBalance plus debitTotal minus\ncreditTotal. Positive balances mean debit; negative balances mean credit,\nindependently of account type. This is cumulative posted history, not a\nfiscal-year opening or carry-forward operation. Currencies are never mixed\nor converted and results are ordered by currency code.\n\nWith no currency filter, returns currencies with posted activity through\nbookingDateTo (including opening history); otherwise an unused currency\nreturns one zero row. A wholly unused account has data: [] without a currency\nfilter. All returned totals must fit signed int64 minor units; any overflow,\nincluding gross debit/credit totals with a representable net, fails the whole\nresponse with 422 balance-overflow. No rounded or partial totals are returned.\n","tags":["accounts"],"parameters":[{"$ref":"#/components/parameters/PostingSnapshot"},{"$ref":"#/components/parameters/ReportCurrency"},{"$ref":"#/components/parameters/ReportDateFrom"},{"$ref":"#/components/parameters/ReportDateTo"}],"responses":{"200":{"description":"Exact totals per currency at the returned posting snapshot.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountBalances"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/periods":{"get":{"operationId":"listPeriods","x-d0-machine-scopes":["accounting.read"],"summary":"List accounting periods","description":"Periods of the calling tenant, ordered by start date.","tags":["periods"],"parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/PeriodCursor"}],"responses":{"200":{"description":"A page of periods.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PeriodPage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]},"post":{"operationId":"createPeriod","x-d0-machine-scopes":["accounting.write"],"summary":"Create an accounting period","description":"Periods of one tenant may not overlap; an overlapping range is\n`409 period-overlap`. A new period starts `open`.\n","tags":["periods"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"required":true,"description":"The period to open.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PeriodCreate"}}}},"responses":{"201":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"The created period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Period"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/periods/{periodId}":{"parameters":[{"$ref":"#/components/parameters/PeriodId"}],"get":{"operationId":"getPeriod","x-d0-machine-scopes":["accounting.read"],"summary":"Fetch one period","description":"Returns the period if it belongs to the calling tenant, else 404.","tags":["periods"],"responses":{"200":{"description":"The period.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Period"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/periods/{periodId}/festschreiben":{"parameters":[{"$ref":"#/components/parameters/PeriodId"}],"post":{"operationId":"festschreibenPeriod","x-d0-machine-scopes":["accounting.periods.close"],"summary":"Finalize an accounting period (Festschreibung)","description":"Permanently seals the period. The database records the authoritative\ntransition time and actor; neither value is caller-controlled. A fresh-key\nretry after an uncertain response is domain-idempotent and returns the\nexisting terminal Period with its original `festgeschriebenAt`.\n\nDraft Buchungen may remain in the period, but they can no longer be posted.\nFestschreibung is irreversible; corrections remain new Storno entries.\n","tags":["periods"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"responses":{"200":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"The sealed period, or its unchanged terminal representation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Period"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/journal-entries":{"get":{"operationId":"listJournalEntries","x-d0-machine-scopes":["accounting.read"],"summary":"List journal entries and drafts","description":"Entries of the calling tenant in ascending immutable entry-ID order,\nindependent of booking date and posting sequence. Select `draft` or\n`posted` explicitly. Date bounds are inclusive and combine with periodId.\nOptional documentId restricts results to entries directly linked to that\nBeleg, including Storno entries with inherited links. Requires accounting.read;\nit returns journal data, not document metadata. Unknown or foreign documentId\nis 404 document-not-found. The optional externalReference filter uses an\nexact, case-sensitive match (no wildcards, trimming or Unicode normalization).\nReferences are not unique: an original and its Storno share the reference.\nKeep filters unchanged when passing nextCursor as after. The cursor must\nidentify an entry visible to this tenant, even if it no longer matches\nthe filters; malformed, unknown or foreign cursors are 400 invalid-cursor.\nPagination is not a snapshot: concurrent commits and mutable drafts or\nstatus transitions can change membership between requests. Entries that\nbecome eligible before the cursor are not revisited; restart traversal\nto rediscover them. Unchanged data is traversed without skips or duplicates.\n","tags":["journal-entries"],"parameters":[{"$ref":"#/components/parameters/Limit"},{"name":"after","in":"query","description":"The nextCursor from the previous page.","schema":{"$ref":"#/components/schemas/EntryId"}},{"name":"status","in":"query","required":true,"description":"Select drafts or posted entries.","schema":{"$ref":"#/components/schemas/JournalEntryStatus"}},{"name":"periodId","in":"query","description":"Restrict to this tenant's accounting period.","schema":{"$ref":"#/components/schemas/PeriodId"}},{"name":"documentId","in":"query","description":"Only entries directly linked to this tenant's Beleg.","schema":{"$ref":"#/components/schemas/DocumentId"}},{"name":"bookingDateFrom","in":"query","description":"Inclusive earliest booking date.","schema":{"type":"string","format":"date"}},{"name":"bookingDateTo","in":"query","description":"Inclusive latest booking date; must not precede bookingDateFrom.","schema":{"type":"string","format":"date"}},{"name":"externalReference","in":"query","description":"Exact business reference; combines with all other filters. U+0000 is forbidden.","schema":{"type":"string","minLength":1,"maxLength":255}}],"responses":{"200":{"description":"A page of journal entries with their lines.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntryPage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]},"post":{"operationId":"createJournalEntryDraft","x-d0-machine-scopes":["accounting.write"],"summary":"Create a draft Buchung","description":"Creates an unposted (draft) Buchung and its lines. The draft is validated\nimmediately — at least two lines, every amount positive, Soll and Haben\nequal, one currency throughout — but it takes no `seq` and enters no hash\nchain until it is posted. A draft in a period that is already\nfestgeschrieben is rejected at post time, not here.\n","tags":["journal-entries"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"required":true,"description":"The Buchung to draft, with all of its lines.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntryCreate"}}}},"responses":{"201":{"description":"The created draft.","headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"},"ETag":{"$ref":"#/components/headers/JournalEntryETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntry"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/journal-entries/{entryId}":{"parameters":[{"$ref":"#/components/parameters/EntryId"}],"get":{"operationId":"getJournalEntry","x-d0-machine-scopes":["accounting.read"],"summary":"Fetch one Buchung with its lines","description":"Returns the Buchung and its lines in line order. A draft carries no `seq`,\n`entryHash` or `postedAt`; a posted one carries all three.\n","tags":["journal-entries"],"responses":{"200":{"description":"The Buchung.","headers":{"ETag":{"$ref":"#/components/headers/JournalEntryETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntry"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]},"put":{"operationId":"replaceJournalEntryDraft","x-d0-machine-scopes":["accounting.write"],"summary":"Replace a draft Buchung","description":"Replaces all draft accounting fields and lines. Omitted optional metadata\nis cleared. Identity, creation evidence and linked Buchungsbelege remain.\nPosted entries reject changes with `409 entry-not-draft`. Requires the\nlatest item ETag; stale versions return `412 precondition-failed`.\n","tags":["journal-entries"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"},{"$ref":"#/components/parameters/JournalEntryIfMatch"}],"requestBody":{"required":true,"description":"The complete replacement accounting content and lines of the draft.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntryCreate"}}}},"responses":{"200":{"description":"The replaced draft.","headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"},"ETag":{"$ref":"#/components/headers/JournalEntryETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntry"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"428":{"$ref":"#/components/responses/PreconditionRequired"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]},"delete":{"operationId":"discardJournalEntryDraft","x-d0-machine-scopes":["accounting.write"],"summary":"Discard a draft Buchung","description":"Discards an unposted draft from item and list reads. Linked archived\nBuchungsbelege remain preserved. Posted entries reject discard with\n`409 entry-not-draft`. Requires the latest item ETag.\n","tags":["journal-entries"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"},{"$ref":"#/components/parameters/JournalEntryIfMatch"}],"responses":{"204":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"The draft was discarded."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"428":{"$ref":"#/components/responses/PreconditionRequired"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/journal-entries/{entryId}/post":{"parameters":[{"$ref":"#/components/parameters/EntryId"}],"post":{"operationId":"postJournalEntry","x-d0-machine-scopes":["accounting.write"],"summary":"Post a draft Buchung","description":"Finalizes a draft: it is assigned the tenant's next `seq` (the legal\nBuchungsnummer), linked into the append-only hash chain, and becomes\nimmutable. A `journal_entry.posted` event is emitted in the same\ntransaction — the entry and its event commit together or not at all.\n\nPosting is terminal. Corrections are new reversal entries (Storno), never\nedits. Posting an entry that is not a draft is `409 entry-not-draft`;\nposting into a period that is festgeschrieben is `409 period-sealed`.\nAt least one archived Buchungsbeleg must already be linked to the draft;\notherwise posting is rejected with `422 beleg-required`.\nOptional If-Match binds posting to the reviewed draft revision under the\nposting lock. A stale version returns 412 without posting; omission\npreserves unconditional posting for existing clients.\n","tags":["journal-entries"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"},{"$ref":"#/components/parameters/JournalEntryPostIfMatch"}],"responses":{"200":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"The posted Buchung.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntry"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/journal-entries/{entryId}/reverse":{"parameters":[{"$ref":"#/components/parameters/EntryId"}],"post":{"operationId":"reverseJournalEntry","x-d0-machine-scopes":["accounting.write"],"summary":"Reverse a posted Buchung (Storno)","description":"Creates and immediately posts a new immutable Storno. The server copies\nthe original currency, lines, frozen Kontonummer snapshots and immutable\nBeleg evidence, swaps Soll and Haben, and sets `reversalOf` to the target.\nA `journal_entry.posted` event is emitted in the same transaction.\n\nThe caller selects an open destination period and a booking date within\nthat period because the original period may already be festgeschrieben.\nOnly posted entries can be reversed. A second direct reversal is rejected\nwith `409 entry-already-reversed`; reverse the Storno itself to undo it.\nRetry an uncertain response with the original `Idempotency-Key` for an\nexact replay; a fresh key represents a new write intent.\n","tags":["journal-entries"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"required":true,"description":"The destination period and Buchungsdatum for the Storno.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntryReversalCreate"}}}},"responses":{"201":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"The newly created and posted Storno.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JournalEntry"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/journal-entries/{entryId}/documents/{documentId}":{"parameters":[{"$ref":"#/components/parameters/EntryId"},{"$ref":"#/components/parameters/DocumentId"}],"post":{"operationId":"linkJournalEntryDocument","x-d0-machine-scopes":["accounting.write"],"summary":"Link an archived Beleg to a draft Buchung","description":"Links an archived Buchungsbeleg to a draft. The link is append-only and is\nincluded in the entry's audit hash when the Buchung is posted. Pending\ndocuments and entries that are already posted are rejected.\n","tags":["journal-entries","documents"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"responses":{"204":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"The document is linked. Repeating the link is a no-op."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/documents":{"get":{"operationId":"listDocuments","x-d0-machine-scopes":["accounting.documents.read"],"summary":"List Belege and generated documents","description":"Requires documents.read. Returns public metadata in ascending immutable\ndocument-ID order, independent of Belegdatum. Omitted filters include all\ndocuments; provided filters combine with AND. Date bounds are inclusive.\njournalEntryId selects direct links to a Buchung (including inherited\nStorno links), without exposing journal data. sourceDocumentId selects\ndirect generated outputs, never transitive descendants. Unknown or foreign\nrelationship IDs are 404; an existing resource with no matches gives an\nempty page. Read each output's sources to inspect source roles.\n\nPass nextCursor as after with unchanged filters. The cursor must name a\ndocument in this tenant, even if it no longer matches the filters;\nmalformed, unknown and foreign cursors are 400 invalid-cursor. Pagination\nis not a snapshot: concurrent commits and pending-to-archived transitions\nmay change membership. Documents that become eligible before the cursor\nare not revisited; restart traversal to discover them. Unchanged data is\ntraversed without skips or duplicates.\n","tags":["documents"],"parameters":[{"$ref":"#/components/parameters/Limit"},{"name":"after","in":"query","description":"The nextCursor from the previous page.","schema":{"$ref":"#/components/schemas/DocumentId"}},{"name":"status","in":"query","description":"Restrict to pending or archived documents.","schema":{"$ref":"#/components/schemas/DocumentStatus"}},{"name":"origin","in":"query","description":"Restrict to uploaded originals or generated derivatives.","schema":{"$ref":"#/components/schemas/DocumentOrigin"}},{"name":"documentDateFrom","in":"query","description":"Inclusive earliest Belegdatum.","schema":{"type":"string","format":"date"}},{"name":"documentDateTo","in":"query","description":"Inclusive latest Belegdatum; must not precede documentDateFrom.","schema":{"type":"string","format":"date"}},{"name":"journalEntryId","in":"query","description":"Only documents directly linked to this tenant's Buchung.","schema":{"$ref":"#/components/schemas/EntryId"}},{"name":"sourceDocumentId","in":"query","description":"Only direct derivatives of this tenant's source document.","schema":{"$ref":"#/components/schemas/DocumentId"}}],"responses":{"200":{"description":"A page of public document metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentPage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]},"post":{"operationId":"createDocument","x-d0-machine-scopes":["accounting.documents.write"],"summary":"Declare an immutable Buchungsbeleg upload","description":"Creates a pending Buchungsbeleg and returns a short-lived, create-only PUT\nrequest for a direct upload. The checksum, exact byte length, media type,\nand COMPLIANCE Object Lock retention are all covered by the signature.\n\n`classification`, storage identity, retention policy, and retention horizon\nare server-owned. This public workflow always creates `buchungsbeleg`.\n","tags":["documents"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"required":true,"description":"Immutable metadata known before the upload starts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentCreate"}}}},"responses":{"201":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"The pending Beleg and its first direct-upload request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentCreateResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/documents/{documentId}/sources":{"parameters":[{"$ref":"#/components/parameters/DocumentId"}],"get":{"operationId":"listDocumentSources","x-d0-machine-scopes":["accounting.documents.read"],"summary":"Read the direct sources of a generated Beleg","description":"Requires documents.read. Returns the immutable direct source references,\nordered by role. Uploaded originals return an empty data array. Currently\nthe only derivation kind is zugferd-pdf, with exactly one invoice source\nand one visual source; this bounded set needs no pagination. Sources are\nvisible as soon as a derived document is declared, including while it is\npending. Each documentId can be resolved through GET /documents/{documentId}.\nStorage keys, object versions and internal identifiers stay private.\n","tags":["documents"],"responses":{"200":{"description":"The complete, bounded set of direct source references.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentSourceList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/documents/{documentId}":{"parameters":[{"$ref":"#/components/parameters/DocumentId"}],"get":{"operationId":"getDocument","x-d0-machine-scopes":["accounting.documents.read"],"summary":"Fetch one Beleg","description":"Returns public lifecycle and declaration metadata. Storage keys, immutable\nobject version ids, actor ids, and internal database ids are never exposed.\n","tags":["documents"],"responses":{"200":{"description":"The Beleg.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Document"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/documents/{documentId}/content":{"parameters":[{"$ref":"#/components/parameters/DocumentId"}],"get":{"operationId":"downloadDocument","x-d0-machine-scopes":["accounting.documents.read"],"summary":"Download the original archived Beleg","description":"Requires documents.read and an archived Beleg visible to the tenant.\nReturns the complete original bytes from the recorded immutable object\nversion after verifying length and SHA-256. No storage coordinates or\ndownload credentials are disclosed. Partial and conditional requests are\nnot supported; Range and conditional headers do not select another result.\nPending documents and a deletion in progress return 409. A confirmed\noriginal deletion returns 410; document metadata and the disposition\nreceipt remain available. Missing or unavailable archive objects return\n503; inconsistent archive bytes or metadata return 502 before any file bytes\nare sent. Clients should verify Content-Digest and Content-Length as well.\n","tags":["documents"],"responses":{"200":{"description":"The verified original bytes, downloaded as an attachment.","headers":{"Content-Disposition":{"description":"Attachment with the validated original filename.","schema":{"type":"string"}},"Content-Digest":{"description":"RFC 9530 SHA-256 digest of the complete original bytes.","schema":{"type":"string"}},"Content-Length":{"description":"Exact byte length of the original.","schema":{"type":"integer","format":"int64","minimum":1}}},"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"410":{"$ref":"#/components/responses/Problem"},"502":{"$ref":"#/components/responses/Problem"},"503":{"$ref":"#/components/responses/Problem"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/documents/{documentId}/upload-requests":{"parameters":[{"$ref":"#/components/parameters/DocumentId"}],"post":{"operationId":"createDocumentUploadRequest","x-d0-machine-scopes":["accounting.documents.write"],"summary":"Create a fresh upload request for a pending Beleg","description":"Re-signs the pending document's original immutable declaration. Use a fresh\n`Idempotency-Key` after an earlier signed request expires. An archived Beleg\ncan never receive another upload URL.\n","tags":["documents"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"responses":{"200":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"A fresh short-lived direct-upload request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentUploadRequest"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/documents/{documentId}/complete":{"parameters":[{"$ref":"#/components/parameters/DocumentId"}],"post":{"operationId":"completeDocument","x-d0-machine-scopes":["accounting.documents.write"],"summary":"Verify and archive an uploaded Beleg","description":"Verifies the exact immutable object version, checksum, length, media type,\nand COMPLIANCE retention before atomically sealing the Beleg. Retrying the\nsame completed upload is safe.\n","tags":["documents"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"responses":{"200":{"headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"}},"description":"The archived Beleg.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Document"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/documents/{documentId}/preservation-requests":{"parameters":[{"$ref":"#/components/parameters/DocumentId"}],"post":{"operationId":"createDocumentPreservation","x-d0-machine-scopes":["accounting.documents.write"],"summary":"Request stronger preservation of an archived Beleg","description":"Requires documents.write. Records the requesting actor, reason and requested\nprotection without changing the original Beleg evidence. Requests may extend\nCOMPLIANCE retention, set a legal hold ON, or both. Shortening, hold release\nand deletion are not supported. An extension must exceed the original storage\ndeadline and every previously accepted extension for this Beleg.\n\nA 202 response confirms durable acceptance only. Poll the returned resource;\napplied means the worker has verified the requested protection on the original\nimmutable object version. Storage outages leave the request pending for retry.\nReplaying the same Idempotency-Key returns the original acceptance response;\nuse GET for current state. The retentionUntil on the original Document remains\nits initial, immutable policy evidence.\n","tags":["documents"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"description":"A reasoned retention extension, legal-hold ON request, or both.","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentPreservationCreate"}}}},"responses":{"202":{"description":"The durable preservation request, awaiting storage confirmation.","headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"},"Location":{"required":true,"description":"Relative URL for polling the request within this API base.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentPreservation"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/document-preservation-requests/{preservationId}":{"parameters":[{"$ref":"#/components/parameters/DocumentPreservationId"}],"get":{"operationId":"getDocumentPreservation","x-d0-machine-scopes":["accounting.documents.read"],"summary":"Fetch a preservation decision and its confirmed application state","description":"Requires documents.read. The request and actor attribution are immutable.\nappliedAt and observed protection are present only after exact-version storage\nverification. A completed observation is historical evidence, not a live\nstorage availability probe. Storage coordinates and credentials are not exposed.\n","tags":["documents"],"responses":{"200":{"description":"The tenant-visible request and its application state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentPreservation"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/documents/{documentId}/e-invoice-validations":{"parameters":[{"$ref":"#/components/parameters/DocumentId"}],"post":{"operationId":"createEInvoiceValidation","x-d0-machine-scopes":["accounting.documents.write"],"summary":"Queue validation of an archived XML e-invoice","description":"Creates one durable validation request for the exact immutable object\nversion sealed into the archived Beleg. The service submits those bytes\nto the standalone compliance platform under a deployment-pinned validation\npolicy, then copies the verified exact result into its own immutable GoBD\narchive before publishing completion. Processing is asynchronous; poll the\nresource named by `Location` until its status is `completed` or `failed`.\nA completed validation with `result.valid: false` is a successful validation\noutcome, not a processing failure.\n\nOnly archived `application/xml` Belege up to 20 MiB (20,971,520 bytes)\nare accepted. The source bytes and storage identity are never exposed by\nthis operation.\n","tags":["documents","e-invoice-validations"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"responses":{"202":{"description":"The validation was durably queued, or an exact idempotent replay.","headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"},"Location":{"required":true,"description":"Relative URI of the durable validation status resource.","schema":{"type":"string","format":"uri-reference"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EInvoiceValidation"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/e-invoice-validations/{validationId}":{"parameters":[{"$ref":"#/components/parameters/EInvoiceValidationId"}],"get":{"operationId":"getEInvoiceValidation","x-d0-machine-scopes":["accounting.documents.read"],"summary":"Fetch one e-invoice validation","description":"Returns the durable validation state if it belongs to the calling tenant,\nelse 404. Terminal validation results include the exact validator and\nruleset identities frozen by the standalone compliance platform. A\nprocessing failure exposes only a stable public code; internal diagnostics\nnever cross this boundary.\n","tags":["e-invoice-validations"],"responses":{"200":{"description":"The validation request and its current state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EInvoiceValidation"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/e-invoice-validations/{validationId}/zugferd-pdfs":{"parameters":[{"$ref":"#/components/parameters/EInvoiceValidationId"}],"post":{"operationId":"createZUGFeRDPDF","x-d0-machine-scopes":["accounting.documents.write"],"summary":"Queue creation of an immutable ZUGFeRD PDF","description":"Combines the exact CII XML from a completed, valid e-invoice validation\nwith one immutable archived PDF visual source. Processing is durable and\nasynchronous; poll the resource named by `Location` until it is terminal.\n\nThe server selects and freezes the ZUGFeRD standard, EN 16931 profile, and\ngeneration recipe. The original XML and visual Belege remain independently\narchived; the output is a new immutable derived Beleg with explicit lineage.\nUBL-to-CII conversion and visual rendering are deliberately not implicit.\n","tags":["e-invoice-validations","zugferd-pdfs"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"description":"Selects the already archived PDF visual source.","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZUGFeRDPDFCreate"}}}},"responses":{"202":{"description":"The generation was durably queued. An exact Idempotency-Key replay\nreturns its originally stored response; the same semantic request under\na new key returns the already existing durable resource in its current\nstate, which may already be terminal.\n","headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"},"Location":{"required":true,"description":"Relative URI of the durable ZUGFeRD PDF status resource.","schema":{"type":"string","format":"uri-reference"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZUGFeRDPDF"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/zugferd-pdfs/{zugferdPdfId}":{"parameters":[{"$ref":"#/components/parameters/ZUGFeRDPDFId"}],"get":{"operationId":"getZUGFeRDPDF","x-d0-machine-scopes":["accounting.documents.read"],"summary":"Fetch one ZUGFeRD PDF generation","description":"Returns tenant-visible durable generation state. A completed resource\nincludes the immutable output Beleg and exact processor/ruleset evidence;\nfailures expose no internal diagnostic or storage identity.\n","tags":["zugferd-pdfs"],"responses":{"200":{"description":"The generation and its current state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZUGFeRDPDF"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/webhook-endpoints":{"get":{"operationId":"listWebhookEndpoints","x-d0-machine-scopes":["accounting.webhooks.manage"],"summary":"List webhook endpoints","description":"Requires explicit webhook endpoint management permission in the calling\ntenant. Returns every endpoint of that tenant, including inactive endpoints,\nordered by creation time. Inactive metadata remains visible so an endpoint\ncan be audited or reactivated without losing its identity or delivery history.\n","tags":["webhook-endpoints"],"parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/WebhookEndpointCursor"}],"responses":{"200":{"description":"A page of webhook endpoint metadata. Collection responses do not carry\nper-item HTTP validators; fetch an item before mutating it to obtain its\ncurrent `ETag`.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointPage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]},"post":{"operationId":"createWebhookEndpoint","x-d0-machine-scopes":["accounting.webhooks.manage"],"summary":"Create a webhook endpoint","description":"Requires explicit webhook endpoint management permission in the calling\ntenant. Registers an active HTTPS destination. The caller supplies the\nsigning secret once; it is write-only and is never included in this or any\nlater response. A tenant may retain at most 256 endpoints in total, including\ninactive endpoints, and at most 64 of them may be active. Deactivation does\nnot free retained-endpoint capacity.\n","tags":["webhook-endpoints"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"}],"requestBody":{"required":true,"description":"Destination, subscription, and caller-generated signing secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointCreate"}}}},"responses":{"201":{"description":"The created endpoint metadata. The signing secret is never returned.","headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"},"ETag":{"$ref":"#/components/headers/WebhookEndpointETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/webhook-endpoints/{webhookEndpointId}":{"parameters":[{"$ref":"#/components/parameters/WebhookEndpointId"}],"get":{"operationId":"getWebhookEndpoint","x-d0-machine-scopes":["accounting.webhooks.manage"],"summary":"Fetch one webhook endpoint","description":"Requires explicit webhook endpoint management permission in the calling\ntenant. Returns safe metadata if the endpoint belongs to that tenant, else\n404. The signing secret is never readable through the API.\n","tags":["webhook-endpoints"],"responses":{"200":{"description":"The webhook endpoint metadata.","headers":{"ETag":{"$ref":"#/components/headers/WebhookEndpointETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]},"put":{"operationId":"replaceWebhookEndpoint","x-d0-machine-scopes":["accounting.webhooks.manage"],"summary":"Replace webhook endpoint metadata","description":"Requires explicit webhook endpoint management permission in the calling\ntenant. Fully replaces the destination URL, event subscription, and active\nstate; the existing signing secret is unchanged. All three metadata fields\nare required.\n\nThe active state and event subscription select recipients only for events\ncommitted after this replacement commits. Each previously selected recipient\nkeeps the routing URL snapshotted with its event, even if this replacement\nchanges the URL. Setting `active` to false also permanently suppresses every\nolder-snapshot recipient for this endpoint that a worker has not already\nloaded. Reactivating the endpoint later does not revive those suppressed\nrecipients; it only makes the endpoint eligible for new event snapshots. An\nattempt loaded before deactivation commits may still complete with its pinned\nURL after this operation returns.\n","tags":["webhook-endpoints"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"},{"$ref":"#/components/parameters/WebhookEndpointIfMatch"}],"requestBody":{"required":true,"description":"Complete replacement metadata for the endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointReplace"}}}},"responses":{"200":{"description":"The replaced webhook endpoint metadata.","headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"},"ETag":{"$ref":"#/components/headers/WebhookEndpointETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"428":{"$ref":"#/components/responses/PreconditionRequired"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]},"delete":{"operationId":"deleteWebhookEndpoint","x-d0-machine-scopes":["accounting.webhooks.manage"],"summary":"Deactivate a webhook endpoint","description":"Requires explicit webhook endpoint management permission in the calling\ntenant. Soft-deactivates the endpoint. No endpoint or delivery-attempt row is\ndeleted: its stable identity, lifecycle attribution, and delivery audit\nhistory are preserved.\n\nAfter deactivation commits, the endpoint is excluded from new event snapshots\nand every older-snapshot recipient for it that a worker has not already loaded\nis permanently suppressed. Reactivating the same endpoint identity later does\nnot revive those suppressed recipients. Deleting an already inactive endpoint\nis a no-op. An attempt loaded before deactivation commits may still complete\nwith its pinned URL after this operation returns.\n","tags":["webhook-endpoints"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"},{"$ref":"#/components/parameters/WebhookEndpointIfMatch"}],"responses":{"204":{"description":"The endpoint is inactive and its audit history is preserved.","headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"},"ETag":{"$ref":"#/components/headers/WebhookEndpointETag"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"428":{"$ref":"#/components/responses/PreconditionRequired"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}},"/webhook-endpoints/{webhookEndpointId}/secret":{"parameters":[{"$ref":"#/components/parameters/WebhookEndpointId"}],"put":{"operationId":"rotateWebhookEndpointSecret","x-d0-machine-scopes":["accounting.webhooks.manage"],"summary":"Rotate a webhook endpoint signing secret","description":"Requires explicit webhook endpoint management permission in the calling\ntenant. Replaces only the signing secret; metadata and active state are\nunchanged. Configure the receiver to accept both secrets before rotating.\nOnce this operation returns 204, every newly loaded attempt uses the current\nsecret version, including a retry for an event snapshotted before rotation.\nA request already loaded or in flight may still arrive with a signature made\nby the old secret. Keep the overlap until those attempts have drained. The\nold and new secrets are both write-only and are never returned.\n","tags":["webhook-endpoints"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ActingUserIssuer"},{"$ref":"#/components/parameters/ActingUserSubject"},{"$ref":"#/components/parameters/WebhookEndpointIfMatch"}],"requestBody":{"required":true,"description":"The caller-generated replacement signing secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointSecretReplace"}}}},"responses":{"204":{"description":"Future delivery attempts use the replacement secret.","headers":{"X-Accounting-Acting-User-Recorded":{"$ref":"#/components/headers/ActingUserRecorded"},"ETag":{"$ref":"#/components/headers/WebhookEndpointETag"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"412":{"$ref":"#/components/responses/PreconditionFailed"},"422":{"$ref":"#/components/responses/Unprocessable"},"428":{"$ref":"#/components/responses/PreconditionRequired"},"default":{"$ref":"#/components/responses/Problem"}},"security":[{"machineToken":[],"machineProof":[],"machineTenant":[]}]}}},"webhooks":{"asyncResult":{"post":{"operationId":"asyncResultEvent","summary":"An asynchronous document or e-invoice operation reached a terminal result","description":"Delivered to active endpoints with `eventTypes: []`. The event-time\nrecipient and route snapshot, HMAC signatures, retry and deduplication\nrules are identical to `journalEntryPosted`. Fetch the named public\nresource for current details; the envelope contains no document bytes,\nvalidation report, internal ID, or sequence number.\n","tags":["documents"],"security":[],"parameters":[{"name":"D0-Signature","in":"header","required":true,"description":"The published v1 HMAC signature; see `journalEntryPosted`.","schema":{"type":"string"}},{"name":"D0-Signature-V2","in":"header","required":true,"description":"The preferred authenticated-header HMAC; see `journalEntryPosted`.","schema":{"type":"string"}},{"name":"D0-Webhook-Id","in":"header","required":true,"description":"The authenticated `event_id`, stable across redelivery.","schema":{"$ref":"#/components/schemas/EventId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsyncEvent"}}}},"responses":{"204":{"description":"Acknowledged."},"2XX":{"description":"Acknowledged with another 2xx status."},"default":{"description":"Not acknowledged; the delivery may be retried."}}}},"journalEntryPosted":{"post":{"operationId":"journalEntryPostedEvent","summary":"A Buchung was posted","description":"When the event is committed, its recipients are snapshotted from the tenant's\nactive endpoints subscribed to `journal_entry.posted`; each selected recipient\nalso pins that endpoint's routing URL. Later URL or subscription changes do\nnot retarget that event. A later deactivation permanently suppresses a selected\nrecipient that a worker has not already loaded, even if the endpoint is then\nreactivated. An attempt already loaded when deactivation commits may complete.\n\nDelivery attempts may be repeated: the same `event_id` can arrive more than\nonce, and events can arrive out of order. Every newly loaded attempt uses the\nendpoint's current signing-secret version. Dedupe on `event_id` and order by\n`resource.seq`. Existing integrations can continue verifying `D0-Signature`.\nNew and upgraded integrations should verify `D0-Signature-V2`, which also\nauthenticates `D0-Webhook-Id`, before trusting the body. Return any 2xx status only after the\nevent is durably recorded; any 2xx marks this recipient successful. Connection\nfailures and non-2xx responses may be retried under the current delivery policy.\nAttempt limits, timing, backoff, and transient/permanent classifications are not\npart of this contract and may evolve. Receivers SHOULD allow at least five minutes\nof signature timestamp tolerance; this covers the bounded fan-out, normal network\ndelay, and ordinary clock skew.\n","tags":["journal-entries"],"security":[],"parameters":[{"name":"D0-Signature","in":"header","required":true,"description":"Backwards-compatible signature with the published v1 wire format:\n`t=<unix-seconds>,v1=<hex>`, where `v1` is lowercase hexadecimal\nHMAC-SHA256(endpoint secret, `\"<unix-seconds>.\" + raw request body`).\nThe header is retained byte-for-byte for existing consumers. It authenticates\nthe timestamp and exact body, but not the separate `D0-Webhook-Id` header.\nCompare in constant time and reject a timestamp outside your tolerance window.\nA tolerance of at least five minutes is recommended.\n","schema":{"type":"string","pattern":"^t=[0-9]+,v1=[0-9a-f]{64}$"}},{"name":"D0-Signature-V2","in":"header","required":true,"description":"Preferred signature: `t=<unix-seconds>,v2=<hex>`, where `v2` is the\nlowercase hexadecimal HMAC-SHA256 of the following versioned binary message:\n\n`\"d0.webhook.v2\" || 0x00 || int64be(t) || uint64be(len(id)) || id ||`\n`uint64be(len(body)) || body`\n\nHere `id` is the exact ASCII value of `D0-Webhook-Id`, `body` is the\nexact raw request body, and both lengths count bytes. Compare the MAC in\nconstant time and reject a timestamp outside your tolerance window. The\ntimestamp, webhook ID, and body are all covered by the MAC, so none can be\nsubstituted independently. A tolerance of at least five minutes is recommended.\n","schema":{"type":"string","pattern":"^t=[0-9]+,v2=[0-9a-f]{64}$"}},{"name":"D0-Webhook-Id","in":"header","required":true,"description":"The envelope's `event_id`, repeated as an authenticated header so a\nreceiver can dedupe a redelivery without parsing the body. It is included\nin the `v2` signature message and is byte-identical to the body field.\n","schema":{"$ref":"#/components/schemas/EventId"}}],"requestBody":{"required":true,"description":"The event envelope. Verify the signature before parsing it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}}},"responses":{"204":{"description":"Acknowledged. This recipient is marked successful."},"2XX":{"description":"Acknowledged with another 2xx status. Every 2xx status marks this\nrecipient successful.\n"},"default":{"description":"Not acknowledged. The delivery may be retried under the current policy;\nretry count and schedule are not contractual.\n"}}}}},"components":{"securitySchemes":{"machineToken":{"type":"http","scheme":"DPoP","description":"Machine-only AUTH_MODE=machine. DPoP-bound PS256 access token issued by\nhttps://auth.d0.eu for the sole audience https://api.d0.eu/accounting.\nUse Authorization: DPoP <token>; Bearer and internal assertions are denied.\n"},"machineProof":{"type":"apiKey","in":"header","name":"DPoP","description":"ES256 dpop+jwt proof binding the token hash, proof key, actual public\nmethod/URI, current issued-at time, fresh unique jti and server nonce.\nOn 401 use_dpop_nonce, retry with DPoP-Nonce and a fresh proof jti.\n"},"machineTenant":{"type":"apiKey","in":"header","name":"X-Accounting-Tenant","description":"Public ten_ handle selecting the target tenant. It conveys no authority:\nthe active organization-to-partner delegation and exact client membership\nmust permit that tenant and operation.\n"}},"schemas":{"Event":{"type":"object","description":"The unchanged `devacc.event.v1` journal posting envelope.","required":["schema","event_id","tenant_id","type","occurred_at","resource"],"additionalProperties":false,"properties":{"schema":{"type":"string","description":"Envelope version. Branch on this before reading any other field.","const":"devacc.event.v1"},"event_id":{"type":"string","description":"Unique per event and stable across redeliveries — the deduplication key.\nRepeated in the `D0-Webhook-Id` header so it can be read without parsing\nthis body.\n","$ref":"#/components/schemas/EventId"},"tenant_id":{"type":"string","format":"uuid","description":"Public opaque UUID of the tenant that owns the event. It is minted\nindependently of, and is not, the tenant's internal primary key.\n"},"type":{"type":"string","description":"The domain event type.","enum":["journal_entry.posted"]},"occurred_at":{"type":"string","format":"date-time","description":"When the change was committed, in UTC."},"resource":{"type":"object","required":["public_id","seq"],"additionalProperties":false,"properties":{"public_id":{"type":"string","description":"Public identifier of the affected resource.","examples":["je_9GXQ8W5V4R3P2N1M0KJHGF7ED6"]},"seq":{"type":"integer","format":"int64","description":"The resource's Buchungsnummer. Process events per tenant in\nascending `seq`; delivery order is not guaranteed.\n"}}},"entry_hash":{"type":"string","description":"Opaque server-side audit fingerprint of the posted Buchung.","pattern":"^[0-9a-f]{64}$"}}},"EventId":{"type":"string","description":"Opaque event identifier — the deduplication handle for webhook delivery. Like\nevery identifier this API publishes it is minted independently of the row it\nnames, so it encodes neither a primary key nor the order events were created in.\n","pattern":"^evt_[0-9A-HJKMNP-TV-Z]{26}$","examples":["evt_9GXQ8W5V4R3P2N1M0KJHGF7ED6"]},"AsyncEvent":{"type":"object","description":"A terminal asynchronous result. `succeeded` means processing completed;\na validation can still report `valid: false` when read through its resource.\n","required":["schema","event_id","tenant_id","type","occurred_at","resource"],"additionalProperties":false,"properties":{"schema":{"type":"string","const":"devacc.async-event.v1"},"event_id":{"$ref":"#/components/schemas/EventId"},"tenant_id":{"type":"string","format":"uuid","description":"Opaque public tenant UUID, never its internal database ID."},"type":{"type":"string","enum":["document.archived","document.preservation.applied","einvoice.validation.succeeded","einvoice.validation.failed","einvoice.zugferd-pdf.succeeded","einvoice.zugferd-pdf.failed"]},"occurred_at":{"type":"string","format":"date-time"},"resource":{"type":"object","required":["public_id"],"additionalProperties":false,"properties":{"public_id":{"type":"string","description":"Typed public handle (`doc_`, `prsv_`, `eiv_`, or `eip_`)."}}}}},"Problem":{"type":"object","description":"RFC 9457 problem detail.","required":["type","title","status"],"properties":{"type":{"type":"string","format":"uri","description":"Stable, machine-readable problem identifier. This is the field to\nbranch on; `title` and `detail` are prose and may be reworded.\n","examples":["https://api.d0.eu/accounting/problems/period-sealed"]},"title":{"type":"string","description":"Short human-readable summary of the problem type."},"status":{"type":"integer","minimum":400,"maximum":599},"detail":{"type":"string","description":"Human-readable explanation specific to this occurrence."},"instance":{"type":"string","format":"uri-reference","description":"The request path this problem occurred on."},"errors":{"type":"array","description":"Field-level detail, when the problem is a validation failure.","items":{"type":"object","required":["detail"],"properties":{"pointer":{"type":"string","description":"JSON Pointer (RFC 6901) into the request body.","examples":["/lines/0/amount"]},"detail":{"type":"string"}}}}}},"WebhookEndpointSecretReplace":{"type":"object","description":"Write-only replacement signing material for one endpoint.","required":["secret"],"additionalProperties":false,"properties":{"secret":{"$ref":"#/components/schemas/WebhookEndpointSecret"}}},"WebhookEndpointSecret":{"type":"string","minLength":43,"maxLength":43,"pattern":"^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$","writeOnly":true,"x-go-type-skip-optional-pointer":true,"description":"Exactly 32 bytes encoded as canonical, unpadded base64url (43 characters).\nFor every create or rotation, the caller MUST generate a unique, independent,\nunpredictable 32-byte value with a cryptographically secure random-number\ngenerator and MUST NOT reuse it for another endpoint or secret version. The\ncaller retains this HMAC-SHA256 signing secret. It is accepted only on create\nor rotation and is never returned by the API. The final-character restriction\nrejects non-canonical encodings with non-zero unused bits.\n"},"WebhookEndpointId":{"type":"string","description":"Opaque webhook endpoint identifier. It encodes neither the tenant nor the\nendpoint's internal database identity.\n","pattern":"^whep_[0-9A-HJKMNP-TV-Z]{26}$","examples":["whep_9GXQ8W5V4R3P2N1M0KJHGF7ED6"]},"WebhookEndpoint":{"type":"object","description":"Safe webhook endpoint metadata. Signing-secret material is never present.","required":["id","url","eventTypes","active","createdAt","updatedAt"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/WebhookEndpointId"},"url":{"$ref":"#/components/schemas/WebhookEndpointURL"},"eventTypes":{"$ref":"#/components/schemas/WebhookEndpointEventTypes"},"active":{"type":"boolean","description":"Whether this endpoint is eligible for newly committed event snapshots.\nDeactivation also permanently suppresses its older-snapshot recipients\nthat a worker has not already loaded; later reactivation does not revive\nthose recipients.\n"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last metadata, active-state, or secret change. A secret rotation changes\nthis timestamp but never exposes either secret.\n"}}},"WebhookEndpointEventTypes":{"type":"array","maxItems":16,"uniqueItems":true,"description":"Unique event types delivered to this endpoint, with at most 16 entries.\nAn empty array subscribes the endpoint to all current and future webhook\nevent types; list explicit values to opt in only to those event types. The\nsubscription is evaluated when each event's recipients are snapshotted;\nchanging it does not add or remove recipients from an existing snapshot.\n","items":{"$ref":"#/components/schemas/WebhookEventType"}},"WebhookEventType":{"type":"string","description":"A domain event type currently available for endpoint subscriptions.","enum":["journal_entry.posted"],"x-enum-varnames":["WebhookEventTypeJournalEntryPosted"]},"WebhookEndpointURL":{"type":"string","format":"uri","pattern":"^[hH][tT][tT][pP][sS]://","maxLength":2048,"description":"Absolute HTTPS delivery URL of at most 2,048 UTF-8 bytes. Userinfo and\nfragments are forbidden, and an IP-literal host must be globally routable;\nloopback, private, link-local, carrier-grade NAT, multicast, documentation,\nunspecified, and other special-use addresses are rejected. On create and\nreplacement, the service canonicalizes an accepted URL before storing and\nreturning it: the scheme and ASCII host are lowercased, the default HTTPS\nport is removed, and a non-default port is normalized to decimal. The caller\ndoes not need to perform that canonicalization first.\n\nDNS names are resolved only when delivery is attempted: the complete answer\nset is validated and the selected public address pinned before every connection\nto prevent rebinding. Redirects are not followed. URL syntax and IP-literal\nrestrictions are rechecked whenever metadata is replaced and again at delivery\ntime. Query strings are retained in immutable routing history for audit and\nretries; they must not contain credentials or capability tokens. Authenticate\ndelivery with the signing secret instead.\n","examples":["https://hooks.example.com/accounting"]},"WebhookEndpointReplace":{"type":"object","description":"Complete replacement of mutable endpoint metadata. The signing secret is\ndeliberately not metadata and remains unchanged.\n","required":["url","eventTypes","active"],"additionalProperties":false,"properties":{"url":{"$ref":"#/components/schemas/WebhookEndpointURL"},"eventTypes":{"$ref":"#/components/schemas/WebhookEndpointEventTypes"},"active":{"type":"boolean"}}},"WebhookEndpointCreate":{"type":"object","description":"Fields accepted when registering an active webhook endpoint.","required":["url","eventTypes","secret"],"additionalProperties":false,"properties":{"url":{"$ref":"#/components/schemas/WebhookEndpointURL"},"eventTypes":{"$ref":"#/components/schemas/WebhookEndpointEventTypes"},"secret":{"$ref":"#/components/schemas/WebhookEndpointSecret"}}},"WebhookEndpointPage":{"type":"object","description":"One page of webhook endpoints, oldest creation time first.","required":["data"],"additionalProperties":false,"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}},"nextCursor":{"type":"string","description":"Pass as the `after` query parameter to fetch the next page of endpoints.\nAbsent once the last page has been returned.\n","$ref":"#/components/schemas/WebhookEndpointId"}}},"ZUGFeRDPDF":{"description":"Durable ZUGFeRD PDF state. Pending has neither result nor failure;\ncompleted has the new immutable output Beleg and exact generation evidence;\nfailed exposes only a stable public failure code.\n","oneOf":[{"$ref":"#/components/schemas/ZUGFeRDPDFPending"},{"$ref":"#/components/schemas/ZUGFeRDPDFCompleted"},{"$ref":"#/components/schemas/ZUGFeRDPDFFailed"}],"discriminator":{"propertyName":"status","mapping":{"pending":"#/components/schemas/ZUGFeRDPDFPending","completed":"#/components/schemas/ZUGFeRDPDFCompleted","failed":"#/components/schemas/ZUGFeRDPDFFailed"}}},"ZUGFeRDPDFFailed":{"type":"object","description":"A terminal PDF-generation failure without sensitive diagnostics.","required":["id","validationId","invoiceDocumentId","visualDocumentId","target","recipe","status","requestedAt","completedAt","failure"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/ZUGFeRDPDFId"},"validationId":{"$ref":"#/components/schemas/EInvoiceValidationId"},"invoiceDocumentId":{"$ref":"#/components/schemas/DocumentId"},"visualDocumentId":{"$ref":"#/components/schemas/DocumentId"},"target":{"$ref":"#/components/schemas/ZUGFeRDPDFStandard"},"recipe":{"$ref":"#/components/schemas/ZUGFeRDPDFRecipe"},"status":{"type":"string","const":"failed","x-enum-varnames":["ZUGFeRDPDFFailedStatusFailed"]},"requestedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"failure":{"$ref":"#/components/schemas/ZUGFeRDPDFFailure"}}},"ZUGFeRDPDFFailure":{"type":"object","description":"Stable public failure classification without internal diagnostics.","required":["code"],"additionalProperties":false,"properties":{"code":{"type":"string","enum":["unsupported-source","generation-capacity-exceeded"],"x-enum-varnames":["ZUGFeRDPDFFailureCodeUnsupportedSource","ZUGFeRDPDFFailureCodeGenerationCapacityExceeded"]}}},"ZUGFeRDPDFRecipe":{"type":"object","description":"Frozen versioned generation recipe selected by trusted server configuration.","required":["name","version"],"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":256},"version":{"type":"string","minLength":1,"maxLength":256}}},"ZUGFeRDPDFStandard":{"type":"object","description":"Frozen standard and profile selected by trusted server configuration.","required":["name","version","profile"],"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":256},"version":{"type":"string","minLength":1,"maxLength":256},"profile":{"type":"string","minLength":1,"maxLength":256}}},"DocumentId":{"type":"string","description":"Opaque Beleg identifier. It does not encode storage identity.","pattern":"^doc_[0-9A-HJKMNP-TV-Z]{26}$","examples":["doc_9GXQ8W5V4R3P2N1M0KJHGF7ED6"]},"EInvoiceValidationId":{"type":"string","description":"Opaque durable e-invoice validation identifier.","pattern":"^eiv_[0-9A-HJKMNP-TV-Z]{26}$","examples":["eiv_9GXQ8W5V4R3P2N1M0KJHGF7ED6"]},"ZUGFeRDPDFId":{"type":"string","description":"Opaque durable ZUGFeRD PDF generation identifier.","pattern":"^eip_[0-9A-HJKMNP-TV-Z]{26}$","examples":["eip_9GXQ8W5V4R3P2N1M0KJHGF7ED6"]},"ZUGFeRDPDFCompleted":{"type":"object","description":"A terminal generation with immutable output and provenance evidence.","required":["id","validationId","invoiceDocumentId","visualDocumentId","target","recipe","status","requestedAt","completedAt","outputDocument","result"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/ZUGFeRDPDFId"},"validationId":{"$ref":"#/components/schemas/EInvoiceValidationId"},"invoiceDocumentId":{"$ref":"#/components/schemas/DocumentId"},"visualDocumentId":{"$ref":"#/components/schemas/DocumentId"},"target":{"$ref":"#/components/schemas/ZUGFeRDPDFStandard"},"recipe":{"$ref":"#/components/schemas/ZUGFeRDPDFRecipe"},"status":{"type":"string","const":"completed","x-enum-varnames":["ZUGFeRDPDFCompletedStatusCompleted"]},"requestedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"outputDocument":{"$ref":"#/components/schemas/ZUGFeRDPDFDocument"},"result":{"$ref":"#/components/schemas/ZUGFeRDPDFResult"}}},"ZUGFeRDPDFResult":{"type":"object","description":"Exact immutable generation evidence reported by the sidecar.","required":["schema","invoiceSha256","visualSha256","output","standard","recipe","processor","ruleset"],"additionalProperties":false,"properties":{"schema":{"type":"string","const":"devacc.einvoice.zugferd-pdf.v1","x-enum-varnames":["ZUGFeRDPDFResultSchemaV1"]},"invoiceSha256":{"type":"string","pattern":"^[0-9a-f]{64}$"},"visualSha256":{"type":"string","pattern":"^[0-9a-f]{64}$"},"output":{"$ref":"#/components/schemas/ZUGFeRDPDFOutput"},"standard":{"$ref":"#/components/schemas/ZUGFeRDPDFStandard"},"recipe":{"$ref":"#/components/schemas/ZUGFeRDPDFRecipe"},"processor":{"$ref":"#/components/schemas/ZUGFeRDPDFComponent"},"ruleset":{"$ref":"#/components/schemas/ZUGFeRDPDFComponent"}}},"ZUGFeRDPDFComponent":{"type":"object","description":"Opaque ZUGFeRD sidecar runtime or versioned artifact identity.","required":["name","version"],"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":256},"version":{"type":"string","minLength":1,"maxLength":256}}},"ZUGFeRDPDFOutput":{"type":"object","description":"Exact output facts verified before the derived Beleg was archived.","required":["contentId","mediaType","sizeBytes","sha256","pdfConformance","embeddedFilename"],"additionalProperties":false,"properties":{"contentId":{"type":"string","const":"output","x-enum-varnames":["ZUGFeRDPDFOutputContentIdOutput"]},"mediaType":{"type":"string","const":"application/pdf","x-enum-varnames":["ZUGFeRDPDFOutputMediaTypeApplicationPDF"]},"sizeBytes":{"type":"integer","format":"int64","minimum":1},"sha256":{"type":"string","pattern":"^[0-9a-f]{64}$"},"pdfConformance":{"type":"string","minLength":1,"maxLength":256,"description":"Conformance identity reported by the configured production sidecar.\nThis API records the evidence but does not independently certify it.\n"},"embeddedFilename":{"type":"string","minLength":1,"maxLength":255}}},"ZUGFeRDPDFDocument":{"type":"object","description":"Public metadata for the completed generation output. This Beleg is always\na newly derived, immutable archived PDF; its exact two-source lineage is\nretained server-side.\n","required":["id","origin","status","originalFilename","mediaType","sizeBytes","contentSha256","documentDate","classification","retentionUntil","createdAt","archivedAt"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/DocumentId"},"origin":{"type":"string","const":"derived","x-enum-varnames":["ZUGFeRDPDFDocumentOriginDerived"]},"status":{"type":"string","const":"archived","x-enum-varnames":["ZUGFeRDPDFDocumentStatusArchived"]},"originalFilename":{"type":"string"},"mediaType":{"type":"string","const":"application/pdf","x-enum-varnames":["ZUGFeRDPDFDocumentMediaTypeApplicationPDF"]},"sizeBytes":{"type":"integer","format":"int64","minimum":1},"contentSha256":{"type":"string","pattern":"^[0-9a-f]{64}$"},"documentDate":{"type":"string","format":"date"},"classification":{"type":"string","description":"Trusted server-assigned Beleg class."},"retentionUntil":{"type":"string","format":"date"},"createdAt":{"type":"string","format":"date-time"},"archivedAt":{"type":"string","format":"date-time"}}},"ZUGFeRDPDFPending":{"type":"object","description":"A durable generation waiting for processing or a retry.","required":["id","validationId","invoiceDocumentId","visualDocumentId","target","recipe","status","requestedAt"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/ZUGFeRDPDFId"},"validationId":{"$ref":"#/components/schemas/EInvoiceValidationId"},"invoiceDocumentId":{"$ref":"#/components/schemas/DocumentId"},"visualDocumentId":{"$ref":"#/components/schemas/DocumentId"},"target":{"$ref":"#/components/schemas/ZUGFeRDPDFStandard"},"recipe":{"$ref":"#/components/schemas/ZUGFeRDPDFRecipe"},"status":{"type":"string","const":"pending","x-enum-varnames":["ZUGFeRDPDFPendingStatusPending"]},"requestedAt":{"type":"string","format":"date-time"}}},"ZUGFeRDPDFCreate":{"type":"object","description":"The immutable PDF visual source to combine with validated CII XML.","required":["visualDocumentId"],"additionalProperties":false,"properties":{"visualDocumentId":{"$ref":"#/components/schemas/DocumentId"}}},"EInvoiceValidation":{"description":"Durable validation state. Branch on `status` before reading terminal\nfields: `pending` has neither result nor failure, `completed` has exactly\none result, and `failed` has exactly one public failure code.\n","oneOf":[{"$ref":"#/components/schemas/EInvoiceValidationPending"},{"$ref":"#/components/schemas/EInvoiceValidationCompleted"},{"$ref":"#/components/schemas/EInvoiceValidationFailed"}],"discriminator":{"propertyName":"status","mapping":{"pending":"#/components/schemas/EInvoiceValidationPending","completed":"#/components/schemas/EInvoiceValidationCompleted","failed":"#/components/schemas/EInvoiceValidationFailed"}}},"EInvoiceValidationFailed":{"type":"object","description":"A terminal validation processing failure without sensitive diagnostics.","required":["id","documentId","status","requestedAt","completedAt","failure"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/EInvoiceValidationId"},"documentId":{"$ref":"#/components/schemas/DocumentId"},"status":{"type":"string","const":"failed","x-enum-varnames":["EInvoiceValidationFailedStatusFailed"]},"requestedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"failure":{"$ref":"#/components/schemas/EInvoiceValidationFailure"}}},"EInvoiceValidationFailure":{"type":"object","description":"Stable public failure classification; internal diagnostics are private.","required":["code"],"additionalProperties":false,"properties":{"code":{"type":"string","const":"validation-processing-failed","x-enum-varnames":["EInvoiceValidationFailureCodeValidationProcessingFailed"]}}},"EInvoiceValidationCompleted":{"type":"object","description":"A terminal validation, whether the invoice is valid or invalid.","required":["id","documentId","status","requestedAt","completedAt","result"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/EInvoiceValidationId"},"documentId":{"$ref":"#/components/schemas/DocumentId"},"status":{"type":"string","const":"completed","x-enum-varnames":["EInvoiceValidationCompletedStatusCompleted"]},"requestedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"result":{"$ref":"#/components/schemas/EInvoiceValidationResult"}}},"EInvoiceValidationResult":{"type":"object","description":"Terminal result for the exact archived source. Validator and ruleset\nversions are opaque identities reported under the pinned standalone-platform\npolicy, never guessed by this service.\n","required":["schema","sourceSha256","valid","syntax","validator","ruleset","issues"],"additionalProperties":false,"properties":{"schema":{"type":"string","const":"devacc.einvoice.validation.v1","x-enum-varnames":["EInvoiceValidationResultSchemaV1"]},"sourceSha256":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"SHA-256 of the exact immutable source version that was validated."},"valid":{"type":"boolean","description":"False is a completed validation outcome, not a processing error."},"syntax":{"type":"string","enum":["ubl","cii","unknown"],"x-enum-varnames":["EInvoiceValidationSyntaxUBL","EInvoiceValidationSyntaxCII","EInvoiceValidationSyntaxUnknown"],"description":"Canonical syntax classification. `valid: true` is only possible for\n`ubl` or `cii`; `unknown` is retained only for invalid outcomes.\n"},"profile":{"type":"string","minLength":1,"maxLength":2048,"description":"Optional non-empty profile identity of at most 2,048 UTF-8 bytes."},"validator":{"$ref":"#/components/schemas/EInvoiceValidationComponent"},"ruleset":{"$ref":"#/components/schemas/EInvoiceValidationComponent"},"issues":{"type":"array","maxItems":1000,"items":{"$ref":"#/components/schemas/EInvoiceValidationIssue"}}}},"EInvoiceValidationIssue":{"type":"object","description":"One structured validator finding.","required":["severity","code","message"],"additionalProperties":false,"properties":{"severity":{"type":"string","enum":["error","warning","information"],"x-enum-varnames":["EInvoiceValidationIssueSeverityError","EInvoiceValidationIssueSeverityWarning","EInvoiceValidationIssueSeverityInformation"]},"code":{"type":"string","minLength":1,"maxLength":256,"description":"Non-empty stable finding code of at most 256 UTF-8 bytes."},"message":{"type":"string","minLength":1,"maxLength":8192,"description":"Non-empty finding text of at most 8,192 UTF-8 bytes."},"location":{"type":"string","minLength":1,"maxLength":8192,"description":"Optional non-empty source location of at most 8,192 UTF-8 bytes."}}},"EInvoiceValidationComponent":{"type":"object","description":"Opaque processor runtime or versioned compliance-artifact identity.","required":["name","version"],"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":256,"description":"Non-empty identity of at most 256 UTF-8 bytes."},"version":{"type":"string","minLength":1,"maxLength":256,"description":"Non-empty opaque version of at most 256 UTF-8 bytes."}}},"EInvoiceValidationPending":{"type":"object","description":"A durable validation waiting for processing or a retry.","required":["id","documentId","status","requestedAt"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/EInvoiceValidationId"},"documentId":{"$ref":"#/components/schemas/DocumentId"},"status":{"type":"string","const":"pending","x-enum-varnames":["EInvoiceValidationPendingStatusPending"]},"requestedAt":{"type":"string","format":"date-time"}}},"DocumentPreservation":{"description":"An immutable preservation decision and its durable execution status.","type":"object","required":["id","documentId","requestedBy","requestedAt","reason","legalHold","status"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/DocumentPreservationId"},"documentId":{"$ref":"#/components/schemas/DocumentId"},"requestedBy":{"$ref":"#/components/schemas/MembershipId"},"requestedAt":{"type":"string","format":"date-time"},"reason":{"type":"string","minLength":1,"maxLength":1000},"retainUntil":{"type":"string","format":"date-time","description":"Requested exclusive retention deadline, absent for a hold-only request."},"legalHold":{"type":"boolean","description":"True requests a legal hold ON. False does not request any hold change."},"status":{"type":"string","enum":["pending","applied"],"x-enum-varnames":["DocumentPreservationStatusPending","DocumentPreservationStatusApplied"]},"nextAttemptAt":{"type":"string","format":"date-time","description":"Earliest scheduled retry after a storage or verification failure."},"failureCode":{"type":"string","enum":["archive-unavailable","source-integrity-mismatch","protection-unconfirmed"],"description":"Last retry reason. No provider errors or storage coordinates are exposed."},"appliedAt":{"type":"string","format":"date-time","description":"Time confirmed protection was committed to the audit record."},"observedRetainUntil":{"type":"string","format":"date-time","description":"Confirmed COMPLIANCE deadline, at least the requested protection."},"observedLegalHold":{"type":"boolean","description":"Legal hold observed when the request was applied."}}},"MembershipId":{"type":"string","description":"Opaque public membership identifier.","pattern":"^mem_[0-9A-HJKMNP-TV-Z]{26}$"},"DocumentPreservationId":{"type":"string","description":"Opaque durable preservation-request identifier.","pattern":"^prsv_[0-9A-HJKMNP-TV-Z]{26}$"},"DocumentPreservationCreate":{"type":"object","required":["reason"],"minProperties":2,"additionalProperties":false,"description":"Supply retainUntil, legalHold=true, or both, with a nonblank reason without\nleading or trailing whitespace. retainUntil is an exclusive whole-second\nUTC deadline, in the future and later than all accepted retention decisions.\nTo preserve through a calendar day, use the following day's UTC midnight.\n","properties":{"reason":{"type":"string","minLength":1,"maxLength":1000},"retainUntil":{"type":"string","format":"date-time","pattern":"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|[+-]00:00)$"},"legalHold":{"type":"boolean","const":true}}},"Document":{"type":"object","description":"Public metadata for an immutable Beleg declaration.","required":["id","origin","status","originalFilename","mediaType","sizeBytes","contentSha256","documentDate","classification","retentionUntil","createdAt","archivedAt"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/DocumentId"},"origin":{"$ref":"#/components/schemas/DocumentOrigin"},"status":{"$ref":"#/components/schemas/DocumentStatus"},"originalFilename":{"type":"string"},"mediaType":{"type":"string"},"sizeBytes":{"type":"integer","format":"int64","minimum":1},"contentSha256":{"type":"string","pattern":"^[0-9a-f]{64}$"},"documentDate":{"type":"string","format":"date"},"classification":{"type":"string","description":"Trusted server-assigned Beleg class; never caller-selected."},"retentionUntil":{"type":"string","format":"date","description":"Inclusive legal retention date selected by the server."},"createdAt":{"type":"string","format":"date-time"},"archivedAt":{"type":["string","null"],"format":"date-time"}}},"DocumentStatus":{"type":"string","description":"`pending` awaits a verified direct or server-owned write; `archived` names\nan immutable, Object-Locked version whose evidence is sealed in the archive.\n","enum":["pending","archived"]},"DocumentOrigin":{"type":"string","description":"`uploaded` is an original caller-provided Beleg. `derived` is a new immutable\nserver-produced Beleg whose exact source lineage is retained independently.\n","enum":["uploaded","derived"]},"DocumentUploadRequest":{"type":"object","description":"Short-lived exact HTTP request for a direct immutable upload.","required":["url","method","headers","expiresAt"],"additionalProperties":false,"properties":{"url":{"type":"string","format":"uri","description":"Signed destination. Treat it as a secret until it expires."},"method":{"type":"string","const":"PUT"},"headers":{"type":"object","description":"Every header must be sent with the exact returned value.","additionalProperties":{"type":"string"}},"expiresAt":{"type":"string","format":"date-time"}}},"DocumentSourceList":{"type":"object","description":"All direct sources; empty for an uploaded original.","required":["data"],"additionalProperties":false,"properties":{"data":{"type":"array","maxItems":2,"items":{"$ref":"#/components/schemas/DocumentSource"}}}},"DocumentSource":{"type":"object","description":"One immutable edge from a generated Beleg to its direct source.","required":["documentId","role","kind"],"additionalProperties":false,"properties":{"documentId":{"$ref":"#/components/schemas/DocumentId"},"role":{"type":"string","description":"The source's role in generating the output.","enum":["invoice","visual"]},"kind":{"type":"string","description":"The transformation which produced the requested output.","enum":["zugferd-pdf"]}}},"DocumentCreateResult":{"type":"object","description":"A pending Beleg plus its first direct-upload request.","required":["document","upload"],"additionalProperties":false,"properties":{"document":{"$ref":"#/components/schemas/Document"},"upload":{"$ref":"#/components/schemas/DocumentUploadRequest"}}},"DocumentCreate":{"type":"object","description":"Caller-known immutable metadata for a Buchungsbeleg upload.","required":["originalFilename","mediaType","sizeBytes","contentSha256","documentDate"],"additionalProperties":false,"properties":{"originalFilename":{"type":"string","minLength":1,"description":"A UTF-8 basename of at most 255 bytes, never a path.","examples":["rechnung-2026-0042.pdf"]},"mediaType":{"type":"string","minLength":3,"description":"Canonical media type covered by the upload signature, at most 255\nUTF-8 bytes.\n","examples":["application/pdf"]},"sizeBytes":{"type":"integer","format":"int64","minimum":1,"description":"Exact content length in bytes.","examples":[48192]},"contentSha256":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"Exact SHA-256 digest in canonical lowercase hexadecimal.","examples":["0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"]},"documentDate":{"type":"string","format":"date","description":"Belegdatum used to select the versioned retention policy."}}},"DocumentPage":{"type":"object","description":"One page in immutable document-ID order; not a snapshot across requests.","required":["data"],"additionalProperties":false,"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Document"}},"nextCursor":{"type":"string","description":"Pass as after with unchanged filters; absent on the last page.","$ref":"#/components/schemas/DocumentId"}}},"EntryId":{"type":"string","description":"Opaque Buchung identifier. Not the Buchungsnummer — that is `seq`.","pattern":"^je_[0-9A-HJKMNP-TV-Z]{26}$","examples":["je_9GXQ8W5V4R3P2N1M0KJHGF7ED6"]},"JournalEntry":{"type":"object","description":"A Buchung — a draft, or a posted entry in the hash chain.","required":["id","periodId","status","bookingDate","currency","lines","createdAt"],"additionalProperties":false,"properties":{"bookingText":{"type":["string","null"],"minLength":1,"maxLength":500,"description":"Buchungstext, preserved exactly and immutable once posted. Null or omitted means absent; U+0000 is forbidden."},"externalReference":{"type":["string","null"],"minLength":1,"maxLength":255,"description":"Caller business reference, preserved exactly and immutable once posted. Not unique; use Idempotency-Key for retries. Null or omitted means absent; U+0000 is forbidden."},"id":{"$ref":"#/components/schemas/EntryId"},"periodId":{"$ref":"#/components/schemas/PeriodId"},"status":{"$ref":"#/components/schemas/JournalEntryStatus"},"bookingDate":{"type":"string","format":"date"},"currency":{"$ref":"#/components/schemas/Currency"},"seq":{"type":["integer","null"],"format":"int64","description":"The legal Buchungsnummer — gapless and strictly increasing per tenant.\nAssigned at posting; null on a draft.\n"},"entryHash":{"type":["string","null"],"description":"Opaque server-side audit fingerprint for this chain link. The canonical\npayload intentionally includes private identifiers that are not exposed\nby the public API, so clients must not treat this as a recomputable proof.\nAssigned at posting; null on a draft.\n","pattern":"^[0-9a-f]{64}$"},"reversalOf":{"type":"string","description":"The Buchung this one reverses (Storno), if any.","$ref":"#/components/schemas/EntryId"},"lines":{"type":"array","minItems":2,"items":{"$ref":"#/components/schemas/JournalLine"}},"createdAt":{"type":"string","format":"date-time"},"postedAt":{"type":["string","null"],"format":"date-time"}}},"JournalLine":{"type":"object","description":"One line of a Buchung as booked.","required":["lineNo","accountId","accountNo","side","amount"],"additionalProperties":false,"properties":{"taxRuleId":{"type":"string","maxLength":128,"description":"Optional immutable published rule; requires matching taxKey and effective booking date."},"lineNo":{"type":"integer","description":"Position within the Buchung, starting at 1.","minimum":1},"accountId":{"$ref":"#/components/schemas/AccountId"},"accountNo":{"type":"string","description":"The Kontonummer as booked — frozen at creation, so it keeps reading\ntrue even if the account is later renumbered (INVARIANT #10).\n"},"side":{"$ref":"#/components/schemas/Side"},"amount":{"$ref":"#/components/schemas/Money"},"taxKey":{"type":["string","null"]}}},"Money":{"type":"object","description":"An exact monetary amount. `amount` is an integer string in the currency's\nMINOR units (cents for EUR) — never a JSON number, so it cannot be rounded\nthrough a binary float. `\"150000\"` with currency `EUR` is 1500,00 €.\n","required":["amount","currency"],"additionalProperties":false,"properties":{"amount":{"type":"string","description":"Signed integer count of minor units, no separators, no exponent.","pattern":"^-?(0|[1-9][0-9]{0,18})$","examples":["150000"]},"currency":{"$ref":"#/components/schemas/Currency"}}},"Currency":{"type":"string","description":"ISO-4217 alphabetic currency code.","pattern":"^[A-Z]{3}$","examples":["EUR"]},"Side":{"type":"string","description":"Buchungsseite — Soll (debit) or Haben (credit).","enum":["debit","credit"]},"AccountId":{"type":"string","description":"Opaque account identifier.","pattern":"^acct_[0-9A-HJKMNP-TV-Z]{26}$","examples":["acct_9GXQ8W5V4R3P2N1M0KJHGF7ED6"]},"JournalEntryStatus":{"type":"string","description":"`draft` is mutable and carries no `seq`; `posted` is in the hash chain and\nimmutable — corrections are new reversal entries (Storno).\n","enum":["draft","posted"]},"PeriodId":{"type":"string","description":"Opaque period identifier.","pattern":"^per_[0-9A-HJKMNP-TV-Z]{26}$","examples":["per_9GXQ8W5V4R3P2N1M0KJHGF7ED6"]},"JournalEntryReversalCreate":{"type":"object","description":"Posting coordinates for a new Storno; economic data, bookingText and externalReference are inherited unchanged from the original.","required":["periodId","bookingDate"],"additionalProperties":false,"properties":{"periodId":{"$ref":"#/components/schemas/PeriodId"},"bookingDate":{"type":"string","format":"date","description":"Buchungsdatum; must fall inside the destination period."}}},"JournalEntryCreate":{"type":"object","description":"A balanced Buchung to draft.","required":["periodId","bookingDate","currency","lines"],"additionalProperties":false,"properties":{"bookingText":{"type":["string","null"],"minLength":1,"maxLength":500,"description":"Buchungstext, preserved exactly and immutable once posted. Null or omitted means absent; U+0000 is forbidden."},"externalReference":{"type":["string","null"],"minLength":1,"maxLength":255,"description":"Caller business reference, preserved exactly and immutable once posted. Not unique; use Idempotency-Key for retries. Null or omitted means absent; U+0000 is forbidden."},"periodId":{"$ref":"#/components/schemas/PeriodId"},"bookingDate":{"type":"string","format":"date","description":"Buchungsdatum; must fall inside the period."},"currency":{"$ref":"#/components/schemas/Currency"},"lines":{"type":"array","description":"At least two lines. Every line's currency must equal the entry's, and\nthe debit total must equal the credit total.\n","minItems":2,"items":{"$ref":"#/components/schemas/JournalLineCreate"}}}},"JournalLineCreate":{"type":"object","description":"One line of a Buchung to create — a Konto, a side, and an amount.","required":["accountId","side","amount"],"additionalProperties":false,"properties":{"taxRuleId":{"type":"string","maxLength":128,"description":"Optional immutable published rule; requires matching taxKey and effective booking date."},"accountId":{"$ref":"#/components/schemas/AccountId"},"side":{"$ref":"#/components/schemas/Side"},"amount":{"type":"object","description":"Always positive; `side` carries the direction.","$ref":"#/components/schemas/Money"},"taxKey":{"type":["string","null"],"description":"Steuerschlüssel.","maxLength":32}}},"JournalEntryPage":{"type":"object","description":"One page in immutable entry-ID order; not a snapshot across requests.","required":["data"],"additionalProperties":false,"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/JournalEntry"}},"nextCursor":{"type":"string","description":"Pass as after with unchanged filters; absent on the last page.","$ref":"#/components/schemas/EntryId"}}},"Period":{"type":"object","description":"An accounting period and its Festschreibung state.","required":["id","label","startsOn","endsOn","status","createdAt"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/PeriodId"},"label":{"type":"string","examples":["2026-01"]},"startsOn":{"type":"string","format":"date"},"endsOn":{"type":"string","format":"date","description":"Inclusive last day of the period."},"status":{"$ref":"#/components/schemas/PeriodStatus"},"festgeschriebenAt":{"type":["string","null"],"format":"date-time","description":"When the period was sealed; null while it is open."},"createdAt":{"type":"string","format":"date-time"}}},"PeriodStatus":{"type":"string","description":"`open` accepts postings; `festgeschrieben` is sealed and immutable\n(Festschreibung, §146 AO).\n","enum":["open","festgeschrieben"]},"PeriodCreate":{"type":"object","description":"Fields accepted when opening a period.","required":["label","startsOn","endsOn"],"additionalProperties":false,"properties":{"label":{"type":"string","minLength":1,"maxLength":64,"examples":["2026-01"]},"startsOn":{"type":"string","format":"date"},"endsOn":{"type":"string","format":"date","description":"Inclusive last day; must not precede `startsOn`."}}},"PeriodPage":{"type":"object","description":"One page of periods, earliest start date first.","required":["data"],"additionalProperties":false,"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Period"}},"nextCursor":{"type":"string","description":"Pass as the `after` query parameter to fetch the next page of periods.\nAbsent once the last page has been returned.\n","$ref":"#/components/schemas/PeriodId"}}},"AccountBalances":{"type":"object","description":"Per-currency balances at a fixed posting cutoff; no currency conversion.","required":["accountId","asOf","data"],"additionalProperties":false,"properties":{"accountId":{"$ref":"#/components/schemas/AccountId"},"asOf":{"$ref":"#/components/schemas/PostingSnapshot"},"data":{"type":"array","items":{"$ref":"#/components/schemas/AccountBalance"}}}},"AccountBalance":{"type":"object","description":"Exact debit/credit totals and signed balances in one currency.","required":["currency","openingBalance","debitTotal","creditTotal","closingBalance"],"additionalProperties":false,"properties":{"currency":{"$ref":"#/components/schemas/Currency"},"openingBalance":{"description":"Signed debit minus credit before bookingDateFrom, or zero without it.","$ref":"#/components/schemas/Money"},"debitTotal":{"description":"Nonnegative debit sum within the inclusive date range.","$ref":"#/components/schemas/Money"},"creditTotal":{"description":"Nonnegative credit sum within the inclusive date range.","$ref":"#/components/schemas/Money"},"closingBalance":{"description":"Signed debit minus credit through bookingDateTo at asOf.","$ref":"#/components/schemas/Money"}}},"PostingSnapshot":{"type":"string","description":"A posted tenant entry's public ID, or empty for the permanent zero cutoff.","pattern":"^(empty|je_[0-9A-HJKMNP-TV-Z]{26})$","examples":["empty"]},"AccountStatementPage":{"type":"object","description":"A bounded page of posted lines at one stable booking cutoff.","required":["accountId","asOf","data"],"additionalProperties":false,"properties":{"accountId":{"$ref":"#/components/schemas/AccountId"},"asOf":{"$ref":"#/components/schemas/PostingSnapshot"},"data":{"type":"array","items":{"$ref":"#/components/schemas/AccountMovement"}},"nextCursor":{"description":"Omitted on the final page; preserves asOf when passed as after.","$ref":"#/components/schemas/AccountStatementCursor"}}},"AccountStatementCursor":{"type":"string","description":"Opaque position including the posting snapshot; pass unchanged as after.","maxLength":73,"examples":["v1:je_9GXQ8W5V4R3P2N1M0KJHGF7ED6:je_9GXQ8W5V4R3P2N1M0KJHGF7ED6:1"]},"AccountMovement":{"type":"object","description":"One immutable posted line for the requested account.","required":["entryId","seq","lineNo","bookingDate","periodId","accountNo","side","amount","taxKey"],"additionalProperties":false,"properties":{"taxRuleId":{"type":"string","maxLength":128,"description":"Optional immutable published rule; requires matching taxKey and effective booking date."},"bookingText":{"type":["string","null"],"minLength":1,"maxLength":500,"description":"Buchungstext, preserved exactly and immutable once posted. Null or omitted means absent; U+0000 is forbidden."},"externalReference":{"type":["string","null"],"minLength":1,"maxLength":255,"description":"Caller business reference, preserved exactly and immutable once posted. Not unique; use Idempotency-Key for retries. Null or omitted means absent; U+0000 is forbidden."},"entryId":{"$ref":"#/components/schemas/EntryId"},"seq":{"type":"integer","format":"int64","minimum":1,"description":"The Buchungsnummer shared by every line in this entry."},"lineNo":{"type":"integer","minimum":1,"description":"Original position in the journal entry, not a page offset."},"bookingDate":{"type":"string","format":"date"},"periodId":{"$ref":"#/components/schemas/PeriodId"},"accountNo":{"type":"string","description":"Frozen Kontonummer as booked."},"side":{"$ref":"#/components/schemas/Side"},"amount":{"$ref":"#/components/schemas/Money"},"taxKey":{"type":["string","null"]},"reversalOf":{"description":"The original entry when this line belongs to a Storno.","$ref":"#/components/schemas/EntryId"}}},"Account":{"type":"object","description":"A Konto in the tenant's chart of accounts.","required":["id","accountNo","name","createdAt"],"additionalProperties":false,"properties":{"retiredAt":{"description":"Retirement prevents new ordinary postings; historical Storno remains available.","type":["string","null"],"format":"date-time"},"id":{"$ref":"#/components/schemas/AccountId"},"accountNo":{"type":"string","description":"Kontonummer within the tenant's chart of accounts.","examples":["1200"]},"name":{"type":"string","examples":["Bank"]},"accountType":{"type":["string","null"],"description":"Optional account classification supplied by the consumer."},"createdAt":{"type":"string","format":"date-time"}}},"AccountCreate":{"type":"object","description":"Fields accepted when adding a Konto.","required":["accountNo","name"],"additionalProperties":false,"properties":{"accountNo":{"type":"string","minLength":1,"maxLength":32,"examples":["1200"]},"name":{"type":"string","minLength":1,"maxLength":200,"examples":["Bank"]},"accountType":{"type":["string","null"],"maxLength":64}}},"AccountPage":{"type":"object","description":"One page of accounts, oldest Kontonummer first.","required":["data"],"additionalProperties":false,"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Account"}},"nextCursor":{"type":"string","description":"Pass as the `after` query parameter to fetch the next page of accounts.\nAbsent once the last page has been returned.\n","$ref":"#/components/schemas/AccountId"}}},"Membership":{"type":"object","description":"An external subject's explicit grants in a tenant.","additionalProperties":false,"required":["id","subject","permissions","status","revision","createdAt"],"properties":{"id":{"$ref":"#/components/schemas/MembershipId"},"subject":{"type":"string"},"permissions":{"$ref":"#/components/schemas/AccessPermissions"},"status":{"$ref":"#/components/schemas/MembershipStatus"},"revision":{"type":"integer","minimum":1,"maximum":2147483647},"createdAt":{"type":"string","format":"date-time"}}},"MembershipStatus":{"type":"string","description":"An inactive membership cannot bind to an API request.","enum":["active","inactive"]},"AccessPermissions":{"type":"array","description":"Explicit unique grants; an empty array grants no operations.","maxItems":9,"uniqueItems":true,"items":{"$ref":"#/components/schemas/AccessPermission"}},"AccessPermission":{"type":"string","description":"A local membership grant. access.manage is explicit partner-wide administration.","enum":["accounting.read","accounting.write","documents.read","documents.write","periods.close","webhooks.manage","access.manage","exports.read","audit.read"]},"MembershipReplace":{"type":"object","description":"Complete grant replacement or revocation guarded by the current revision.","additionalProperties":false,"required":["permissions","status","expectedRevision"],"properties":{"permissions":{"$ref":"#/components/schemas/AccessPermissions"},"status":{"$ref":"#/components/schemas/MembershipStatus"},"expectedRevision":{"type":"integer","minimum":1,"maximum":2147483647}}},"TenantId":{"type":"string","description":"Opaque public tenant identifier.","pattern":"^ten_[0-9A-HJKMNP-TV-Z]{26}$"},"MembershipCreate":{"type":"object","description":"Explicit grants for a subject in the administrator's trusted issuer.","additionalProperties":false,"required":["subject","permissions"],"properties":{"subject":{"type":"string","minLength":1,"maxLength":512},"permissions":{"$ref":"#/components/schemas/AccessPermissions"}}},"MembershipPage":{"type":"object","description":"One page ordered by public identifier.","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Membership"}},"nextCursor":{"description":"Exclusive boundary for the next page; absent on the last page.","$ref":"#/components/schemas/MembershipId"}}},"Tenant":{"type":"object","description":"An isolated accounting tenant within the caller's partner.","additionalProperties":false,"required":["id","name","createdAt"],"properties":{"id":{"$ref":"#/components/schemas/TenantId"},"name":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}},"TenantCreate":{"type":"object","description":"A new isolated tenant.","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200}}},"TenantPage":{"type":"object","description":"One page ordered by public identifier.","additionalProperties":false,"required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Tenant"}},"nextCursor":{"description":"Exclusive boundary for the next page; absent on the last page.","$ref":"#/components/schemas/TenantId"}}},"AccountMapping":{"description":"A complete immutable tenant-owned account mapping.","type":"object","required":["id","version","digest","createdAt","items"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/AccountMappingId"},"version":{"type":"integer","format":"int64","minimum":1},"digest":{"type":"string","pattern":"^sha256:[0-9a-f]{64}$"},"createdAt":{"type":"string","format":"date-time"},"items":{"type":"array","items":{"$ref":"#/components/schemas/AccountMappingItem"}}}},"AccountMappingItem":{"description":"Immutable classification and account labels frozen at publication.","type":"object","required":["accountId","accountNo","accountName","classification","reportCode","reportName"],"additionalProperties":false,"properties":{"accountId":{"$ref":"#/components/schemas/AccountId"},"accountNo":{"type":"string"},"accountName":{"type":"string"},"classification":{"$ref":"#/components/schemas/AccountClassification"},"reportCode":{"type":"string"},"reportName":{"type":"string"}}},"AccountClassification":{"description":"Explicit consumer classification for financial reporting.","type":"string","enum":["asset","liability","equity","income","expense"]},"AccountMappingId":{"description":"Opaque tenant-owned account mapping revision identifier.","type":"string","pattern":"^map_[0-9A-HJKMNP-TV-Z]{26}$"},"AccountMappingCreate":{"description":"Complete classification revision, subject also to the global mutation body byte limit.","type":"object","required":["items"],"additionalProperties":false,"properties":{"items":{"type":"array","minItems":1,"maxItems":10000,"items":{"$ref":"#/components/schemas/AccountMappingInputItem"}}}},"AccountMappingInputItem":{"description":"Consumer-selected classification and report section for one account.","type":"object","required":["accountId","classification","reportCode","reportName"],"additionalProperties":false,"properties":{"accountId":{"$ref":"#/components/schemas/AccountId"},"classification":{"$ref":"#/components/schemas/AccountClassification"},"reportCode":{"type":"string","minLength":1,"maxLength":64},"reportName":{"type":"string","minLength":1,"maxLength":200}}},"AccountMappingPage":{"description":"Mapping revision identities in ascending version order.","type":"object","required":["data"],"additionalProperties":false,"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AccountMappingSummary"}},"nextCursor":{"$ref":"#/components/schemas/AccountMappingId"}}},"AccountMappingSummary":{"description":"Published revision identity and content digest.","type":"object","required":["id","version","digest","createdAt"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/AccountMappingId"},"version":{"type":"integer","format":"int64","minimum":1},"digest":{"type":"string","pattern":"^sha256:[0-9a-f]{64}$"},"createdAt":{"type":"string","format":"date-time"}}},"TrialBalance":{"type":"object","description":"Summen- und Saldenliste with exact currency control totals.","required":["asOf","data","totals"],"additionalProperties":false,"properties":{"asOf":{"$ref":"#/components/schemas/PostingSnapshot"},"data":{"type":"array","items":{"$ref":"#/components/schemas/TrialBalanceRow"}},"totals":{"type":"array","items":{"$ref":"#/components/schemas/AccountBalance"}}}},"TrialBalanceRow":{"type":"object","description":"Exact totals for one account and currency.","required":["accountId","accountNo","currency","openingBalance","debitTotal","creditTotal","closingBalance"],"additionalProperties":false,"properties":{"accountId":{"$ref":"#/components/schemas/AccountId"},"accountNo":{"type":"string","description":"Account number from the latest participating posted line."},"currency":{"$ref":"#/components/schemas/Currency"},"openingBalance":{"$ref":"#/components/schemas/Money"},"debitTotal":{"$ref":"#/components/schemas/Money"},"creditTotal":{"$ref":"#/components/schemas/Money"},"closingBalance":{"$ref":"#/components/schemas/Money"}}},"FiscalYearCarryforward":{"type":"object","description":"Consumer-selected target and evidence for one-currency opening.","required":["targetYearId","targetPeriodId","mappingRevisionId","resultAccountId","documentId","currency"],"additionalProperties":false,"properties":{"targetYearId":{"$ref":"#/components/schemas/FiscalYearId"},"targetPeriodId":{"$ref":"#/components/schemas/PeriodId"},"mappingRevisionId":{"$ref":"#/components/schemas/AccountMappingId"},"resultAccountId":{"$ref":"#/components/schemas/AccountId"},"documentId":{"$ref":"#/components/schemas/DocumentId"},"currency":{"type":"string","pattern":"^[A-Z]{3}$"}}},"FiscalYearId":{"type":"string","description":"Opaque fiscal-year identifier.","pattern":"^fy_[0-9A-HJKMNP-TV-Z]{26}$"},"FiscalYearCloseResult":{"type":"object","description":"Closing entries and the sealed period.","required":["entries","period"],"additionalProperties":false,"properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/JournalEntry"}},"period":{"$ref":"#/components/schemas/Period"}}},"FiscalYearClose":{"type":"object","description":"Consumer-selected closing classification and evidence.","required":["periodId","mappingRevisionId","resultAccountId","documentId"],"additionalProperties":false,"properties":{"periodId":{"$ref":"#/components/schemas/PeriodId"},"mappingRevisionId":{"$ref":"#/components/schemas/AccountMappingId"},"resultAccountId":{"$ref":"#/components/schemas/AccountId"},"documentId":{"$ref":"#/components/schemas/DocumentId"}}},"FiscalYear":{"type":"object","description":"Tenant fiscal year and its finalization state.","required":["id","label","startsOn","endsOn","status","createdAt"],"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/FiscalYearId"},"label":{"type":"string"},"startsOn":{"type":"string","format":"date"},"endsOn":{"type":"string","format":"date"},"status":{"$ref":"#/components/schemas/FiscalYearStatus"},"finalizedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}},"FiscalYearStatus":{"type":"string","enum":["open","finalized"],"x-enum-varnames":["FiscalYearStatusOpen","FiscalYearStatusFinalized"],"description":"Finalized years cannot be changed."},"FiscalYearCreate":{"type":"object","description":"Inclusive fiscal-year date range chosen by the consumer.","required":["label","startsOn","endsOn"],"additionalProperties":false,"properties":{"label":{"type":"string","minLength":1,"maxLength":64},"startsOn":{"type":"string","format":"date"},"endsOn":{"type":"string","format":"date"}}},"FiscalYearPage":{"type":"object","description":"One page of fiscal years.","required":["data"],"additionalProperties":false,"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FiscalYear"}},"nextCursor":{"$ref":"#/components/schemas/FiscalYearId"}}},"FinancialStatements":{"type":"object","description":"Reproducible year-scoped reports under an explicit immutable mapping.","required":["asOf","fiscalYearId","mappingRevisionId","mappingDigest","bookingDateFrom","bookingDateTo","closingEntryIds","data","totals"],"additionalProperties":false,"properties":{"asOf":{"$ref":"#/components/schemas/PostingSnapshot"},"fiscalYearId":{"$ref":"#/components/schemas/FiscalYearId"},"mappingRevisionId":{"$ref":"#/components/schemas/AccountMappingId"},"mappingDigest":{"type":"string","pattern":"^sha256:[0-9a-f]{64}$"},"bookingDateFrom":{"type":"string","format":"date"},"bookingDateTo":{"type":"string","format":"date"},"closingEntryIds":{"type":"array","items":{"$ref":"#/components/schemas/EntryId"}},"data":{"type":"array","items":{"$ref":"#/components/schemas/FinancialStatementGroup"}},"totals":{"type":"array","items":{"$ref":"#/components/schemas/FinancialStatementTotals"}}}},"FinancialStatementTotals":{"type":"object","description":"Separate balance sheet and profit/loss control totals in one currency.","required":["currency","assets","liabilities","equity","income","expense","result","balanceDifference"],"additionalProperties":false,"properties":{"currency":{"$ref":"#/components/schemas/Currency"},"assets":{"$ref":"#/components/schemas/Money"},"liabilities":{"$ref":"#/components/schemas/Money"},"equity":{"$ref":"#/components/schemas/Money"},"income":{"$ref":"#/components/schemas/Money"},"expense":{"$ref":"#/components/schemas/Money"},"result":{"$ref":"#/components/schemas/Money"},"balanceDifference":{"$ref":"#/components/schemas/Money"}}},"FinancialStatementGroup":{"type":"object","description":"One consumer-defined report section and its underlying accounts.","required":["classification","reportCode","reportName","amount","accounts"],"additionalProperties":false,"properties":{"classification":{"$ref":"#/components/schemas/AccountClassification"},"reportCode":{"type":"string"},"reportName":{"type":"string"},"amount":{"$ref":"#/components/schemas/Money"},"accounts":{"type":"array","items":{"$ref":"#/components/schemas/FinancialStatementAccount"}}}},"FinancialStatementAccount":{"type":"object","description":"One account's year-scoped net in its classification's sign convention.","required":["accountId","accountNo","accountName","amount"],"additionalProperties":false,"properties":{"accountId":{"$ref":"#/components/schemas/AccountId"},"accountNo":{"type":"string"},"accountName":{"type":"string"},"amount":{"$ref":"#/components/schemas/Money"}}},"TaxCalculation":{"description":"Exact rounded tax with the selected rule provenance.","type":"object","additionalProperties":false,"required":["tax","evidence"],"properties":{"tax":{"$ref":"#/components/schemas/Money"},"evidence":{"$ref":"#/components/schemas/TaxRule"}}},"TaxRule":{"description":"An immutable rule from a digest-verified published artifact.","type":"object","additionalProperties":false,"required":["id","taxKey","effectiveFrom","effectiveUntil","rateNumerator","rateDenominator","sourceVersion","artifactSha256"],"properties":{"id":{"type":"string","maxLength":128},"taxKey":{"type":"string","maxLength":32},"effectiveFrom":{"type":"string","format":"date"},"effectiveUntil":{"type":["string","null"],"format":"date"},"rateNumerator":{"type":"string","pattern":"^[0-9]+$"},"rateDenominator":{"type":"string","pattern":"^[1-9][0-9]*$"},"sourceVersion":{"type":"string","maxLength":64},"artifactSha256":{"type":"string","pattern":"^[0-9a-f]{64}$"}}},"TaxCalculationInput":{"description":"Explicit net amount, booking date and qualified rule.","type":"object","additionalProperties":false,"required":["taxRuleId","bookingDate","net"],"properties":{"taxRuleId":{"type":"string","maxLength":128},"bookingDate":{"type":"string","format":"date"},"net":{"$ref":"#/components/schemas/Money"}}},"TaxRulePage":{"description":"A bounded page of published reference rules.","type":"object","additionalProperties":false,"required":["data","nextCursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TaxRule"},"maxItems":200},"nextCursor":{"type":["string","null"]}}}},"responses":{"Problem":{"description":"An error. Branch on `type`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"PreconditionRequired":{"description":"This mutation requires the latest item `ETag` in `If-Match`; no\nresource state or audit evidence was changed.\n","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"Unprocessable":{"description":"The request is well-formed but cannot be carried out — an unbalanced\nBuchung, or an `Idempotency-Key` reused with a different request (including\na different `Content-Type` or `If-Match` validator).\n","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"PreconditionFailed":{"description":"The supplied `If-Match` validator is well-formed but stale. No resource\nstate or audit evidence was changed. Fetch the item and retry the\nintended change with its new `ETag` and a fresh `Idempotency-Key`.\n","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"Conflict":{"description":"The request conflicts with current state — a duplicate, a sealed period, an\nentry that is no longer a draft, or an in-flight request with this\n`Idempotency-Key`.\n","headers":{"Retry-After":{"description":"Present only on `request-in-progress`, where the conflict is transient:\na duplicate of this request is still running and retrying after the\ngiven number of seconds will succeed. Absent on the other conflicts,\nwhich retrying cannot resolve.\n","required":false,"schema":{"type":"integer","minimum":0}}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"NotFound":{"description":"No such resource in the calling tenant.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"Forbidden":{"description":"The caller is authenticated for the tenant but lacks the explicit permission\nrequired for this operation. No data is read or written.\n","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"Unauthorized":{"description":"No valid tenant assertion. No data is read or written.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"BadRequest":{"description":"The request is malformed or violates the contract.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"headers":{"WebhookEndpointETag":{"required":true,"description":"Strong opaque validator for this endpoint's routing and signing-secret\nversion. Send this exact value as `If-Match` on replace, secret rotation,\nor deactivation. Operational KEK rewrapping does not change it.\n","schema":{"type":"string","pattern":"^\"whep-v1-[A-Za-z0-9_-]{32}\"$"},"example":"\"whep-v1-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\""},"ActingUserRecorded":{"required":false,"description":"Present with exactly v1 on successfully committed partner-attributed\nmachine writes and their idempotent replays. Absent on unattributed writes.\nAn attributed consumer must qualify this acknowledgment before presenting\nsuccess. Missing acknowledgment leaves an unknown attribution outcome.\n","schema":{"type":"string","const":"v1"}},"JournalEntryETag":{"required":true,"description":"Strong opaque validator bound to the entry identity and revision.","schema":{"type":"string","pattern":"^\"je-v1-[A-Za-z0-9_-]{32}\"$"}}},"parameters":{"WebhookEndpointIfMatch":{"name":"If-Match","in":"header","required":true,"description":"The exact strong `ETag` returned by the latest item response. Weak tags,\nwildcard matching, and validator lists are not accepted because every\nmutation must name one observed endpoint version. A missing precondition is\n`428 precondition-required`; a well-formed but stale validator is\n`412 precondition-failed`. After a 412, fetch the item and retry the intended\nchange with its new `ETag` and a fresh `Idempotency-Key`.\n","schema":{"type":"string","pattern":"^\"whep-v1-[A-Za-z0-9_-]{32}\"$"},"example":"\"whep-v1-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\""},"ActingUserSubject":{"name":"X-Accounting-Acting-User-Subject","in":"header","required":false,"description":"Stable opaque person reference in the issuer namespace, asserted by the\nauthenticated partner. Send exactly one value together with\nX-Accounting-Acting-User-Issuer; 1–200 ASCII bytes without whitespace or\ncommas. Use technical references, not names or email addresses. A different\nattributed human or asserting machine under the same Idempotency-Key is\na different request (422). Neither header is an Accounting membership ID.\n","schema":{"type":"string","minLength":1,"maxLength":200,"pattern":"^[\\x21-\\x2B\\x2D-\\x7E]+$"},"example":"person-42"},"ActingUserIssuer":{"name":"X-Accounting-Acting-User-Issuer","in":"header","required":false,"description":"Optional partner-attested human attribution, machine authentication only.\nSend exactly one value together with X-Accounting-Acting-User-Subject.\nAbsolute HTTPS issuer URI without credentials, query or fragment; ASCII\nwithout whitespace or commas. The authenticated partner asserts this\nidentity; Accounting does not independently verify a human login token.\nThis metadata grants no tenant access or permissions and never replaces\nthe authenticated machine membership. Retained as immutable same-transaction\nevidence with the write and included in the idempotency fingerprint.\nSuccessful attributed writes and their replays return\nX-Accounting-Acting-User-Recorded: v1. Deploy this API before enabling\nattributed writes; a missing acknowledgment leaves the outcome unqualified.\n","schema":{"type":"string","minLength":1,"maxLength":200,"pattern":"^https://[\\x21-\\x2B\\x2D-\\x7E]+$"},"example":"https://auth.d0.eu"},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":true,"description":"Client-generated key that makes this mutation replay-safe. Send exactly\none field value. Use a fresh value (a UUID or equivalent) per logical\noperation and reuse it verbatim when retrying that operation.\n\nOne layer of surrounding double quotes is stripped before the key is\ncompared, so the RFC 8941 structured-field form (`\"k\"`) and the bare form\n(`k`) name the same key.\n","schema":{"type":"string","minLength":1,"maxLength":255},"example":"2f1c7f6e-5f0b-4a3a-8f8f-2b8f6c1f9a11"},"WebhookEndpointId":{"name":"webhookEndpointId","in":"path","required":true,"description":"Identifier of the tenant-managed webhook endpoint.","schema":{"$ref":"#/components/schemas/WebhookEndpointId"}},"WebhookEndpointCursor":{"name":"after","in":"query","required":false,"description":"Return items after this webhook endpoint — the `nextCursor` of the\nprevious page.\n","schema":{"$ref":"#/components/schemas/WebhookEndpointId"}},"Limit":{"name":"limit","in":"query","required":false,"description":"Maximum number of items to return.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},"ZUGFeRDPDFId":{"name":"zugferdPdfId","in":"path","required":true,"description":"Identifier of the durable ZUGFeRD PDF generation.","schema":{"$ref":"#/components/schemas/ZUGFeRDPDFId"}},"EInvoiceValidationId":{"name":"validationId","in":"path","required":true,"description":"Identifier of the durable e-invoice validation request.","schema":{"$ref":"#/components/schemas/EInvoiceValidationId"}},"DocumentId":{"name":"documentId","in":"path","required":true,"description":"Identifier of the Beleg.","schema":{"$ref":"#/components/schemas/DocumentId"}},"DocumentPreservationId":{"name":"preservationId","in":"path","required":true,"description":"Identifier of the durable document preservation request.","schema":{"$ref":"#/components/schemas/DocumentPreservationId"}},"EntryId":{"name":"entryId","in":"path","required":true,"description":"Identifier of the Buchung.","schema":{"$ref":"#/components/schemas/EntryId"}},"JournalEntryPostIfMatch":{"name":"If-Match","in":"header","required":false,"description":"Optional strong item ETag of the reviewed draft. Checked under the same\nlock as posting. A stale revision returns 412; omission preserves the\nexisting unconditional posting behavior. Weak tags, wildcards, duplicate\nvalues and lists return 400. A failed request rolls back its key and can\nreuse it after reviewing the current draft. Use a new Idempotency-Key\nfor a new intent after a previously successful request.\n","schema":{"type":"string","pattern":"^\"je-v1-[A-Za-z0-9_-]{32}\"$"}},"JournalEntryIfMatch":{"name":"If-Match","in":"header","required":true,"description":"Exactly one strong item ETag observed from create, GET or replacement.\nWeak tags, wildcards and lists are rejected with 400. Missing tags return\n428; stale tags return 412. Fetch again and retry with a fresh\nIdempotency-Key after a stale version.\n","schema":{"type":"string","pattern":"^\"je-v1-[A-Za-z0-9_-]{32}\"$"}},"PeriodId":{"name":"periodId","in":"path","required":true,"description":"Identifier of the accounting period.","schema":{"$ref":"#/components/schemas/PeriodId"}},"PeriodCursor":{"name":"after","in":"query","required":false,"description":"Return items after this period — the `nextCursor` of the previous page.","schema":{"$ref":"#/components/schemas/PeriodId"}},"ReportDateTo":{"name":"bookingDateTo","in":"query","description":"Inclusive latest booking date; must not precede bookingDateFrom.","schema":{"type":"string","format":"date"}},"ReportDateFrom":{"name":"bookingDateFrom","in":"query","description":"Inclusive earliest booking date for movements; earlier history forms openingBalance.","schema":{"type":"string","format":"date"}},"ReportCurrency":{"name":"currency","in":"query","description":"Restrict to one currency without conversion; omit to include each separately.","schema":{"$ref":"#/components/schemas/Currency"}},"PostingSnapshot":{"name":"asOf","in":"query","description":"Inclusive posted-entry cutoff; omit for latest, or reuse a prior response's asOf.","schema":{"$ref":"#/components/schemas/PostingSnapshot"}},"AccountId":{"name":"accountId","in":"path","required":true,"description":"Identifier of the account, as returned when it was created.","schema":{"$ref":"#/components/schemas/AccountId"}},"AccountCursor":{"name":"after","in":"query","required":false,"description":"Return items after this account — the `nextCursor` of the previous page.","schema":{"$ref":"#/components/schemas/AccountId"}},"MembershipId":{"name":"membershipId","in":"path","required":true,"description":"Opaque public membership identifier.","schema":{"$ref":"#/components/schemas/MembershipId"}},"TenantId":{"name":"tenantId","in":"path","required":true,"description":"Opaque public tenant identifier.","schema":{"$ref":"#/components/schemas/TenantId"}},"MembershipCursor":{"name":"after","in":"query","description":"Exclusive public identifier boundary from nextCursor.","schema":{"$ref":"#/components/schemas/MembershipId"}},"TenantCursor":{"name":"after","in":"query","description":"Exclusive public identifier boundary from nextCursor.","schema":{"$ref":"#/components/schemas/TenantId"}},"AccountMappingId":{"description":"Opaque tenant-owned account mapping revision identifier.","name":"mappingId","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AccountMappingId"}},"AccountMappingCursor":{"description":"Mapping identifier returned as nextCursor by the previous page.","name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/AccountMappingId"}},"FiscalYearId":{"name":"yearId","in":"path","required":true,"description":"Opaque fiscal-year identifier.","schema":{"$ref":"#/components/schemas/FiscalYearId"}},"FiscalYearCursor":{"name":"after","in":"query","required":false,"description":"Fiscal-year ID returned as nextCursor by the preceding page.","schema":{"$ref":"#/components/schemas/FiscalYearId"}}}},"x-d0-environment":"production","x-d0-resource":"https://api.d0.eu/accounting"}