Skip to content

Error codes

HTTPerror.codeMeaningWhat to do
401missing_tokenNo API token provided. Expected: Authorization: Bearer <token>.Correct the input: Send the header Authorization: Bearer <token>.
401invalid_authorization_schemeThe Authorization scheme is not supported. Expected: Authorization: Bearer <token>.Correct the input: Use the Bearer scheme (not Basic or similar).
401invalid_tokenThe API token is invalid or has been revoked.Correct the input: Check the token for typos. If it was revoked or its creator deactivated, create a new token in the management app and replace it in your program. Do not retry: repeated attempts with a wrong token count as failed attempts per IP address.
401token_expiredThe API token has expired. Please create a new token.Correct the input: Create a new token in the management app and replace it in your program.
401wrong_environmentThe API token belongs to a different environment.Correct the input: Use the token of the matching environment (live, stg, dev) — every token only works in its own.
400token_in_url_forbiddenTokens must not appear in the URL. Please use the Authorization header.Correct the input: Remove the token from the URL and send it in the Authorization header. Treat tokens that appeared in a URL as compromised: revoke them.
403insufficient_scopeThe API token lacks the required permission (scope).Correct the input: The token needs the scope named in error.required_scope: create a new token with that scope (an existing token cannot be extended).
403insufficient_permissionsThe creator of the API token does not have (or no longer has) the required permission.Correct the input: Give the token creator the missing permission in the management app (or create a token owned by a person who has it). The token then works again without any change.
400validation_errorThe request is invalid.Correct the input: error.details lists the error codes per field: correct the request and send it again. An unchanged retry fails again.
400invalid_cursorThe cursor is invalid. Please start again without a cursor.Correct the input: Start again without a cursor. Only pass cursors back unchanged and only to the endpoint they came from.
410cursor_expiredThe change-sync cursor has expired (changes are kept for 90 days). Please get a new starting cursor and fetch everything once.Correct the input: Get a new starting cursor from …/changes/cursor, fetch everything once through the list and continue syncing from there.
404not_foundNot found.Correct the input: Check the id (does it come from your organization and the right list call?). The resource may also have been deleted. Do not retry.
404route_not_foundThis address does not exist in the API. All endpoints are under /v1.Correct the input: Check path and spelling; all endpoints are under /v1 (see the reference).
400range_too_largeThe period is too long (maximum span per endpoint in the documentation). Please split it into smaller parts.Correct the input: Split the period or selection into smaller parts (maximum span per endpoint in the reference; for file output error.details.max_rows).
400field_not_allowedBank details (iban, bic, account_holder) cannot be submitted via the API.Correct the input: Remove the fields named in error.details (row and field) from the request; bank details are entered in the management app.
400idempotency_key_requiredThe Idempotency-Key header is missing. Please send a unique key per operation (e.g. a UUID).Correct the input: Send an Idempotency-Key (e.g. a UUID) per operation and reuse it for retries of the same operation.
409idempotency_conflictThis Idempotency-Key has already been used with different content.Correct the input: The key belongs to different content. Generate a new key for a new operation; for a retry, send exactly the first body.
409readings_lockedThe meter readings of your organization are being edited right now (another entry, import or billing run). This call was aborted; please resend in a few seconds (with the same Idempotency-Key for calls that carry one). If a call is processed in several blocks (import commit from 25 records on), earlier blocks may already have been taken over: they report `already_transferred` when you resend.Try again: The meter readings of your organization are being edited right now. The call was aborted; resend in a few seconds (with the same Idempotency-Key if the call carries one). For the import commit in several blocks, earlier blocks may already have been taken over (`already_transferred`).
409appointment_conflictThere is already an active appointment of this type for this lease. It cannot be rescheduled via the API.Correct the input: An active appointment of this type already exists for the lease; it cannot be rescheduled via the API. Change or cancel it in the management app.
409meter_existsThis unit already has an active meter of this kind (electricity or water). Another one cannot be created via the API.Correct the input: The unit already has an active meter of this kind. Read existing meters with GET /v1/meters?unit_id=…; replacing or changing a meter is done in the management app.
422unit_not_configuredThe unit is not set up for this kind (electricity 230 V, electricity 400 V or water). Please enter the connection on the unit in the management app first.Correct the input: Enter the connection (electricity 230 V, electricity 400 V or water) on the unit in the management app, then repeat the call with the same Idempotency-Key.
413payload_too_largeThe request is too large. The limit applies per endpoint and is given in error.details.max_bytes (in bytes).Correct the input: Bring the body below the limit of this endpoint (error.details.max_bytes, in bytes), e.g. spread records over several calls.
409session_has_transferred_recordsThe import already contains transferred records and cannot be cancelled (tenants and leases refer to it).Correct the input: An import with transferred records cannot be cancelled. It can only be handled further in the management app.
415unsupported_media_typeExpected: Content-Type: application/json.Correct the input: Send the header Content-Type: application/json.
403sandbox_unavailableThe sandbox is currently not usable: the API access of your organization is not active (trial ended, add-on module API access not booked, or blocked by the operator) or the sandbox has expired. Check the add-on module under Administration → API access; with active access the same token works again.Correct the input: Sandbox tokens only: check the add-on module API access in the management app (trial, booking); if the sandbox has expired, create a new one. The token itself is valid and works again once access is active.
422sandbox_limit_reachedThe sandbox has reached its data volume (at most 3000 tenants, prospects, damages, meter readings, appointments and import records combined). Remove unneeded test data in the management app or replace the sandbox; unused sandboxes are deleted after 30 days.Correct the input: Sandbox only: remove test data in the management app or replace the sandbox ("Create new sandbox" after expiry); unused sandboxes are deleted after 30 days.
422import_limit_reachedThe maximum number of open imports has been reached (limit in error.details). Take over or delete open imports in the management app — or ask support for a higher limit.Correct the input: Take over open imports in the management app or cancel them with DELETE /v1/tenant-imports/{id}; or ask support for a higher limit.
422import_total_limit_reachedYour organization already has 500 imports (file and API, including completed ones). Please delete completed imports in the management app.Support: Delete completed imports in the management app; if that is not enough, contact support with the request_id.
429import_rate_limitedThis token has reached the maximum number of new imports per hour (limit in error.details). Please try again later or ask support for a higher limit.Try again: Try again later (hourly limit per token, limit and count in error.details); or ask support for a higher limit.
400period_conflictTime filter: please use exactly one form (period, date or from/to).Correct the input: Use exactly one form of the time filter: period, date or from/to.
400range_invalidThe time range is invalid (from must be before to).Correct the input: from must be before to; swap or correct the values.
400unsupported_formatThis output format is not supported by this endpoint.Correct the input: Choose one of the formats in error.details.supported_formats (json, csv); or fetch fewer columns or shorter texts.
404file_not_availableNo file is available for this entry.Correct the input: There is no file for this entry; do not retry. The list shows per entry whether a file exists.
409export_not_readyThe export is not ready yet. Please try again later.Try again: Fetch the export again after a short wait (GET /v1/datev/exports/{id}) until status = ready.
400webhook_url_invalidThe webhook URL is invalid or not allowed (https on port 443 only, public addresses only).Correct the input: Use a publicly reachable https address on port 443 without credentials that resolves through DNS.
409webhook_limit_reachedThe maximum number of webhooks has been reached (limit in error.details).Correct the input: Delete a webhook you no longer need (DELETE /v1/webhooks/{id}) or change an existing one.
409delivery_not_replayableThis delivery cannot be sent again: only failed or abandoned deliveries of the last 29 days, no test events (reason in error.details.reason).Correct the input: error.details.reason names the cause (not_failed, test_event, expired, paused); do not retry. For test_event, trigger a new test event.
429rate_limitedToo many requests. Please retry after the time given in Retry-After.Try again: Wait at least the time in the Retry-After header (seconds), then repeat unchanged; double the wait on further 429 responses.
503service_unavailableThe API is temporarily unavailable.Try again: Try again with a growing wait (backoff). Repeat writing calls with the same Idempotency-Key. If the error persists, contact support with the request_id.
500internal_errorInternal error. Please contact support and quote the request_id.Try again: Retry once after a short wait (for writes with the same Idempotency-Key); if the error persists, contact support with the request_id. Do not retry in a tight loop.

