Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.openfiskal.com/llms.txt

Use this file to discover all available pages before exploring further.

Every error response uses the same envelope. Switch on code (machine-readable, stable), not on message (human-readable, may change).

Response shape

{
  "code": "precondition_failed",
  "message": "Resource version mismatch. Expected 1, current is 2.",
  "retryable": true
}
FieldTypeNotes
codestringStable machine-readable identifier. See the catalog below.
messagestringShort human-readable label. Safe to log, not locale-stable, not a stable contract.
retryablebooleanWhether retrying the same request can succeed. 5xx, 429, and 412 are retryable.
detailsobjectReserved for structured context. Not currently populated by any handler — variable state (e.g. expected vs. current resource versions) is interpolated into message today. Treat the field as optional and absent in current responses.
The trace identifier is returned only in the X-Request-Id response header (e.g. X-Request-Id: req_a1b2c3d4e5f60718293a). It is not embedded in the error body. Persist it in your client logs and attach it to support cases.

Retry guidance

ClassRetry?How
5xx, rate_limit_exceeded, precondition_failedYesResend the request. For precondition_failed, re-read the resource to refresh ETag first. For rate_limit_exceeded, honor Retry-After.
All other 4xxNoFix the request before resending.
The retryable field on each response reflects this rule: it is true for any 5xx, 429, or 412, and false otherwise.

Standard error codes

These codes map directly to HTTP status codes and apply to every endpoint across the Fiscalization, Reporting, and Hosted Receipts APIs unless noted.
HTTPcodeRetryableWhen
400invalid_requestNoRequest body, query, or path is malformed or fails schema validation. Includes missing required fields, wrong types, decimal vs. integer mismatches, invalid ISO 3166-1 alpha-3 country codes (DEU not DE), unknown discriminator values (e.g. unknown operation type), and a malformed If-Match header value (must be a quoted positive integer, e.g. "1").
401unauthorizedNoMissing or malformed Authorization header, invalid API key, or API key environment mismatch (sandbox key against production or vice versa).
403forbiddenNoAuthenticated caller is not allowed to access the requested tenant, country, or merchant. Most commonly: API key country does not match the merchant or location country.
404not_foundNoResource does not exist or is not visible to the caller.
409conflictNoGeneric resource conflict. Specific 409 subtypes are listed below — code distinguishes the cause.
412precondition_failedYesStale If-Match on a mutation. The expected and current resource versions appear in message (e.g. "Resource version mismatch. Expected 1, current is 2."). Re-read the resource and retry with the current ETag.
422validation_errorNoRequest shape is valid but a field value is semantically wrong (amount sign mismatch, missing register reference, invalid timestamp range, etc.). See Validation errors for the specific conditions.
428precondition_requiredNoIf-Match header is missing on a mutation that requires it (PATCH /operations/{id}/complete, PATCH /operations/{id}/void).
429rate_limit_exceededYesPer-API-key rate budget exceeded. Honor Retry-After and reduce concurrency.
500internal_errorYesUnhandled server error. Retry the request; escalate with X-Request-Id if sustained. Status-501 paths (see Not-yet-implemented endpoints) currently surface with this code as well.

Conflict (409) variants

All 409 responses share the envelope. The code field distinguishes the cause.
codeWhereWhen
location_has_registersDELETE /locations/{id}Location still has registers attached. Delete or reassign the registers first.
register_has_dependenciesDELETE /registers/{id}Register has operations recorded against it, or the register is fiscalized. Decommission first if applicable; otherwise the register cannot be removed.
register_already_fiscalizedPOST /registers/{id}/fiscalizeRegister is already fiscalized, or a fiscalization is in progress.
register_invalid_fiscal_statePOST /registers/{id}/decommission, completion pathsRegister is decommissioned, not yet fiscalized, or otherwise not in a state that supports the requested action.
register_no_open_sessionPOS operation endpointsRegister has no open session. Open a session first via POST /operations with type: SESSION_OPEN.
session_invalid_stateSession lifecycle endpointsSession-level state violation: a session is already open on the register, session counts or amounts are invalid, or the session cannot be closed in its current state.
operation_invalid_statePATCH /operations/{id}/complete, PATCH /operations/{id}/voidOperation is not in OPEN state. You cannot complete an already-completed or voided operation, and you cannot void what is not open.

