code (machine-readable, stable), not on message (human-readable, may change).
Response shape
| Field | Type | Notes |
|---|---|---|
code | string | Stable machine-readable identifier. See the catalog below. |
message | string | Short human-readable label. Safe to log, not locale-stable, not a stable contract. |
retryable | boolean | Whether retrying the same request can succeed. 5xx, 429, and 412 are retryable. |
details | object | Optional structured context. precondition_failed (412) responses include expected_resource_version and current_resource_version here so clients can react without parsing message. Other endpoints may add their own structured payload (for example, export_dsfinvk_build_pending returns unbuilt_session_ids). Always check whether the field is present before reading it. |
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
| Class | Retry? | How |
|---|---|---|
5xx, rate_limit_exceeded, precondition_failed | Yes | Resend the request. For precondition_failed, re-read the resource to refresh ETag first. For rate_limit_exceeded, honor Retry-After. |
All other 4xx | No | Fix the request before resending. |
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.| HTTP | code | Retryable | When |
|---|---|---|---|
| 400 | invalid_request | No | Request 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"). |
| 401 | unauthorized | No | Missing or malformed Authorization header, invalid API key, or API key environment mismatch (sandbox key against production or vice versa). |
| 403 | forbidden | No | Authenticated 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. |
| 404 | not_found | No | Resource does not exist or is not visible to the caller. |
| 409 | conflict | No | Generic resource conflict. Specific 409 subtypes are listed below — code distinguishes the cause. |
| 412 | precondition_failed | Yes | Stale If-Match on a mutation. Both the expected and current resource versions appear in details (expected_resource_version, current_resource_version) and in message. Re-read the resource and retry with the current ETag. |
| 422 | validation_error | No | Request 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. |
| 428 | precondition_required | No | If-Match header is missing on a mutation that requires it (POST /operations/{id}/complete, POST /operations/{id}/void). |
| 429 | rate_limit_exceeded | Yes | Per-API-key rate budget exceeded. Honor Retry-After and reduce concurrency. |
| 500 | internal_error | Yes | Unhandled server error. Retry the request; escalate with X-Request-Id if sustained. |
| 501 | not_implemented | Yes | The requested capability is registered in the schema but no handler is wired up. Today this only happens if a request lands for an export type with no matching exporter. |
Conflict (409) variants
All 409 responses share the envelope. The code field distinguishes the cause.
code | Where | When |
|---|---|---|
location_has_registers | DELETE /locations/{id} | Location still has registers attached. Delete or reassign the registers first. |
register_has_dependencies | DELETE /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_fiscalized | POST /registers/{id}/fiscalize | Register is already fiscalized, or a fiscalization is in progress. |
register_invalid_fiscal_state | POST /registers/{id}/decommission | Register is already decommissioned, or has not been fiscalized yet. Other decommission preconditions (non-KassenSichV regime, missing TSS/client metadata) surface as validation_error with 422. |
register_no_open_session | POS operation endpoints | Register has no open session. Open a session first via POST /operations with type: SESSION_OPEN. |
session_invalid_state | Session lifecycle endpoints | Session-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_state | POST /operations/{id}/complete, POST /operations/{id}/void | Operation 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
| Condition | Operation 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
| Condition | Operation 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 |
Decimal precision
| HTTP | code | When |
|---|---|---|
| 422 | tax_amount_precision_invalid | A line_items[].taxes[].tax_amount doesn’t match total_amount × rate_i / (1 + Σ rate_j) at 8 dp. See Decimal precision. |
Fiscalization preconditions
| Condition | Endpoint |
|---|---|
| Register is not fiscalized. Complete fiscalization before completing operations. | POST /operations/{id}/complete |
| Register is missing active TSS or Client. Re-fiscalize the register. | POST /operations/{id}/complete |
| Operation has no goods-movement details; cannot complete. | POST /operations/{id}/complete |
POS operation is missing a register reference. | POST /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. | POST /operations/{id}/complete |
| TSE signing failed (phase 2) — TSS rejected the operation after start. The operation is left in a recoverable state. | POST /operations/{id}/complete |
Reporting API
| Condition | Endpoint |
|---|---|
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
Today the only path that can return HTTP501 Not Implemented is the export endpoint, when a request lands for an export type that has no exporter registered. The body carries code: "not_implemented" and retryable: true. Because the public wire enum currently exposes only dsfinvk, this path is unreachable through normal use.
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.code | HTTP | Status |
|---|---|---|
idempotency_key_conflict | 409 | Documented 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_failed | 422 | Documented 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 anX-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.