“Try again” means: the request was fine, a later attempt can succeed — for writes with the same Idempotency-Key. “Correct the input” means: an unchanged retry fails again; change the request, the token or the permissions. “Support”: cannot be fixed on your side — report it with the request_id (see Security & support and the threat model).

If the API answers with 500 internal_error or 503 service_unavailable, or requests come back unusually slowly, check the status page first: it shows the current state of the API and of webhook delivery as well as ongoing incidents.

The error.message field of a response is currently in German; the English explanations above are for reference. Programs should always evaluate error.code.

Unknown or foreign ids are always answered with 404 not_found — never with “no permission”. This way the API does not reveal whether an id exists elsewhere. An identifier in the path that is not a UUID at all (/v1/invoices/abc) is a format error instead: 400 validation_error with { "id": ["invalid_uuid"] } in error.details (until 2026-10-07 the API answered 404 here). An address that does not exist (/v1/foo) returns 404 route_not_found in the same error format.

What error.details contains

error.details is optional and absent for most errors. Where it is present it has a fixed shape per error code; the OpenAPI describes it accordingly, and the responses of the individual endpoints in the reference name which details they return.

error.codeForm of error.detailsExample
validation_errorobject: field name → list of codes (codes see below){ "from": ["required"], "foo": ["unknown_parameter"] }
field_not_allowedlist with row and field, at most 50 entries[{ "row": 1, "field": "iban" }]
payload_too_largemaximum size of this endpoint in bytes (differs per endpoint, e.g. 1048576 for the import, 65536 for the takeover, 4096 for the bulk replay){ "max_bytes": 1048576 }
import_limit_reachedlimit and count of open imports{ "limit": 50, "open": 50 }
import_rate_limitedlimit and count per hour and token{ "limit": 20, "used": 20 }
webhook_limit_reachedlimit and current count of webhooks per organization (in a sandbox only the limit: { "limit": 3 }){ "limit": 10, "used": 10 }
delivery_not_replayabledelivery status and reason{ "status": "succeeded", "reason": "not_failed" }
rate_limitedonly for webhook replay, test events and prospect submissions: limit (replay additionally retry_after); otherwise no details{ "limit": 1000, "retry_after": 1800 }
range_too_largeonly for file output (CSV/Excel): maximum rows; otherwise no details{ "max_rows": 200 }
unsupported_formatallowed formats; for an Excel cell that is too long additionally reason, column and maximum length{ "supported_formats": ["json", "csv", "xlsx"] }

