Rechnungen
Rechnungen und Gutschriften anlegen, ausstellen, versenden und als PDF oder XML abrufen – inklusive Validierung.
Create, issue, and manage invoices, credit notes, and their PDF/XML artifacts and validation.
Operationen
-
GET/invoicesList invoice summaries (newest first, paginated) -
POST/invoicesCreate an invoice (server computes all totals) -
POST/invoices/preview-totalsPreview the totals for a set of line items (nothing is created) -
GET/invoices/next-numberPreview the next auto-generated invoice number (no side effects) -
GET/invoices/{id}Get a full invoice by id -
PUT/invoices/{id}Replace a draft invoice (full replace of all mutable fields; server recomputes totals) -
DELETE/invoices/{id}Delete a draft invoice -
GET/invoices/{id}/pdfDownload the stored ZUGFeRD PDF for an issued invoice -
GET/invoices/{id}/xmlDownload the stored XRechnung 3.0 CII XML for an issued invoice -
GET/invoices/{id}/preview.pdfRender a non-persisted, watermarked WYSIWYG preview PDF for a draft -
PATCH/invoices/{id}/retry-pdfReset a failed PDF render to pending and retry generation -
PATCH/invoices/{id}/retry-xmlReset a failed XML render to pending and retry generation -
PATCH/invoices/{id}/processing-restrictionSet or clear the DSGVO Art. 18 processing restriction -
PATCH/invoices/{id}/scheduleTransition a draft to 'scheduled' with an auto-issuance date -
PATCH/invoices/{id}/finalizeIssue a draft (compliance gate). Runs the KoSIT gate synchronously: assigns the invoice number (sticky — reused on a retry after a gate failure), renders + validates both XRechnung and ZUGFeRD in parallel, and either commits (artifacts + report + email outbox) or rejects with actionable findings. the validation gate in report-only mode (rollout default) never blocks — the commit runs regardless of verdict, with `validationState` recording the outcome; the validation gate in enforce mode blocks on any error-severity finding. The invoice date (`issueDate`) is set to the day of issue (Europe/Berlin), whatever date the draft carried. -
POST/invoices/{id}/credit-noteDerive a Stornorechnung (credit note, UNTDID 381) from an issued invoice -
POST/invoices/{id}/validateDry-run preflight (compliance gate). Draft or scheduled only. Runs the same local preflight + parallel render+validate calls as `PATCH /invoices/{id}/finalize`, but never assigns a number (a placeholder BT-1 is used when the invoice is unnumbered) and never commits — no state change on the invoice, regardless of verdict. The report is stored with `kind=preflight`. -
POST/validateStateless Validation-as-a-service. Runs a submitted invoice through the SAME KoSIT + §14 compliance gate and returns the report — persisting NOTHING (no invoice, no stored report, no numbering, no metering). Requires the `validation:run` scope. Mounted only where standalone validation is enabled (off by default → 404). -
GET/invoices/{id}/validation-reportsList validation report metadata for an invoice: every attempt (issue attempts, successful issuance, preflight dry-runs), verdict, kind, artifact/validator versions, and findings. Byte-exact report XML is not included — see `GET /invoices/{id}/validation-reports/{reportId}`. -
GET/invoices/{id}/validation-reports/{reportId}Retrieve a stored Prüfbericht (native KoSIT XML, byte-exact). 404 (not the report) if `reportId` belongs to a different invoice than `id` — no cross-invoice leakage. -
GET/invoices/{id}/section14The typed §14 Abs. 4 UStG Pflichtangaben result for an invoice — the clean three-state `Section14Summary` the frontends bind to (the raw `kind='section14'` row also remains in the validation-reports list). For an issued document the stored result is returned (`evaluatedAt` = when it was computed); a lost post-commit persist is transparently recomputed over the immutable issued document and re-persisted (recompute-on-read), so the result is always retrievable. Advisory-only: `status` is `passed`/`has_warnings` (or `unavailable`) — NEVER `failed`; a `not_applicable` item is never counted as a warning. RLS-scoped — a cross-tenant request 404s (indistinguishable from missing). PII-free. -
POST/invoices/{id}/sendManually (re-)send an issued invoice's delivery email -
GET/invoices/{id}/sendsThe append-only send history of an invoice -
POST/invoices/{id}/external-sendRecord a dispatch you performed yourself -
POST/invoices/{id}/sends/{sendId}/retractionWithdraw a dispatch record
List invoice summaries (newest first, paginated)
GET /invoices
- Erforderlicher Scope
-
documents:read - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
page | Query | integer | Page number (1-indexed). Standard: 1 Bereich: ≥ 1 |
pageSize | Query | integer | Number of items per page. Standard: 25 Bereich: ≥ 1, ≤ 100 |
status | Query | string | Filter by invoice status. Omit to return all statuses. Werte: "draft", "scheduled", "issued", "paid", "void" |
correctionState | Query | string | Filter by the DERIVED correction state — computed from the document chain (the preceding-invoice link), never stored. Werte: "cancelled", "corrected", "none" |
validationState | Query | string | Compliance-gate filter. Werte: "failed", "passed", "legacy" |
customerId | Query | string (uuid) | Return only documents linked to this customer (exact match on the customer link). Documents created for an ad-hoc buyer carry no customer link and are therefore never returned when this filter is set. |
q | Query | string | Free-text filter, case-insensitive substring match over a CLOSED per-resource field set (invoices: invoice number + buyer name; offers: offer number + buyer name; customers: name + email). Trimmed; an empty/whitespace-only value is treated as absent, not as an error. Results are bounded by the pagination limit and by your own entity's scope. Deliberately NOT a fuzzy/ranked search — it is a plain substring match, and it does not search line items, notes or amounts. The term is never written to logs, audit records or webhook payloads. Länge: max. 100 |
transmissionProblem | Query | string | Return only documents whose latest system-performed dispatch is in a PROBLEM state — Werte: "true" |
Antworten
-
200Paginated list of invoice summaries
application/jsonobject -
400The query string was refused. Three distinguishable facts share this status, and a client should branch on the
errorcode rather than on the status:unknown_query_parameter— the request named one or more query parameters this deployment does not define, andparameterslists their names. Nothing was filtered and nothing was written. This is a DEPLOY-ORDER signal: it is what a client released ahead of the API sees, and it replaces the older behaviour in which an unrecognised parameter was silently ignored and the request answered200 OKwith the UNFILTERED collection — indistinguishable, to the caller, from the filter having been applied.validation_error— a parameter this endpoint does define carried an unusable value (including a required one being absent or too short).issues[]names the parameter and what was wrong with it.invalid_cursor— on cursor-paginated endpoints, the pagination cursor is malformed. No response on this path ever echoes a submitted VALUE; only parameter names appear.application/jsonUnknownQueryParameterError | ValidationError
Create an invoice (server computes all totals)
POST /invoices
Honors the optional Idempotency-Key header so a retried DRAFT create is a replay, not a duplicate. NB: issuance (PATCH /invoices/{id}/finalize) is deliberately NOT idempotency-wrapped — it is idempotent by domain construction (numbering sticky-lock + one-per-finalize metering).
- Erforderlicher Scope
-
documents:write - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
Idempotency-Key | Header | string | Optional client-supplied key that makes this mutating request safe to retry. The FIRST request runs and its outcome is recorded keyed by Länge: max. 255 |
Request-Body erforderlich
application/json CreateInvoice
{
"currency": "EUR",
"invoiceTypeCode": "380",
"buyer": {
"name": "Achsfeld Transporte GmbH",
"addressLine1": "Gewerbestraße 8",
"postalCode": "59192",
"city": "Bergkamen",
"countryCode": "DE",
"vatId": "DE245817096"
},
"buyerReference": "FP-2026-0219",
"deliveryDate": "2026-07-31",
"lineItems": [
{
"description": "Ladevorgänge Januar",
"quantity": "1",
"unitCode": "C62",
"unitPriceNet": "420.00",
"taxRatePercent": "19"
}
]
} Antworten
-
201The created invoice (or, on an Idempotency-Key replay, the recorded response)
application/jsonInvoice -
400Validation error (
validation_error), including an invoiceTypeCode not on the configured allowlist, or a request body that is not valid JSON (invalid_json). Issues may carry acode:service_timing_conflict(deliveryDate: the body carries both a single service date and a service period),reverse_charge_buyer_vat_id_required(buyer.vatId),vat_id_prefix_unknown(buyer.vatId/seller.vatId),currency_code_unknown(currency),vat_category_unavailable(lineItems.N.vatCategory),vat_rate_category_mismatch(lineItems.N.taxRatePercent),vat_exemption_reason_requiredandvat_exemption_reason_not_allowed(lineItems.N.vatExemptionReasonText), andunit_code_unknown(lineItems.N.unitCode). The three*_unknowncodes occur only where the deployment enforces that check.application/jsonValidationError -
409Idempotency-Key conflict.
IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_BODY— the key was already used with a different request body;REQUEST_IN_PROGRESS— the first request bearing this key is still in flight (retryable).application/jsonobject -
422Either an unresolvable
precedingInvoicereference (invoiceTypeCode="384" with a missing/dangling/draft/external reference) or a negative-amount line/total (UStAE 14.3 Abs. 2 Satz 7 — a reduction must be its own corrective document).application/problem+jsonInvalidPrecedingReferenceProblem | SplitRequiredProblem
Preview the totals for a set of line items (nothing is created)
POST /invoices/preview-totals
Returns the document totals, VAT breakdown and per-line net amounts that an invoice with these line items would get — computed by exactly the same server-side engine POST /invoices uses, so the previewed totals are byte-identical to the ones issuance persists for the same line items.
Nothing is created or consumed. No invoice, no invoice number, no numbering event, no metering record and no outbox entry — so no Idempotency-Key is accepted or needed, and the endpoint is safe to call as often, as concurrently and as out-of-order as a form's keystrokes demand. It is a read of would-be values, hence documents:read rather than documents:write, and it needs no persisted draft — which is the whole point: it previews an unsaved form.
Tax treatment is resolved on the server. Kleinunternehmer status (§ 19 UStG) is read from your entity's company profile; there is no field to send, and a client may not decide it. A Kleinunternehmer entity's preview therefore already shows the exempt (E) treatment its issued invoice will carry.
What this does NOT do: it is not a validation verdict (use POST /invoices/{id}/validate or POST /validate), and it cannot detect a mistyped amount — 1250 is a valid decimal and the server has no way to know 12.50 was meant. It shows the consequence of the numbers actually submitted, so a human can catch one that looks wrong.
- Erforderlicher Scope
-
documents:read - Zugang
- API-Schlüssel oder angemeldete Sitzung
Request-Body erforderlich
application/json PreviewTotalsRequest
Antworten
-
200The totals these line items would produce.
application/jsonPreviewTotals -
400Validation error (line-item shape / VAT-category rules). Issues may carry a
code:vat_category_unavailable(lineItems.N.vatCategory),vat_rate_category_mismatch(lineItems.N.taxRatePercent),vat_exemption_reason_requiredandvat_exemption_reason_not_allowed(lineItems.N.vatExemptionReasonText), andunit_code_unknown(lineItems.N.unitCode);unit_code_unknownoccurs only where the deployment enforces that check.application/jsonValidationError -
422A line has a non-positive quantity or a negative unit price ( UStAE 14.3 Abs. 2 Satz 7 — a reduction must be its own corrective document). The same refusal
POST /invoicesgives.application/problem+jsonSplitRequiredProblem
Preview the next auto-generated invoice number (no side effects)
GET /invoices/next-number
Read-only preview — does NOT consume the counter. The number actually assigned at issue time (PATCH /invoices/{id}/finalize) can differ if another invoice is issued in between (the counter is only reserved atomically at issue time, not here).
- Erforderlicher Scope
-
documents:read - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
issueDate | Query | string (date) | ISO date (YYYY-MM-DD) the number's date segment would use. Defaults to today. |
Antworten
-
200The previewed number, or null if no company profile is configured yet.
application/jsonobject
Get a full invoice by id
GET /invoices/{id}
- Erforderlicher Scope
-
documents:read - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) |
Antworten
-
200The invoice
application/jsonInvoice -
404Not found
Replace a draft invoice (full replace of all mutable fields; server recomputes totals)
PUT /invoices/{id}
Draft only — always 409 on a non-draft invoice, regardless of which fields the request tries to change (numbers, artifacts, and totals of an issued document are immutable). Same request shape and the same validation as POST /invoices, including the invoiceTypeCode allowlist check (400) and the precedingInvoice/negative-amount checks (422, same as create), with one difference: the rule that a single service date (deliveryDate) and a service period (billingPeriodStart/billingPeriodEnd) are alternatives is judged on the RESULTING invoice — the stored values together with the ones you send — not on the request body alone. A save that would leave both on the invoice, or a body carrying both, is refused with 400 validation_error and one issue { path: deliveryDate, code: service_timing_conflict }; nothing is written and the stored values are unchanged. Remove the one you no longer want. A correction document (invoiceTypeCode 381/384, or one referencing a preceding invoice) may keep, unchanged, a date and a period it inherited from its original.
A replace writes the service period (billingPeriodStart, billingPeriodEnd) like every other mutable field: an omitted bound stores no value, so an omitted billingPeriodEnd leaves the period open-ended and omitting both clears it. An explicit null on deliveryDate, billingPeriodStart or billingPeriodEnd means the same as omitting it. One exception: on an invoice generated from a recurring template (subscriptionId set) the period belongs to the template. Omit it or resend the stored period unchanged (compared as calendar dates) and it is kept; a different period is refused with 400 validation_error and one issue { path: billingPeriodStart, code: billing_period_owned_by_subscription }, and nothing is written. To bill later periods differently, change the recurring template.
Rollout: a deployment that has not yet enabled this ignores billingPeriodStart/billingPeriodEnd on a replace and keeps the stored period. The response always shows the period that was stored; compare it with the one you sent. This note is removed once every deployment writes the period.
- Erforderlicher Scope
-
documents:write - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) |
Request-Body erforderlich
application/json CreateInvoice
Antworten
-
200The updated invoice
application/jsonInvoice -
400Validation error, including an invoiceTypeCode not on the configured allowlist, or an unknown customerId. Also
service_timing_conflict(issue pathdeliveryDate): the invoice after this save would state both a single service date and a service period, or the body carries both (see the operation description). Alsobilling_period_owned_by_subscription(issue pathbillingPeriodStart): the invoice was generated from a recurring template and the request changes its period. Issues may also carry the other codesPOST /invoicesdocuments:reverse_charge_buyer_vat_id_required,vat_id_prefix_unknown,currency_code_unknown,vat_category_unavailable,vat_rate_category_mismatch,vat_exemption_reason_required,vat_exemption_reason_not_allowedandunit_code_unknown, with the same paths.application/jsonValidationError -
404Not found
-
409Not a draft (
not_draft), or the suppliedinvoiceNois already in use by another invoice (invoice_number_conflict).application/jsonValidationError -
422Either an unresolvable
precedingInvoicereference (invoiceTypeCode="384" with a missing/dangling/draft/external reference) or a negative-amount line/total (UStAE 14.3 Abs. 2 Satz 7 — a reduction must be its own corrective document).application/problem+jsonInvalidPrecedingReferenceProblem | SplitRequiredProblem
Delete a draft invoice
DELETE /invoices/{id}
Draft only. An issued/paid/void invoice is immutable (GoBD), so a delete on a non-draft is refused with 409 not_draft — the correction path for an issued document is a credit note (POST /invoices/{id}/credit-note), never a delete. A draft generated by a recurring invoice is refused with 409 subscription_draft_not_deletable while that recurring invoice is active or paused: the next billing run would create the draft again for the same period, so deleting it and billing the period by hand would invoice one supply twice. Edit the draft, or end the recurring invoice first; the body names the recurring invoice (subscriptionId). A draft still referenced by another record is refused with 409 invoice_delete_blocked. Idempotent from the caller's view: a missing invoice is a plain 404.
- Erforderlicher Scope
-
documents:write - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) |
Antworten
-
204Deleted (no content).
-
404Invoice not found. ⚠ Indistinguishable, deliberately, from "an id belonging to a DIFFERENT entity": where row-scoped context resolution is enabled, the id is resolved to its own tenant/entity/mode before the handler runs, and an id this caller may not reach returns the same 404 as an unknown one. Distinguishing the two would leak the existence of another tenant's document.
application/jsonValidationError -
409Refused by the document's current state, nothing deleted.
erroris one of:not_draft— only draft invoices can be deleted;subscription_draft_not_deletable— the draft was generated by a recurring invoice that is still active or paused (the body carriessubscriptionId);invoice_delete_blocked— another record still references the draft (the body carriesreferencedBy, a record type, never its contents).application/jsonValidationError
Download the stored ZUGFeRD PDF for an issued invoice
GET /invoices/{id}/pdf
Streams the persisted artifact (invoices.pdf_key, written at issue time by the compliance-gate orchestrator). 404 whenever it is not ready to serve — draft, pdf_status not 'ready', or the object is unexpectedly missing from storage — the response body does not distinguish which. Readable with an API key carrying documents:read as well as with a dashboard session — this is the call an integration makes to hand the issued document on.
- Erforderlicher Scope
-
documents:read - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) |
Antworten
-
200The PDF/A-3b (ZUGFeRD) file, as
attachment.application/pdf -
404Not found, not ready, or missing from storage
application/jsonValidationError
Download the stored XRechnung 3.0 CII XML for an issued invoice
GET /invoices/{id}/xml
Streams the persisted artifact (invoices.xml_key, written at issue time). 404 whenever it is not ready to serve — draft, xml_status not 'ready', or the object is unexpectedly missing from storage.
- Erforderlicher Scope
-
documents:read - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) |
Antworten
-
200The XRechnung 3.0 CII XML document, as
attachment.application/xml -
404Not found, not ready, or missing from storage
application/jsonValidationError
Render a non-persisted, watermarked WYSIWYG preview PDF for a draft
GET /invoices/{id}/preview.pdf
Draft only (409 otherwise). Rendered on demand by the document renderer — the same template and renderer as the real PDF, so this is byte-faithful to what PATCH /invoices/{id}/finalize will produce. Nothing is stored; a fresh render happens on every call.
- Erforderlicher Scope
-
documents:read - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) |
Antworten
-
200The watermarked preview PDF,
inline.application/pdf -
404Not found
application/jsonValidationError -
409Invoice is not a draft
application/jsonValidationError -
422buyer_address_incomplete— the buyer's postal address (street / postal code / city + a 2-letter country code) is incomplete, so no document can be rendered from this draft.missing[]names the offending request fields. Checked in api BEFORE the renderer is called, so an incomplete address is a clean, field-naming refusal rather than an opaque 5xx. A502/503here means the renderer itself failed or was unreachable, never that the data was wrong.application/jsonValidationError -
502render_failed— the render service rejected the document. -
503renderer_unavailable— the render service is unreachable; retry.
Reset a failed PDF render to pending and retry generation
PATCH /invoices/{id}/retry-pdf
Requires an issued invoice whose pdf_status is 'failed'.
- Erforderlicher Scope
-
documents:write - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) |
Antworten
-
200The invoice with
pdf_statusreset to'pending'; regeneration runs fire-and-forget.application/jsonInvoice -
404Not found
-
409PDF retry requires an issued invoice with
pdf_status = 'failed'application/jsonValidationError
Reset a failed XML render to pending and retry generation
PATCH /invoices/{id}/retry-xml
Requires an issued invoice whose xml_status is 'failed'.
- Erforderlicher Scope
-
documents:write - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) |
Antworten
-
200The invoice with
xml_statusreset to'pending'; regeneration runs fire-and-forget.application/jsonInvoice -
404Not found
-
409XML retry requires an issued invoice with
xml_status = 'failed'application/jsonValidationError
Set or clear the DSGVO Art. 18 processing restriction
PATCH /invoices/{id}/processing-restriction
Document-level DSGVO Art. 18 processing restriction. Setting restricted: true requires a reason; it stamps restrictedAt/ restrictedBy and writes an audit-log entry documenting the conflict. Clearing (restricted: false) resets the reason/actor/timestamp. A restriction is metadata ABOUT the document — it never alters the issued content, and a restricted document still fully archives, exports, and appears in DATEV output (restriction limits processing, not existence). Role-gated owner/admin.
- Erforderlicher Scope
-
documents:write - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) |
Request-Body erforderlich
application/json object
Antworten
-
200The invoice with its updated restriction metadata.
application/jsonInvoice -
400Missing
reasonon a set, or an invalid reason value.application/jsonValidationError -
403Insufficient role (owner/admin required).
-
404Not found
Transition a draft to 'scheduled' with an auto-issuance date
PATCH /invoices/{id}/schedule
A scheduling worker (a separate process) polls for invoices whose scheduledIssuanceDate has arrived and issues them automatically. No number is assigned here — numbering only happens at issue time.
- Erforderlicher Scope
-
documents:write - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) |
Request-Body erforderlich
application/json object
Antworten
-
200The invoice, now
status = 'scheduled'application/jsonInvoice -
400Invalid or missing scheduledIssuanceDate
application/jsonValidationError -
404Not found
-
409Only draft invoices can be scheduled
application/jsonValidationError
Issue a draft (compliance gate). Runs the KoSIT gate synchronously: assigns the invoice number (sticky — reused on a retry after a gate failure), renders + validates both XRechnung and ZUGFeRD in parallel, and either commits (artifacts + report + email outbox) or rejects with actionable findings. the validation gate in report-only mode (rollout default) never blocks — the commit runs regardless of verdict, with `validationState` recording the outcome; the validation gate in enforce mode blocks on any error-severity finding. The invoice date (`issueDate`) is set to the day of issue (Europe/Berlin), whatever date the draft carried.
PATCH /invoices/{id}/finalize
- Erforderlicher Scope
-
documents:write - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) |
Antworten
-
200Issued. Artifacts and a validation report exist; the email outbox is enqueued when a recipient is configured.
section14is the advisory §14 Pflichtangaben summary computed for the issued document —passed/has_warnings/unavailable, neverfailed(§14 is advisory and never gates issuance). The durable §14 result is persisted strictly post-commit; a persist failure is self-healing (recomputed on read) and never affects issuance.application/jsonobject -
402The plan tier does not include live issuance. Nothing was consumed — no invoice number, no document, no metering entry — and the draft is unchanged and still editable. Cleared by an upgrade, never by retrying.
application/jsonEntitlementRequiredError -
404Not found
-
409Not a draft, or issuing new documents is suspended for this account (
issuing_suspended); nothing was issued and no number was used. A correction (credit note or corrected invoice) is never refused for that reason.application/jsonValidationError -
422Gate failed (validation gate enforcing). Draft unchanged (number retained, still editable). Machine-readable findings. A B2G issue blocked solely by target-conditional B2G fields returns the distinct
B2G_FIELD_REQUIREDvariant instead.application/problem+jsonValidationProblem | B2gFieldRequiredProblem -
503The document renderer was unavailable, unreachable, or timed out (15s budget). Retryable — the draft is intact and the sticky number (if already assigned) is unaffected.
application/jsonValidationError
Derive a Stornorechnung (credit note, UNTDID 381) from an issued invoice
POST /invoices/{id}/credit-note
Creates and returns a new DRAFT credit note (invoiceTypeCode="381") that references the given issued/paid invoice via the EN 16931 Preceding Invoice Reference (BG-3). Line items are copied from the original with POSITIVE amounts — a credit note states positive figures; the credit direction is carried by the type code, never by sign — and totals/tax are recomputed server-side. The original invoice is never modified. A full credit is returned; a partial credit is produced by editing the returned draft before issuing. The returned draft carries reversalScope="full" and is titled „Stornorechnung" on the rendered document. Editing it so it no longer covers the whole original flips reversalScope to "partial" and the title to „Rechnungskorrektur", so the printed word always matches what the document actually reverses. invoiceTypeCode stays "381" either way — the scope is a label, not a second document type, and nothing about booking, dunning or the permitted preceding references changes with it. Auth/scope/idempotency are identical to POST /invoices (honors the optional Idempotency-Key header so a retried derive is a replay, not a second draft).
- Erforderlicher Scope
-
documents:write - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) | The issued/paid invoice to credit. |
Idempotency-Key | Header | string | Optional client-supplied key that makes this mutating request safe to retry. The FIRST request runs and its outcome is recorded keyed by Länge: max. 255 |
Antworten
-
201The seeded 381 draft (same shape as POST /invoices; or, on an Idempotency-Key replay, the recorded response).
application/jsonInvoice -
404Source invoice not found in the caller's scope
-
409Idempotency-Key conflict.
IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_BODY— the key was already used with a different request body;REQUEST_IN_PROGRESS— the first request bearing this key is still in flight (retryable).application/jsonobject -
422INVALID_PRECEDING_REFERENCE — the source is not a legal credit-note target (draft/scheduled/void, or a type outside {380,381,384}).
application/problem+jsonInvalidPrecedingReferenceProblem
Dry-run preflight (compliance gate). Draft or scheduled only. Runs the same local preflight + parallel render+validate calls as `PATCH /invoices/{id}/finalize`, but never assigns a number (a placeholder BT-1 is used when the invoice is unnumbered) and never commits — no state change on the invoice, regardless of verdict. The report is stored with `kind=preflight`.
POST /invoices/{id}/validate
- Erforderlicher Scope
-
documents:read - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) |
Antworten
-
200Verdict (pass or fail) and the reports produced by this dry-run, plus the advisory §14 Pflichtangaben summary (
section14) — evaluated pre-issue so a draft's missing Leistungsdatum etc. is visible with no document consumed.section14never blocks: it ispassed/has_warnings/unavailable, neverfailed.application/jsonobject -
404Not found
-
409Invoice is neither draft nor scheduled
application/jsonValidationError -
422Local field-completeness preflight failed (predates the KoSIT gate; unconditional on the validation gate). Nothing was rendered/consumed —
reportIdsis empty. A B2G dry-run blocked solely by target-conditional B2G fields returns the distinctB2G_FIELD_REQUIREDvariant.⚠ Two different bodies share this status, and they are told apart by CONTENT TYPE, not by a field. The compliance verdicts above are
application/problem+jsonand carrycode; an incomplete buyer address is the platform's standard envelope (application/json, discriminatorerror=buyer_address_incomplete, withmissing[]naming the offending request fields) and carries nocodeat all. It is answered BEFORE the renderer is called, so it names the fields in this API's own vocabulary rather than the renderer's.application/problem+jsonValidationProblem | B2gFieldRequiredProblemapplication/jsonValidationError -
503The document renderer was unavailable, unreachable, or timed out.
application/jsonValidationError
Stateless Validation-as-a-service. Runs a submitted invoice through the SAME KoSIT + §14 compliance gate and returns the report — persisting NOTHING (no invoice, no stored report, no numbering, no metering). Requires the `validation:run` scope. Mounted only where standalone validation is enabled (off by default → 404).
POST /validate
Two request shapes, one verdict contract: (a) an invoice BODY (application/json, same shape as POST /invoices) is assembled in-memory (totals computed server-side, never trusted from input) and run through the api's KoSIT + §14 engine; (b) a SUBMITTED structured EN 16931 XML (application/xml) is proxied to zugferd's validate-only endpoint (no §14 — that reads an assembled model, not raw XML). The verdict is the SAME one issuance would gate (same engine) — it can never diverge.
200-with-findings (D-3): a fail verdict — KoSIT, §14, OR the local field-completeness preflight — is ordinary 200 response DATA, never an HTTP error. This NARROWLY diverges from the landed POST /invoices/{id}/validate dry-run, which throws 422 on its local preflight; this endpoint folds even that into the 200 findings. The two 422 boundaries are deliberately NOT reconciled (the dry-run's pre-flight-422 stays for its shipped consumers). 4xx here is reserved for a body that cannot be ASSEMBLED at all (path a) or an unknown formatVersion (path b).
Stateless fence: a submitted document is validated and forgotten — there is no landing zone, no field-extraction into invoices, no inbound receiving. Version is explicit (?profile=/?formatVersion= for path b; the api default for path a) and echoed as profile so the verdict is self-describing.
- Erforderlicher Scope
-
validation:run - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
profile | Query | string | Path (b) only — the XRechnung/EN 16931 profile version to validate the submitted XML against (alias |
Request-Body erforderlich
application/json CreateInvoice
application/xml string (binary)
Antworten
-
200The verdict (
pass/fail) + rule-referenced report(s), the echoedprofile, and (path a only) the advisory §14 Pflichtangaben summary. Afailverdict is a successful evaluation, NOT an error.application/jsonobject -
400The body cannot be assembled at all (path a — bad JSON / schema / customer / preceding reference) or an unknown
formatVersion(path b). NOT afailverdict (that is a200).application/jsonValidationError -
403The caller lacks the
validation:runscope.application/jsonValidationError -
503The document renderer was unavailable, unreachable, or timed out (path b, or path a render).
application/jsonValidationError
List validation report metadata for an invoice: every attempt (issue attempts, successful issuance, preflight dry-runs), verdict, kind, artifact/validator versions, and findings. Byte-exact report XML is not included — see `GET /invoices/{id}/validation-reports/{reportId}`.
GET /invoices/{id}/validation-reports
- Erforderlicher Scope
-
documents:read - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) |
Antworten
-
200Report metadata, oldest attempt first.
application/jsonArray<ValidationReportMeta> -
404Not found
Retrieve a stored Prüfbericht (native KoSIT XML, byte-exact). 404 (not the report) if `reportId` belongs to a different invoice than `id` — no cross-invoice leakage.
GET /invoices/{id}/validation-reports/{reportId}
- Erforderlicher Scope
-
documents:read - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) | |
reportId erforderlich | Pfad | string (uuid) |
Antworten
-
200The stored KoSIT Prüfbericht, byte-exact.
application/xml -
404Not found (including a reportId belonging to a different invoice)
The typed §14 Abs. 4 UStG Pflichtangaben result for an invoice — the clean three-state `Section14Summary` the frontends bind to (the raw `kind='section14'` row also remains in the validation-reports list). For an issued document the stored result is returned (`evaluatedAt` = when it was computed); a lost post-commit persist is transparently recomputed over the immutable issued document and re-persisted (recompute-on-read), so the result is always retrievable. Advisory-only: `status` is `passed`/`has_warnings` (or `unavailable`) — NEVER `failed`; a `not_applicable` item is never counted as a warning. RLS-scoped — a cross-tenant request 404s (indistinguishable from missing). PII-free.
GET /invoices/{id}/section14
- Erforderlicher Scope
-
documents:read - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) |
Antworten
-
200The §14 Pflichtangaben summary for the invoice.
application/jsonSection14Summary -
404Not found — a missing/cross-tenant invoice, or a draft never validated (no §14 result yet; render "nicht geprüft").
Manually (re-)send an issued invoice's delivery email
POST /invoices/{id}/send
The body is OPTIONAL and a request without one behaves exactly as it always has. In ONE transaction append an immutable immutable send record + RE-ARM the single an outbound-mail record of kind invoice (sent_at=NULL, status='pending') so the mail dispatcher re-sends the ALREADY-ISSUED ZUGFeRD PDF. The invoice row is byte-UNTOUCHED (0058 immutability): no render, no re-issue. A deliberate re-send (fresh Idempotency-Key) is allowed and writes a NEW send record + re-arms; a raced double-submit with the same key is a replay of the 201. Refuses (writing nothing) for a non-issued/paid status (409 invoice_not_sendable), no recipient (422 no_recipient_email — buyer email ∅ and the linked customer's email ∅), or no SMTP configured (422 email_settings_not_configured). sendeprotokolle is left untouched. Customer copy recipients are applied by automatic delivery; on this operation copies are sent only when named in the body.
- Erforderlicher Scope
-
documents:write - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) | |
Idempotency-Key | Header | string |
Request-Body optional
application/json object
Antworten
-
201The created send.
application/jsonInvoiceSend -
403Caller lacks the
documents:writescope. -
404invoice_not_found— the invoice is not in scope. -
409invoice_not_sendable— status ∉ {issued, paid}. -
422no_recipient_email(no email on file AND no recipient supplied),email_settings_not_configured(no default SMTP config for the entity/mode),ad_hoc_recipient_invalid(an address is not a single valid address),ad_hoc_copy_cap_exceeded,ad_hoc_recipient_not_enabled(this deployment does not accept a chosen recipient — a body-less send is unaffected), orvalidation_error(the body's shape;details[]listspathandmessage, and an address entry that is too short or too long carriescode: invalid_address), orsandbox_recipient_not_allowed(test mode, sent by the platform's test sender: a recipient — including a copy or blind copy — is neither a confirmed reply address of the company nor the address of an active member of the account; the body names no address). Nothing is written on any of them. Theemail_settings_not_configuredbody NAMES the gap and the fix —mode(which of the two mode-scoped configs is empty),missing: ["email_settings.default"]andremedy: "settings.delivery". The mode is load-bearing: delivery configuration is scoped per entity and mode, and a fresh session defaults to Test, so a tenant who has configured SMTP has very often configured the OTHER mode — which is exactly how a live invoice ends up issued and never mailed. -
429sandbox_mail_daily_cap_reached— test mode, sent by the platform's test sender: this company has reached its daily limit of test-mode e-mails sent by the platform (counted per company over the Europe/Berlin calendar day: e-mails sent today plus e-mails still waiting to be sent). Nothing was written and nothing was sent; a reminder level stays free. The limit resets at midnight Europe/Berlin;Retry-Aftergives the seconds until then andlimitthe number. E-mails sent through the company's own mail server are not counted.-
Retry-AfterintegerSeconds until the next business day starts (Europe/Berlin midnight).
application/jsonobject -
The append-only send history of an invoice
GET /invoices/{id}/sends
Every (re-)send attempt of the invoice as an immutable send record (newest first) — both manual re-sends and the auto_issue delivery at issue. Drives the detail page's "Senden" vs "Erneut senden" label and the "zuletzt gesendet am" line.
- Erforderlicher Scope
-
documents:read - Zugang
- API-Schlüssel oder angemeldete Sitzung
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) |
Antworten
-
200The send history.
application/jsonobject -
403Caller lacks the
documents:readscope. -
404The invoice is not in scope.
Record a dispatch you performed yourself
POST /invoices/{id}/external-send
Records that a named person dispatched this invoice outside this application — by post, in person, or from their own mail client. It sends nothing and changes no document: no message is composed, no delivery is queued and the invoice is untouched.
The record is evidence of the STATEMENT, not of the dispatch. What it establishes is who said it, when they said it, and what they said. It establishes nothing about whether the document arrived.
Available to interactive sessions only. Programmatic credentials are refused, because the substance of the record is that a named person stated something.
- Erforderlicher Scope
-
documents:write - Zugang
- nur mit angemeldeter Sitzung – ein API-Schlüssel wird abgelehnt
(
oidc-only)
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) | |
Idempotency-Key | Header | string | An optional client-generated key. A retry carrying the same key returns the recorded response instead of writing a second time. Länge: max. 255 |
Request-Body erforderlich
application/json object
Antworten
-
201The record was written.
application/jsonInvoiceSendHistoryItem -
403Caller lacks the
documents:writescope, or presents a programmatic credential. -
404The invoice is not in scope, or recording is unavailable in this deployment.
-
409Only an issued or paid invoice can carry a dispatch record.
-
422The stated date is out of range, or the channel is not accepted here.
Withdraw a dispatch record
POST /invoices/{id}/sends/{sendId}/retraction
Withdraws a dispatch record you entered. The original record is never edited and never removed: the withdrawal is written as its own entry beside it, and the history shows both.
Only a record you entered can be withdrawn. A dispatch this application actually performed cannot be — correct a message sent to the wrong address by sending it again to the right one.
- Erforderlicher Scope
-
documents:write - Zugang
- nur mit angemeldeter Sitzung – ein API-Schlüssel wird abgelehnt
(
oidc-only)
Parameter
| Parameter | In | Typ | Beschreibung |
|---|---|---|---|
id erforderlich | Pfad | string (uuid) | |
sendId erforderlich | Pfad | string (uuid) | |
Idempotency-Key | Header | string | An optional client-generated key. A retry carrying the same key returns the recorded response instead of writing a second time. Länge: max. 255 |
Request-Body erforderlich
application/json object
Antworten
-
201The withdrawal was written.
application/jsonInvoiceSendHistoryItem -
403Caller lacks the
documents:writescope, or presents a programmatic credential. -
404No such record on this invoice, or recording is unavailable in this deployment.
-
409The record is not one that can be withdrawn, or it was already withdrawn.
Datenmodelle
Jedes Datenmodell dieser Gruppe einmal, mit allen Feldern – auch den verschachtelten. Verschachtelte Objekte sind aufklappbar; ein benannter Typ verweist auf seinen eigenen Abschnitt.
InvoiceSummary object
| Feld | Typ | Beschreibung |
|---|---|---|
id | string (uuid) | |
invoiceNumber | string | null | Null on a freshly created draft unless an explicit invoiceNo was supplied at creation (numbers are no longer auto-assigned at draft creation — only at issue time, sticky thereafter). Use |
issueDate | string (date) | EN 16931 BT-2, the invoice date. It is always the day the invoice is issued (Europe/Berlin): on a draft it is provisional and reads as the day of the latest save; issuing sets it to the issue day, and a scheduled issuance to the day it runs. Frozen at issue. |
status | string | ⚠ Werte: "draft", "scheduled", "issued", "paid", "void" |
currency | string | ISO 4217 |
buyerName | string | null | |
totalGross | DecimalString | |
validationState | string | null | Compliance-gate outcome. null until a validation attempt has run. 'legacy' = issued before the gate existed (backfilled when the gate was introduced). Werte: "failed", "passed", "legacy" |
correctionState | string | null | The DERIVED correction state, computed on read from the document chain and never stored. Werte: "cancelled", "corrected" |
transmissionState | TransmissionState | |
transmissionProblemReason | TransmissionProblemReason | |
transmissionLastEventAt | string (date-time) | null | When we learned the fact |
mode | Mode |
Verwendet von:
GET /invoices
PaginationMeta object
Pagination metadata returned alongside every paginated list.
| Feld | Typ | Beschreibung |
|---|---|---|
total erforderlich | integer | Total number of records matching the current filter. |
page erforderlich | integer | Current page (1-indexed, echoes the request). |
pageSize erforderlich | integer | Records per page (echoes the request). |
totalPages erforderlich | integer | Total number of pages. Minimum 1, even when total is 0. |
Verwendet von:
GET /invoices
UnknownQueryParameterError object
The request named query parameters this endpoint does not define. A list endpoint refuses them rather than ignoring them, so a caller can never mistake an UNFILTERED collection for a filtered one.
| Feld | Typ | Beschreibung |
|---|---|---|
error erforderlich | string | Werte: "unknown_query_parameter" |
parameters erforderlich | Array<string> | The unrecognised parameter NAMES, deduplicated and sorted. Bounded at ten entries, and a name longer than 64 characters is omitted — a parameter name is caller-supplied text, and this response never reflects unbounded caller input. The array may therefore be EMPTY while the refusal still stands: the refusal is decided by the request, not by the length of this list. |
message | string | |
requestId | string |
Verwendet von:
GET /invoices
ValidationError object
A refused request body. error is validation_error when the body was read and failed validation (each problem is listed in issues), or invalid_json when the body is not valid JSON at all (then issues is absent). An unparsable body is answered 400 invalid_json on every operation that takes a JSON body. The submitted body is never echoed.
| Feld | Typ | Beschreibung | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
error | string | |||||||||||||
issues | Array<object> | Felder
|
Verwendet von:
GET /invoices , POST /invoices , POST /invoices/preview-totals , PUT /invoices/{id} , DELETE /invoices/{id} , GET /invoices/{id}/pdf , GET /invoices/{id}/xml , GET /invoices/{id}/preview.pdf , PATCH /invoices/{id}/retry-pdf , PATCH /invoices/{id}/retry-xml , PATCH /invoices/{id}/processing-restriction , PATCH /invoices/{id}/schedule , PATCH /invoices/{id}/finalize , POST /invoices/{id}/validate , POST /validate
CreateInvoice object
Create payload. NOTE: no totals — the server computes them. Any totals present in the body are ignored.
| Feld | Typ | Beschreibung | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
currency erforderlich | string | ISO 4217 (3 letters) | |||||||||
invoiceTypeCode | string | UNTDID 1001 (BT-3). Defaults to "380" if omitted. Allowlist- checked against | |||||||||
precedingInvoice | object | EN 16931 BG-3 reference. REQUIRED when invoiceTypeCode="384" (correction); a missing, dangling, draft/scheduled, or otherwise unresolvable reference is a 422 Felder
| |||||||||
issueDate | string (date) | Optional, and set by the server: the invoice date is always the day the invoice is issued, so this property is accepted only when it names today (Europe/Berlin). Any other day is refused with a | |||||||||
dueDate | string (date) | null | EN 16931 BT-9 payment due date. Optional; when omitted the render clients fall back to issueDate + 30 days. Frozen at issue by the 0058/0059 immutability trigger. | |||||||||
paymentTerms | string | null | EN 16931 BT-20 free-text payment terms / Zahlungsziel. Optional; frozen at issue. | |||||||||
customerId | string (uuid) | Optional link to a saved customer; buyer is still snapshotted | |||||||||
documentTarget | string | Optional per-invoice override of whether the recipient is a public authority. Omitting it does NOT clear it: the server resolves the linked customer's setting, then this draft's own stored value, so a replace that does not mention it keeps what the document already says. Snapshotted onto the invoice and frozen at issue. Note that the same property behaves differently on the customer replace, where omitting it sets 'b2b'. Werte: "b2b", "b2g" | |||||||||
status | string | Desired initial status; defaults to "draft" when omitted. "issued" is NOT accepted — issued documents are reachable only via the finalize / internal-issue endpoints, which assign the invoice number and generate the artifacts. Werte: "draft", "scheduled" | |||||||||
buyer erforderlich | Party | ||||||||||
seller | Party | Optional; defaults to the configured seller | |||||||||
buyerReference | string | ||||||||||
buyerElectronicAddress | string | EN 16931 BT-49 — the buyer's ELECTRONIC ROUTING address (a Leitweg-ID for B2G, or an email), used when generating XRechnung. Falls back to Länge: max. 200 | |||||||||
title | string | null | Document title / Betreff, rendered above the line items. On a create, OMITTING the field applies the company-wide default title; an explicit Länge: max. 500 | |||||||||
text | string | null | Free introductory body text rendered above the line items. Distinct from Länge: max. 5000 | |||||||||
buyerEmail | string (email) | Recipient email (prefilled from the customer) | |||||||||
notes | string | null | Note / footer text rendered below the line items. On a create, OMITTING the field applies the company-wide default note; an explicit Länge: max. 2000 | |||||||||
invoiceNo | string | Optional custom invoice number, used verbatim (uniqueness checked, counter NOT incremented). For a Dauerrechnung this is where the static, contract-derived Objektnummer goes (UStAE 14.5 Abs. 12), which satisfies §14 Abs. 4 Nr. 4. On a replace this property distinguishes the two cases explicitly: omitting it keeps the stored number, sending an empty string clears it. | |||||||||
billingPeriodStart | string (date) | null | EN 16931 BT-73 — service/billing period start. For a Dauerrechnung this is the Leistungszeitraum "von" satisfying §14 Abs. 4 Nr. 6. Mutually exclusive with A replace writes this field like every other mutable field: omitting it, or sending Rollout: a deployment that has not yet enabled this ignores | |||||||||
billingPeriodEnd | string (date) | null | EN 16931 BT-74 — service/billing period end (Leistungszeitraum "bis"; leave unset for an open-ended continuous supply). Mutually exclusive with A replace writes this field like every other mutable field: omitting it, or sending Rollout: a deployment that has not yet enabled this ignores | |||||||||
deliveryDate | string (date) | null | EN 16931 BT-72 — actual delivery date (single-point Leistungsdatum) satisfying §14 Abs. 4 Nr. 6 for a one-off invoice whose service date differs from the issue date. Mutually exclusive with | |||||||||
isDauerrechnung | boolean | Mark this invoice as a Dauerrechnung (continuous-supply invoice, UStAE 14.5 Abs. 17 self-identifying branch). Orthogonal to | |||||||||
lineItems erforderlich | Array<CreateInvoiceLineItem> | Elemente: min. 1, max. 1000 |
Verwendet von:
POST /invoices , PUT /invoices/{id} , POST /validate
Invoice object
| Feld | Typ | Beschreibung | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
id | string (uuid) | ||||||||||
invoiceNumber | string | null | Null on a freshly created draft unless an explicit invoiceNo was supplied at creation (numbers are no longer auto-assigned at draft creation — only at issue time, sticky thereafter). Use | |||||||||
issueDate | string (date) | EN 16931 BT-2, the invoice date. It is always the day the invoice is issued (Europe/Berlin): on a draft it is provisional and reads as the day of the latest save; issuing sets it to the issue day, and a scheduled issuance to the day it runs. Frozen at issue. | |||||||||
status | string | ⚠ Werte: "draft", "scheduled", "issued", "paid", "void" | |||||||||
currency | string | ISO 4217 | |||||||||
buyerName | string | null | ||||||||||
totalGross | DecimalString | ||||||||||
validationState | string | null | Compliance-gate outcome. null until a validation attempt has run. 'legacy' = issued before the gate existed (backfilled when the gate was introduced). Werte: "failed", "passed", "legacy" | |||||||||
correctionState | string | null | The DERIVED correction state, computed on read from the document chain and never stored. Werte: "cancelled", "corrected" | |||||||||
transmissionState | TransmissionState | ||||||||||
transmissionProblemReason | TransmissionProblemReason | ||||||||||
transmissionLastEventAt | string (date-time) | null | When we learned the fact | |||||||||
mode | Mode | ||||||||||
sendState | InvoiceSendState | What is on record about this invoice having gone out. Present on this read only. | |||||||||
transmission | Transmission | null | What the transport reported about the latest system-performed dispatch of this invoice, and the remedies offered for it. Present on this read only; | |||||||||
capabilities | object | What this deployment currently accepts on this invoice. It rides inside this read so a client can offer only what would be accepted, without a second request. Felder
| |||||||||
dueDate | string (date) | null | EN 16931 BT-9 payment due date. Frozen at issue; render clients fall back to issueDate + 30 days when null. | |||||||||
paymentTerms | string | null | EN 16931 BT-20 payment terms / Zahlungsziel. Frozen at issue. | |||||||||
invoiceTypeCode | string | UNTDID 1001 (BT-3), e.g. "380" (commercial invoice). Write requests are allowlist-checked ( | |||||||||
reversalScope nur lesbar | string | null | Derived, read-only: whether a credit note ( Werte: "full", "partial" | |||||||||
precedingInvoice | PrecedingInvoiceRef | null | EN 16931 BG-3 (BT-25 number / BT-26 issue date). Set only on a correction ( | |||||||||
correctedBy | Array<string (uuid)> | Derived, read-only: ids of correction invoices ( | |||||||||
sourceOfferId | string (uuid) | null | The INTERNAL backlink to the source Angebot this invoice was derived from ( | |||||||||
sourceOffer | SourceOfferRef | null | The reverse projection of | |||||||||
correctionDocuments | Array<CorrectionDocumentRef> | The reverse-link LABEL projection — the ISSUED/paid corrections ( | |||||||||
customerId | string (uuid) | null | Soft link to the source customer; buyer is snapshotted | |||||||||
documentTarget | string | The document-target (Rechnungsintent) snapshotted onto this invoice at creation (override > linked customer > 'b2b'), re-resolved on draft edit, frozen at issue. 'b2b' renders ZUGFeRD (EN 16931) with the German XRechnung CIUS rules (BR-DE-, e.g. BT-10) advisory; 'b2g' renders XRechnung + ZUGFeRD with BR-DE- as hard requirements. Werte: "b2b", "b2g" | |||||||||
seller | Party | ||||||||||
sellerIsKleinunternehmer | boolean | §19 UStG snapshot: whether the seller was a Kleinunternehmer at this invoice's CREATION, captured from the company profile's Kleinunternehmer setting and frozen on the invoice (like | |||||||||
isDauerrechnung | boolean | The Dauerrechnung marker — an ORTHOGONAL invoice-level flag, NOT a new | |||||||||
dauerrechnungState | string | null | DERIVED, read-only standing state. Werte: "active", "superseded" | |||||||||
buyer | Party | ||||||||||
buyerReference | string | null | ||||||||||
buyerEmail | string (email) | null | Snapshot recipient email | |||||||||
notes | string | null | ||||||||||
lineItems | Array<InvoiceLineItem> | ||||||||||
totals | InvoiceTotals | ||||||||||
taxBreakdown | Array<TaxBreakdownEntry> | ||||||||||
issuedAt | string (date-time) | null | Set at issue time. Approximate (from updatedAt) for 'legacy' rows backfilled when the field was introduced; null for drafts. | |||||||||
processingRestricted | boolean | DSGVO Art. 18 processing restriction. When true, the document is flagged restricted — it is still fully archived, exported, and DATEV-included (restriction limits processing, not existence). Set/cleared via | |||||||||
processingRestrictionReason | string | null | Reason for the restriction; null when not restricted. Werte: "article_18_gdpr_request", "legal_dispute", "disputed_accuracy" | |||||||||
restrictedAt | string (date-time) | null | When the restriction was set (denormalized render-copy). | |||||||||
restrictedBy | string (uuid) | null | users.id that set the restriction (denormalized render-copy). | |||||||||
createdAt | string (date-time) | ||||||||||
updatedAt | string (date-time) |
Verwendet von:
POST /invoices , GET /invoices/{id} , PUT /invoices/{id} , PATCH /invoices/{id}/retry-pdf , PATCH /invoices/{id}/retry-xml , PATCH /invoices/{id}/processing-restriction , PATCH /invoices/{id}/schedule , POST /invoices/{id}/credit-note
InvalidPrecedingReferenceProblem object
POST /invoices / PUT /invoices/{id} with invoiceTypeCode="384" and a precedingInvoice that is missing, dangling, points at a draft/scheduled invoice, or is otherwise unresolvable in-system (external originals are rejected in v1).
| Feld | Typ | Beschreibung |
|---|---|---|
type | string | |
title | string | |
status | integer | |
detail | string | |
code erforderlich | string | Werte: "INVALID_PRECEDING_REFERENCE" |
Verwendet von:
POST /invoices , PUT /invoices/{id} , POST /invoices/{id}/credit-note
SplitRequiredProblem object
POST /invoices / PUT /invoices/{id} with a non-positive line quantity, a negative line unitPriceNet, or negative document totals (UStAE 14.3 Abs. 2 Satz 7: a reduction must be its own corrective document, not a negative amount mixed into this one). detail cites the paragraph verbatim.
| Feld | Typ | Beschreibung |
|---|---|---|
type | string | |
title | string | |
status | integer | |
detail | string | |
code erforderlich | string | Werte: "SPLIT_REQUIRED" |
Verwendet von:
POST /invoices , POST /invoices/preview-totals , PUT /invoices/{id}
PreviewTotalsRequest object
The line items to price. Nothing else is accepted: no buyer, no currency, no dates, no document type — and, deliberately, no tax-treatment flag (Kleinunternehmer status is read from your company profile, never from the request). Any totals present in the body are ignored; the server computes them.
| Feld | Typ | Beschreibung |
|---|---|---|
lineItems erforderlich | Array<CreateInvoiceLineItem> | Elemente: min. 1, max. 1000 |
Verwendet von:
POST /invoices/preview-totals
PreviewTotals object
The totals an invoice with these line items would get — identical to what POST /invoices computes for the same line items. Nothing was created.
| Feld | Typ | Beschreibung | ||||||
|---|---|---|---|---|---|---|---|---|
totals erforderlich | InvoiceTotals | |||||||
taxBreakdown erforderlich | Array<TaxBreakdownEntry> | |||||||
lines erforderlich | Array<object> | Per-line net amounts, in the same order as the submitted Felder
|
Verwendet von:
POST /invoices/preview-totals
DecimalString string
A fixed-precision decimal as a string, e.g. "9542.50". Read shapes deliberately publish no scale: a document issued before the request scales below were introduced may carry more decimals than a new request may send, and such a document is served from the archive rather than re-created.
Beispiel: "9542.50"
Verwendet von:
PATCH /invoices/{id}/finalize
TransmissionState string | null
What the TRANSPORT reported about this document's latest system-performed dispatch. A separate axis from sendState (who says it was sent) and from the sender kind (whose infrastructure carried it), and the three are never merged.
transmitted is the highest claim this rail can ever make: a receiving mail system ACCEPTED the bytes. There is no delivered value and there will never be one — SMTP cannot report that a message reached a person, and a value that claimed it would be a fabricated fact on a retained document trail. A future rail that genuinely carries a delivery confirmation gets its own, different value.
failed and undeliverable are different facts and are never merged: failed means nothing was ever handed over, undeliverable means it was and it came back. null means this document has no system-performed dispatch at all — a hand-recorded dispatch, a draft, or a document that was never mailable. It never means "unknown" and must not be rendered as a problem.
- Werte: "not_sent", "queued", "held", "transmitted", "delayed", "undeliverable", "failed"
Verwendet von:
PATCH /invoices/{id}/finalize
TransmissionProblemReason string | null
Why a document is in a delayed, undeliverable or failed state. null in every other state. ⚠ It carries a CLASS and never the report's own diagnostic text: that text is a fragment of third-party correspondence about somebody else's invoice and is neither stored nor published anywhere in this platform.
- Werte: "hard_bounce", "soft_bounce", "smtp_rejected", "attempts_exhausted"
Verwendet von:
PATCH /invoices/{id}/finalize
Mode string
Mode echo ("unmissable live/test indicator"): the row's own live/test mode, echoed on every scoped-resource representation so a live vs. test record is unmissable. Read from the record's mode column (never re-derived from the request credential); live for legacy rows created before mode existed.
- Werte: "live", "test"
Beispiel: "live"
Verwendet von:
PATCH /invoices/{id}/finalize
InvoiceSendState object
What this application knows about the invoice leaving the building. It is a class rather than a bare date, because a date alone cannot say whether we dispatched the document or somebody told us they had.
| Feld | Typ | Beschreibung |
|---|---|---|
class erforderlich | string | Werte: "none", "system_transmitted", "user_asserted" |
recordedAt | string (date-time) | null | When we wrote the record down. Null when there is no record. |
assertedOn | string (date) | null | The date stated by the person who recorded it. Present only for |
Verwendet von:
PATCH /invoices/{id}/finalize
Transmission object
What the transport reported about one document's latest system-performed dispatch, with the remedies this API offers for it. Present on the single-invoice read; null when there is no system-performed dispatch to report on.
| Feld | Typ | Beschreibung | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
state erforderlich | TransmissionState | |||||||||||||
heldReason erforderlich | TransmissionHeldReason | |||||||||||||
problemReason erforderlich | TransmissionProblemReason | |||||||||||||
lastEventAt erforderlich | string (date-time) | null | |||||||||||||
sender erforderlich | object | null | The sender identity the message actually went out under, read from the send record's own snapshot. It is never re-derived from current settings: a sender identity can be changed, and re-reading it would show today's sender beside yesterday's message. Felder
| ||||||||||||
recipientSuppressed erforderlich | boolean | The recipient address is currently on this account's suppression list, so no further mail is sent to it until the address is changed. | ||||||||||||
actions erforderlich | Array<string> | The remedies this API offers for this state, in the order they should be presented. The list is decided by the server and a client renders it as offered — a client that decides for itself whether a re-send is possible will eventually offer one the server refuses. |
Verwendet von:
PATCH /invoices/{id}/finalize
PrecedingInvoiceRef object
EN 16931 BG-3 snapshot (BT-25 number / BT-26 issue date) — the original invoice a correction (invoiceTypeCode="384") restates.
| Feld | Typ | Beschreibung |
|---|---|---|
id erforderlich | string (uuid) | |
invoiceNumber erforderlich | string | |
issueDate erforderlich | string (date) |
Verwendet von:
PATCH /invoices/{id}/finalize
SourceOfferRef object
The source Angebot an invoice was derived from via POST /offers/{id}/convert-to-invoice — id + number, for the invoice-detail "Entstanden aus Angebot AN-…" reference line. NOT an EN 16931 preceding-invoice reference (a quote is not an invoice).
| Feld | Typ | Beschreibung |
|---|---|---|
id erforderlich | string (uuid) | |
offerNumber erforderlich | string | Angebotsnummer (e.g. AN-00042) |
Verwendet von:
PATCH /invoices/{id}/finalize
CorrectionDocumentRef object
One issued correction / credit-note that references an invoice as its BG-3 preceding reference — number + UNTDID 1001 type code + issue date, so a client can label the reverse link ("storniert durch (381)" vs "korrigiert durch (384)").
| Feld | Typ | Beschreibung |
|---|---|---|
id erforderlich | string (uuid) | |
invoiceNumber erforderlich | string | |
invoiceTypeCode erforderlich | string | UNTDID 1001 |
reversalScope erforderlich nur lesbar | string | null | The referencing document's own reversal scope, so a client can label this reverse link with the same word that document carries on itself („Stornorechnung" when full, „Rechnungskorrektur" when partial). Werte: "full", "partial" |
issueDate erforderlich | string (date) |
Verwendet von:
PATCH /invoices/{id}/finalize
Party object
Seller or buyer (EN 16931 BG-4 / BG-7, simplified).
| Feld | Typ | Beschreibung |
|---|---|---|
name erforderlich | string | |
addressLine1 | string | null | |
postalCode | string | null | |
city | string | null | |
countryCode | string | null | ISO 3166-1 alpha-2 |
vatId | string | null | USt-IdNr (EN 16931 BT-31 / BT-48) |
taxNumber | string | null | Steuernummer (seller snapshot on invoices) |
Verwendet von:
PATCH /invoices/{id}/finalize
InvoiceLineItem object
A line (EN 16931 BG-25, simplified).
| Feld | Typ | Beschreibung |
|---|---|---|
id erforderlich | string (uuid) | |
description erforderlich | string | |
quantity erforderlich | DecimalString | |
unitCode erforderlich | string | EN 16931 BT-130 — the unit of measure of the invoiced quantity, from the UN/ECE Recommendation 20 code list with the Recommendation 21 extension (rule BR-CL-23). E.g. C62 (piece), HUR (hour), KGM. NO PATTERN IS PUBLISHED ON THIS READ, deliberately: documents issued before the input boundary existed may carry a value outside the code list, and those documents are immutable and are streamed from the archive rather than re-rendered. Publishing a pattern here would make our own historical responses fail a client's schema validation. |
unitPriceNet erforderlich | DecimalString | |
lineNetAmount erforderlich | DecimalString | Computed = quantity × unitPriceNet |
taxRatePercent erforderlich | DecimalString | |
itemDescription | string | null | EN 16931 BT-154 — optional longer item description, distinct from the item name ( |
vatCategory | string | EN 16931 BT-118 VAT category. Werte: "S", "Z", "E", "AE", "K", "G" Standard: "S" |
vatExemptionReasonCode | string | null | EN 16931 BT-121 VATEX exemption reason code (late-binding / StB-gated). |
vatExemptionReasonText | string | null | EN 16931 BT-120 exemption reason free-text. |
Verwendet von:
PATCH /invoices/{id}/finalize
InvoiceTotals object
Document totals (EN 16931 BG-22, simplified).
| Feld | Typ | Beschreibung |
|---|---|---|
totalNet | DecimalString | |
totalTax | DecimalString | |
totalGross | DecimalString |
Verwendet von:
PATCH /invoices/{id}/finalize
TaxBreakdownEntry object
One VAT group (EN 16931 BG-23, simplified).
| Feld | Typ | Beschreibung |
|---|---|---|
taxRatePercent | DecimalString | |
taxableAmount | DecimalString | |
taxAmount | DecimalString |
Verwendet von:
PATCH /invoices/{id}/finalize
ValidationSummary object
| Feld | Typ | Beschreibung |
|---|---|---|
verdict erforderlich | string | Werte: "pass", "fail" |
reports erforderlich | Array<ValidationReportMeta> |
Verwendet von:
PATCH /invoices/{id}/finalize
Section14Summary object
The advisory §14 Abs. 4 UStG Pflichtangaben result — the three-state UI contract. Embedded inline under section14 in the POST /invoices/{id}/validate dry-run response (and the finalize/issue response) and returned by GET /invoices/{id}/section14. status is passed/has_warnings for a computed result (NEVER failed — the §14 layer never fails a document), or unavailable when the §14 layer degraded (a guard that can never affect issuance/validation).
| Feld | Typ | Beschreibung |
|---|---|---|
status erforderlich | string | Werte: "passed", "has_warnings", "unavailable" |
rulesetVersion erforderlich | string | The versioned Section14Ruleset the result was evaluated under. |
evaluatedAt erforderlich | string (date-time) | |
items erforderlich | Array<Section14Item> |
Verwendet von:
PATCH /invoices/{id}/finalize , POST /invoices/{id}/validate , POST /validate , GET /invoices/{id}/section14
ValidationFinding object
| Feld | Typ | Beschreibung |
|---|---|---|
ruleId erforderlich | string | |
severity erforderlich | string | Werte: "error", "warning" |
message erforderlich | string | |
location | string | XPath where provided by the validator |
Verwendet von:
PATCH /invoices/{id}/finalize , POST /invoices/{id}/validate , POST /validate
EntitlementRequiredError object
402 body when the tenant's plan tier does not include the capability the request needs. Cleared by an upgrade, never by the caller — which is what distinguishes it from entity_limit_reached (409), a conflict with current state that the same caller clears by deactivating an entity. Nothing already issued is affected: an entitlement change never touches a document, never touches the archive and never revokes a read.
| Feld | Typ | Beschreibung |
|---|---|---|
error erforderlich | string | Werte: "entitlement_required" |
message | string | |
capability erforderlich | string | The capability the current plan does not include. Werte: "live_issuance", "live_api_keys" |
planTier erforderlich | string | Werte: "launch", "entry", "platform", "sovereign" |
upgradeUrl | string | Relative dashboard hint to the plan surface. |
Verwendet von:
PATCH /invoices/{id}/finalize
ValidationProblem object
| Feld | Typ | Beschreibung |
|---|---|---|
type | string | |
title | string | |
status | integer | |
detail | string | |
code erforderlich | string | Werte: "VALIDATION_FAILED" |
findings erforderlich | Array<ValidationFinding> | |
reportIds erforderlich | Array<string (uuid)> | Ids of the persisted issue-attempt validation reports. Empty when the local preflight blocked before any KoSIT call ("fast 422, nothing consumed"). |
Verwendet von:
PATCH /invoices/{id}/finalize , POST /invoices/{id}/validate
B2gFieldRequiredProblem object
A B2G (Behörde / XRechnung) issue or dry-run blocked SOLELY by target-conditional B2G fields (e.g. BT-10 buyer reference). A DISTINCT, stable code so the client can offer the two remedies — fill the field, or switch the invoice's target to B2B (the same fields are advisory for B2B). Emitted only when EVERY blocking finding is a B2G-conditional rule; a mixed EN-core/KoSIT failure stays VALIDATION_FAILED.
| Feld | Typ | Beschreibung |
|---|---|---|
type | string | |
title | string | |
status | integer | |
detail | string | |
code erforderlich | string | Werte: "B2G_FIELD_REQUIRED" |
documentTarget erforderlich | string | Werte: "b2g" |
missing erforderlich | Array<string> | The BT/field tokens the B2G document is missing. |
remedies erforderlich | Array<string> | |
findings erforderlich | Array<ValidationFinding> | |
reportIds erforderlich | Array<string (uuid)> |
Verwendet von:
PATCH /invoices/{id}/finalize , POST /invoices/{id}/validate
ValidationReportMeta object
| Feld | Typ | Beschreibung |
|---|---|---|
id | string (uuid) | |
format | string | Werte: "xrechnung-cii", "xrechnung-ubl", "zugferd-cii", "section14" |
formatVersion | string | |
artifactSetId | string | |
validatorName | string | |
validatorVersion | string | |
verdict | string | Werte: "pass", "fail" |
kind | string | Werte: "issue", "issue_attempt", "preflight", "section14" |
findings | Array<ValidationFinding> | |
validatedAt | string (date-time) |
Verwendet von:
POST /invoices/{id}/validate , POST /validate , GET /invoices/{id}/validation-reports
InvoiceSend object
One invoice send — an append-only fact recorded per (re-)send attempt. The POST /invoices/{id}/send response carries trigger:manual; GET /invoices/{id}/sends also lists auto_issue rows.
| Feld | Typ | Beschreibung |
|---|---|---|
id erforderlich | string (uuid) | |
invoiceId erforderlich | string (uuid) | |
invoiceNumber erforderlich | string | null | |
toEmail erforderlich | string | The recipient at send time. |
subject erforderlich | string | The api-rendered email subject. |
bodyHtml erforderlich | string | The api-rendered email body (HTML). |
trigger erforderlich | string | What triggered the send (a manual re-send, or auto-delivery at issue). Werte: "manual", "auto_issue" |
createdAt erforderlich | string (date-time) |
Verwendet von:
POST /invoices/{id}/send
InvoiceSendHistoryItem object
One entry in an invoice's send history. The list carries two kinds of entry and evidence is what tells them apart: a dispatch this application performed, and a dispatch a named person recorded having performed themselves. A withdrawal of a record is a third entry referencing the one it withdraws.
| Feld | Typ | Beschreibung | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
id erforderlich | string (uuid) | |||||||||||||
invoiceId erforderlich | string (uuid) | |||||||||||||
trigger erforderlich | string | Werte: "manual", "auto_issue" | ||||||||||||
evidence erforderlich | string |
Werte: "system_transmitted", "user_asserted" | ||||||||||||
channel | string | null | The medium. Absent on a withdrawal, which names none. Werte: "email", "post", "handover", "external_email", "other" | ||||||||||||
toEmail | string | null | The recipient at send time. Absent on a record and on a withdrawal. | ||||||||||||
cc | Array<string> | null | |||||||||||||
bcc | Array<string> | null | Blind-copy recipients, recorded permanently for the document's retention period. | ||||||||||||
sentOn | string (date) | null | The date the person states they dispatched it. Present only on a record, and always alongside | ||||||||||||
note | string | null | Länge: max. 280 | ||||||||||||
reversesSendId | string (uuid) | null | Set on a withdrawal; names the entry being withdrawn. | ||||||||||||
retractionReason | string | null | Werte: "wrong_invoice", "not_actually_sent", "wrong_date", "duplicate_entry", "other" | ||||||||||||
retractedAt | string (date-time) | null | When this entry was withdrawn. Absent while it stands. | ||||||||||||
recordedBy erforderlich | object | Who the entry belongs to, resolved when the history is read rather than stored — so a corrected e-mail address repairs every historical entry at once. Felder
| ||||||||||||
createdAt erforderlich | string (date-time) | When this application wrote the entry down. |
Verwendet von:
GET /invoices/{id}/sends , POST /invoices/{id}/external-send , POST /invoices/{id}/sends/{sendId}/retraction
CreateInvoiceLineItem object
| Feld | Typ | Beschreibung |
|---|---|---|
description erforderlich | string | |
quantity erforderlich | QuantityString | |
unitCode erforderlich | string | EN 16931 BT-130 — the unit of measure of the invoiced quantity. This is a CODED element: the value must come from the UN/ECE Recommendation 20 code list with the Recommendation 21 extension, as enforced by rule BR-CL-23, and NOT from your own vocabulary. Codes are UPPERCASE and are never corrected for you — "hur" is not "HUR". Common values: C62 (piece), HUR (hour), DAY, KGM (kilogram), MTR (metre), LTR (litre), MTK (square metre), KWH. The full accepted set is much wider than these examples; anything matching the published pattern and present in the code list is accepted. ANNOUNCED NARROWING: this endpoint currently ACCEPTS values outside the code list and records them, so an existing integration keeps working. Refusal is a separate, later change. Send a listed code now; the pattern above is what will be enforced. Länge: min. 2, max. 3 Muster: ^[A-Z0-9]{2,3}$ |
unitPriceNet erforderlich | MoneyString | |
taxRatePercent erforderlich | PercentString | |
itemDescription | string | EN 16931 BT-154 — optional longer item description, distinct from the item name ( |
vatCategory | string | EN 16931 BT-118. Increment 1 accepts only Werte: "S" |
Verwendet von:
POST /invoices , POST /invoices/preview-totals , PUT /invoices/{id} , POST /validate
TransmissionHeldReason string | null
Why the queue is holding this message back. Set only in the held state, null otherwise. A held message consumes no delivery attempt and is re-tried automatically once the condition clears.
- Werte: "tenant_paused", "entity_cap", "domain_cap", "recipient_suppressed"
Verwendet von:
POST /invoices , GET /invoices/{id} , PUT /invoices/{id} , PATCH /invoices/{id}/retry-pdf , PATCH /invoices/{id}/retry-xml , PATCH /invoices/{id}/processing-restriction , PATCH /invoices/{id}/schedule , PATCH /invoices/{id}/finalize , POST /invoices/{id}/credit-note
Section14Item object
One row of the §14 Abs. 4 UStG Pflichtangaben checklist. Advisory only — severity is warning at most, NEVER an error/blocking outcome. A not_applicable item is legitimately excused by an exemption (records exemptionRule) and is unambiguously distinct from missing, so it is never mistaken for — nor counted as — a warning. PII-free by construction (per-Nr code/status/rule/label only, never invoice contents).
| Feld | Typ | Beschreibung |
|---|---|---|
code erforderlich | string | Stable machine code (primary signal), e.g. |
nr erforderlich | integer | The §14 Abs. 4 Satz 1 Nr. (1–8) this item checks. Bereich: ≥ 1, ≤ 8 |
label erforderlich | string | Static German label for the Nr. (no invoice PII). |
status erforderlich | string | Werte: "present", "missing", "not_applicable" |
severity erforderlich | string | null |
Werte: "warning" |
exemptionRule erforderlich | string | null | The statute that excused a |
message erforderlich | string | The evaluator's per-Nr human hint (secondary to |
Verwendet von:
PATCH /invoices/{id}/finalize , POST /invoices/{id}/validate , POST /validate , GET /invoices/{id}/section14
QuantityString string
An invoiced quantity as a string, with at most four decimal places. Unlike a price, a quantity is not rounded before use — the line total is price × quantity, rounded once — so a fractional quantity such as "0.25" is ordinary and carries a wider scale than a monetary amount.
- Muster: ^\d+(\.\d{1,4})?$
Beispiel: "0.25"
Verwendet von:
POST /invoices , POST /invoices/preview-totals , PUT /invoices/{id} , POST /validate
MoneyString string
A monetary amount as a string, with at most two decimal places — the scale amounts are billed in. A finer value is refused rather than rounded: rounding it would change the number the caller sent, and because a unit price is rounded to whole cents before being multiplied by the quantity, that change is then multiplied too. A price of "12.999" at quantity 1000 billed 13000.00 against an honest 12999.00; "0.005" at the same quantity billed double.
- Muster: ^\d+(\.\d{1,2})?$
Beispiel: "9542.50"
Verwendet von:
POST /invoices , POST /invoices/preview-totals , PUT /invoices/{id} , POST /validate
PercentString string
A VAT rate as a string, with at most one decimal place — the scale the rendered document can carry. One decimal covers every German rate in use: the standard rates and the agricultural and forestry averages. A finer rate is refused rather than printed, because the document would then state a rate that does not produce the tax amount printed beside it.
- Muster: ^\d+(\.\d{1})?$
Beispiel: "19"
Verwendet von:
POST /invoices , POST /invoices/preview-totals , PUT /invoices/{id} , POST /validate
Nach oben · Alle Ressourcengruppen · OpenAPI-Spezifikation (YAML)