Validation errors (422)

422 responses currently emit code: "validation_error" for every case. The message field describes the specific violation.

Operation shape

ConditionOperation types
ONLINE operations must not specify a register_id.SALE, RETURN, EXCHANGE
POS operations must specify a register_id.SALE, RETURN, EXCHANGE
SALE operations must not specify external_related_operation or related_operation_id.SALE
RETURN and EXCHANGE operations must specify exactly one of related_operation_id or external_related_operation.RETURN, EXCHANGE

Amount and payment validation

ConditionOperation types
SALE total_amount must be ≥ 0; RETURN total_amount must be ≤ 0.SALE, RETURN
Each line_items[].total_amount must follow the same sign rule as the parent operation.SALE, RETURN
Each payments[].amount must follow the same sign rule as the parent operation.All goods-movement types
Sum of payments[].amount must match the operation total_amount.All goods-movement types on complete

Fiscalization preconditions

ConditionEndpoint
Register is not fiscalized. Complete fiscalization before completing operations.PATCH /operations/{id}/complete
Register is missing active TSS or Client. Re-fiscalize the register.PATCH /operations/{id}/complete
Operation has no goods-movement details; cannot complete.PATCH /operations/{id}/complete
POS operation is missing a register reference.PATCH /operations/{id}/complete
Merchant is missing a German fiscal identity (DEU).POST /registers/{id}/fiscalize
Fiscalization is only supported for locations in Germany (DEU).POST /registers/{id}/fiscalize
Decommission is only supported for KassenSichV registers.POST /registers/{id}/decommission
Register has no active TSS or client to decommission.POST /registers/{id}/decommission
Active TSS is missing an admin PIN; cannot authenticate to decommission.POST /registers/{id}/decommission
TSE signing failed (phase 1) — TSS unreachable or rejected the request before the operation reached the device.PATCH /operations/{id}/complete
TSE signing failed (phase 2) — TSS rejected the operation after start. The operation is left in a recoverable state.PATCH /operations/{id}/complete

Reporting API

ConditionEndpoint
from and to must be valid ISO-8601 timestamps.Reporting export endpoints (returns 400 invalid_request).
from must be strictly before to.Reporting export endpoints.

Not-yet-implemented endpoints

Two paths return HTTP 501 Not Implemented today. The exception filter has no 501 mapping, so the body surfaces with code: "internal_error" and retryable: true even though the status itself signals the cause:
HTTPWhereWhy
501POST /operations with type: SESSION_CASH_COUNTThe session cash-count operation type is reserved in the schema but not yet handled.
501Reporting export endpoints with an unsupported export typeNo exporter is registered for the requested type (e.g. DSFinV-K archive compilation).
Treat the 501 status — not the body code — as the signal here. This will tighten in a future filter update.

Reserved codes (not yet emitted)

The OpenAPI contract advertises these codes on relevant endpoints, but the runtime does not emit them yet. They are kept here so client switch statements can include them ahead of enforcement landing.
codeHTTPStatus
idempotency_key_conflict409Documented on every mutating operation endpoint. Idempotency-Key replay is not enforced server-side today; reusing a key currently re-runs the request. Treat the documented behavior as forward-looking.
regime_validation_failed422Documented for regime-specific rule failures (KassenSichV / RKSV / Fattura). Current handlers throw UnprocessableEntityException without a custom code, so these responses surface as validation_error.

Idempotency and concurrency

Idempotency keys, retention windows, and concurrency rules (If-Match / ETag) are documented on the Limits page. The errors those rules emit (precondition_required, precondition_failed) are in the standard table above; idempotency_key_conflict is forward-looking — see Reserved codes.

Request tracing

Every response includes an X-Request-Id header. Persist it in your client logs and attach it to every support case — it is the fastest path to a root-cause answer.

Next steps