Details of validation_error

With 400 validation_error, error.message states the first problem in plain words. error.details maps every affected field to a list of codes — the key is the name of the query parameter or body field, for lists in the body with the position (records[2].lease_start). The codes are stable; the message may change.

Example
{
  "success": false,
  "error": {
    "code": "validation_error",
    "message": "Parameter „from“ fehlt (erwartet: YYYY-MM-DD).",
    "details": { "from": ["required"], "to": ["required"], "foo": ["unknown_parameter"] }
  },
  "request_id": "req_…"
}
CodeMeaning
requiredrequired value is missing
invalid_typewrong type, e.g. a number sent as text
invalid_formatvalue does not match the expected format
invalid_uuidnot a valid identifier (UUID)
invalid_datenot a valid date (YYYY-MM-DD) or ISO datetime
invalid_valuevalue not allowed (the message lists the allowed values)
too_smallnumber, text or list below the minimum size
too_bignumber, text or list above the maximum size
too_many_idsmore than 100 identifiers in ids
duplicate_idsthe same identifier more than once in ids
unknown_parameterunknown query parameter (its name is the key)
unknown_fieldunknown field in the body
before_fromto is before from — the error code is then range_invalid
invalid_jsonthe body is not valid JSON
invalid_charactersillegal character (U+0000) in the URL or body

Earlier raw codes (VALIDATION_INVALID_UUID, date_format_yyyy_mm_dd, date_or_datetime_iso, status_code_or_class, path_prefix_format) are still listed behind the new code for the parameters that used to return them — for positions in lists (ids[1], records[2].lease_start) additionally under the top-level field (ids, records), where they used to appear. English default texts of the validation library (such as “Too small: expected number to be >=1”) are no longer returned; from after to is reported as range_invalid everywhere instead of validation_error.