# The error format

Every error of the API comes back in the same body: `code`, `message`, and `traceId` always, further fields if applicable. This page lists the fields, the rule by which a code becomes the HTTP status, the seven central responses of the reference, and the RFC 9457 form.

> **Type:** reference
> **For:** Anyone who handles error responses in a client or has to describe a response when asking for help.
> **Prerequisite:** none, because the format applies on every route, even without authentication

## The error body

```json
{
  "code": "Conflict.OptimisticLock",
  "message": "Die Szene „Willkommen & Öffnungszeiten\" wurde zwischenzeitlich geändert. Bitte neu laden und erneut speichern.",
  "traceId": "trc_01KXZACQD0PFAD000000000001",
  "correlationId": "cor_01KXZACQD0KETTE00000000001",
  "details": { "sceneId": "scn_01KJPX8NH0SZENE00000000001", "expectedVersion": 3, "actualVersion": 4 }
}
```

| Field | When | Content |
|---|---|---|
| `code` | always | the stable error code in the form `Area.Cause`, pattern `^[A-Z][a-zA-Z]+\.[A-Z][a-zA-Z0-9]+$` |
| `message` | always | short description for humans, 1–1000 characters, may change |
| `traceId` | always | the trace of this call: `trc_` and 26 characters, never empty and identical to the `X-Trace-Id` header |
| `details` | if applicable | additional information without personal data: `issues` for input errors, `expectedVersion` and `actualVersion` for version conflicts. Omitted entirely when there is nothing to report |
| `correlationId` | if applicable | identifier of a connected process, `cor_…`, up to 128 characters |
| `retryAfterSeconds` | for downstream services | whole seconds until the next attempt. Also sent as the `Retry-After` header |
| `externalService` | for downstream services | name of the service that returned an unusable response |
| `stack` | never in production | only in development environments, for server errors |

Two rules for every client: Branch on `code`, never on `message`. And include the `traceId` when you ask for help, because it is the key that lets the operations team find the call again.

## How the HTTP status follows from the code

The status is not set freely but derived deterministically from the code. The resolution checks in a fixed order, and the first match wins.

| Step | Rule | Example |
|---|---|---|
| 1 | Exact mapping of the individual code | `Validation.IdempotencyKeyRequired` → 409, `Http.BadRequest` → 400 |
| 2 | Mapping of the functional area | `Billing.LicensesExhausted` → 402, `Tenant.Frozen` → 403, `Signup.TicketInvalid` → 401 |
| 3 | Convention of the namespace | `Validation.*` → 422, `Auth.*` → 401, `Permission.*` → 403, `*.NotFound` → 404, `Conflict.*` → 409, `RateLimit.*` → 429, `Payload.*` → 413, `Integration.*` → 502 |
| 4 | Otherwise | 500 |

That is why most server errors carry no mapping of their own: They deliberately fall through to 500. Where a domain code deviates from the convention, that is intentional and explained on the respective code page.

## The seven central responses

The reference describes seven responses centrally. They mean the same on every operation.

| Response | Status | Meaning |
|---|---|---|
| `Unauthorized` | 401 | missing or invalid authentication |
| `PermissionDenied` | 403 | authenticated, but without the required permission |
| `NotFound` | 404 | the resource does not exist or is not visible to you |
| `Conflict` | 409 | concurrent change or violation of a uniqueness constraint |
| `ValidationFailed` | 422 | the request violates the schema or a business rule |
| `RateLimit` | 429 | too many requests, `Retry-After` gives the wait time |
| `IntegrationError` | 502 | a downstream service returned an unusable response |

## RFC 9457: Problem Details

If you send `Accept: application/problem+json`, the API responds with `Content-Type: application/problem+json` and a `ProblemDetails` document instead of the standard body.

| Field | When | Content |
|---|---|---|
| `type` | always | the address form of the code |
| `title` | always | short title, up to 200 characters |
| `status` | always | the HTTP status as a number |
| `detail` | always | the description, up to 1000 characters |
| `code` | always set | the same error code as in the standard body |
| `instance` | if applicable | the requested path |
| `errorDetails` | if applicable | the same additional information as `details` |
| `traceId`, `correlationId`, `retryAfterSeconds` | if applicable | as in the standard body |

There is no `stack` in this form.

```bash
curl -sS "$SZENERA_API/v1/scenes/scn_00000000000000000000000000" \
  -H "Authorization: Bearer $SZENERA_API_KEY" \
  -H "Accept: application/problem+json"
```

## Where the codes are listed

The catalog contains 612 codes, spread across seven pages:

| Page | Areas |
|---|---|
| [Possible on any route](/api/error-codes-general) | authentication, permissions, input validation, conflicts, throttling, payload, HTTP, operator routes |
| [Accounts and sessions](/api/error-codes-accounts) | accounts, keys, devices, sign-in, second factor |
| [Organization, access, joining](/api/error-codes-organization) | master data, roles, invitations, registration, storage connections |
| [Billing](/api/error-codes-billing) | package, licenses, payment method, invoices |
| [Media, scenes, programs, apps](/api/error-codes-content) | media, scenes, playlists, programs, app catalog, marketplace, app runtime, news sources, advertising |
| [Devices, playback, notifications, operations](/api/error-codes-devices-operations) | fleet, playback, realtime, webhooks, analytics, audit log, operations console |
| [Server errors (5xx)](/api/error-codes-server) | invariants, startup, database, downstream services |

## Related

- [Error codes: possible on any route](/api/error-codes-general)
- [What a response contains](/getting-started/response-contents)
- [Concurrent changes: ETag and If-Match](/api/etag-and-if-match)
