Change history
Every change to this page, newest first. Expand an entry to see what changed.
- 19 Oct 2026 · +19 −0 lines
Show changes
⋯ | 503 | `5031` | Wandura could not check the key in time. | Backoff | Retry. | + ### Download a creative's final file {#creative-file-errors} + + `GET /api/delivery/v1/creatives/{id}/file` + + | Status | Code | When | Retry? | Your backend does | + |---|---|---|---|---| + | 400 | `unsupported_version` (flat) | Unknown or retired dated version. | No | Send a supported version. | + | 401 | `4010` | No `Authorization: Bearer` header. | No | Fix the request. | + | 401 | `4011` | Key unknown or revoked. | No | Check the key. | + | 403 | `4030` | The key lacks `creatives:export`. | No | Use a key with that scope. | + | 403 | `4031` | Your organisation is suspended. | No | Contact Wandura. | + | 403 | `4032` | `rights_ended`: your export rights for the film have ended. | No | Stop using the creative. | + | 404 | `4040` | No approved creative with this ID for your organisation (also: the ID is not a UUID). | No | Remove it from your active set. | + | 409 | `4090` | `final_not_ready`: the final file is not ready yet. | Later | Retry when the record's `final.ready` is `true`. | + | 429 | `rate_limited` (flat) | Creatives limit reached (shared with the other creatives endpoints). | After `Retry-After` | Wait. | + | 503 | `5030` | Creative data is temporarily unavailable. | Backoff | Retry. | + | 503 | `5031` | Wandura could not check the key in time. | Backoff | Retry. | + | 503 | `5032` | The download link could not be prepared. | Backoff | Retry. | + ### List open videos {#videos-errors} ⋯
- 5 Oct 2026 · +35 −0 lines
Show changes
⋯ | 503 | `5031` | Wandura could not check the key in time. | Backoff | Retry. | + ### List playlists {#playlists-errors} + + `GET /api/delivery/v1/playlists` + + | Status | Code | When | Retry? | Your backend does | + |---|---|---|---|---| + | 400 | `unsupported_version` (flat) | Unknown or retired dated version. | No | Send a supported version. | + | 401 | `4010` | No `Authorization: Bearer` header. | No | Fix the request. | + | 401 | `4011` | Key unknown or revoked. | No | Check the key. | + | 403 | `4030` | The key lacks `metadata:read`. | No | Use a key with that scope. | + | 403 | `4031` | Your organisation is suspended. | No | Contact Wandura. | + | 403 | `4032` | Your organisation licenses films, not video. | No | Use [List series](../reference/series-list.md). | + | 404 | `4040` | The `category` is not in the library. | No | Check the Category slug. | + | 422 | `4220` | `limit` is not 1–100, `updated_since` is not epoch seconds, `category` is not a slug, or `cursor` is not a value this endpoint returned. | No | Fix the request; restart from your last checkpoint. | + | 429 | `rate_limited` (flat) | Catalog limit reached (shared with the catalog). | After `Retry-After` | Wait; sync less often. | + | 503 | `5030` | The list is temporarily unavailable. | Backoff | Retry; keep your checkpoint. | + | 503 | `5031` | Wandura could not check the key in time. | Backoff | Retry. Do not rotate the key. | + + ### Get playlist metadata {#playlists-get-errors} + + `GET /api/delivery/v1/playlists/{code}` + + | Status | Code | When | Retry? | Your backend does | + |---|---|---|---|---| + | 400 | `unsupported_version` (flat) | Unknown or retired dated version. | No | Send a supported version. | + | 401 | `4010` | No `Authorization: Bearer` header. | No | Fix the request. | + | 401 | `4011` | Key unknown or revoked. | No | Check the key. | + | 403 | `4030` | The key lacks `metadata:read`. | No | Use a key with that scope. | + | 403 | `4031` | Your organisation is suspended. | No | Contact Wandura. | + | 403 | `4032` | Your organisation licenses films, not video. | No | Use [Get series metadata](../reference/series-get.md). | + | 404 | `4040` | No such playlist, or it is no longer in the library (the same answer on purpose). | No | Stop showing it; check the list. | + | 429 | `rate_limited` (flat) | Catalog limit reached (shared with the catalog). | After `Retry-After` | Wait. | + | 503 | `5030` | The playlist is temporarily unavailable. | Backoff | Retry. | + | 503 | `5031` | Wandura could not check the key in time. | Backoff | Retry. | + ### Playback {#playback-errors} ⋯
- 4 Oct 2026 · +32 −0 lines
Show changes
⋯ | 503 | `service_unavailable` | Wandura could not read the catalog in time (`Could not read the catalog in time — retry`). | After `Retry-After` | — | Retry. **Never** treat it as an empty catalog. | + ### List series {#series-errors} + + `GET /api/delivery/v1/series` + + | Status | Code | When | Retry? | Your backend does | + |---|---|---|---|---| + | 400 | `unsupported_version` (flat) | Unknown or retired dated version. | No | Send a supported version. | + | 401 | `4010` | No `Authorization: Bearer` header. | No | Fix the request. | + | 401 | `4011` | Key unknown or revoked. | No | Check the key. | + | 403 | `4030` | The key lacks `metadata:read`. | No | Use a key with that scope. | + | 403 | `4031` | Your organisation is suspended. | No | Contact Wandura. | + | 422 | `4220` | `limit` is not 1–100, `updated_since` is not epoch seconds, or `cursor` is not a value this endpoint returned. | No | Fix the request; restart from your last checkpoint. | + | 429 | `rate_limited` (flat) | Catalog limit reached (shared with the catalog). | After `Retry-After` | Wait; sync less often. | + | 503 | `5030` | The list is temporarily unavailable. | Backoff | Retry; keep your checkpoint. | + | 503 | `5031` | Wandura could not check the key in time. | Backoff | Retry. Do not rotate the key. | + + ### Get series metadata {#series-get-errors} + + `GET /api/delivery/v1/series/{code}` + + | Status | Code | When | Retry? | Your backend does | + |---|---|---|---|---| + | 400 | `unsupported_version` (flat) | Unknown or retired dated version. | No | Send a supported version. | + | 401 | `4010` | No `Authorization: Bearer` header. | No | Fix the request. | + | 401 | `4011` | Key unknown or revoked. | No | Check the key. | + | 403 | `4030` | The key lacks `metadata:read`. | No | Use a key with that scope. | + | 403 | `4031` | Your organisation is suspended. | No | Contact Wandura. | + | 404 | `4040` | No such series, or your organisation may not stream it (the same answer on purpose). | No | Stop showing it; check the list. | + | 429 | `rate_limited` (flat) | Catalog limit reached (shared with the catalog). | After `Retry-After` | Wait. | + | 503 | `5030` | The series is temporarily unavailable. | Backoff | Retry. | + | 503 | `5031` | Wandura could not check the key in time. | Backoff | Retry. | + ### Playback {#playback-errors} ⋯
- 2 Oct 2026 · +3 −5 lines
Show changes
⋯ ### Request id {#request-id} - > **Rolling out.** Every response carries a `Wandura-Request-Id` header (for example `req_01J9Z3W2N8V4K7D5QX6T0B1C2M`), and every error body, flat or envelope, a `request_id` field with the same value. Log it, and quote it when you contact support: it finds the exact request in Wandura's logs. - - Until it is live, quote the endpoint, the time in UTC to the second, the status, the code and your key prefix. + Every response carries a `Wandura-Request-Id` header (for example `req_01J9Z3W2N8V4K7D5QX6T0B1C2M`), and every JSON error body, flat or envelope, a `request_id` field with the same value. Log it, and quote it when you contact support: it finds the exact request in Wandura's logs. ## Handle errors by code {#handle-by-code} ⋯ | 429 | `rate_limited` | Playback limit reached for every item. With `"reason": "prefetch_shed"`: prefetch is paused near the limit. | After `Retry-After` | Busy (play); nothing (prefetch) | Retry plays; drop prefetches. See [Rate limits](rate-limits.md). | | 503 | `service_unavailable` | Wandura could not check the key in time. | After `Retry-After` | Busy | Retry. Do not rotate the key. | - | 500 | none (rare) | An unexpected error. | Backoff | Busy | Retry once; report it with the time. | + | 500 | none (rare) | An unexpected error. The JSON body carries `request_id`. | Backoff | Busy | Retry once; report it with the request id. | **Per ticket** (top level for a single episode, or one item of a batch) ⋯ > **Do not take irreversible steps on one answer.** Before you stop selling a film or refund viewers because of `not_entitled` or `not_ready`, confirm it with your next catalog sync: a withdrawn film disappears from the catalog. - > **Rolling out.** If Wandura cannot check your licence or the episode in time while issuing a ticket, it answers `503` `service_unavailable` (`Could not check the licence or episode right now — retry`) with `Retry-After`, never `404` or `403`. Keep selling, and retry. + If Wandura cannot check your licence or the episode in time while issuing a ticket, it answers `503` `service_unavailable` (`Could not check the licence or episode right now — retry`) with `Retry-After`, never `404` or `403`. Keep selling, and retry. #### `not_entitled` reasons {#not-entitled-reasons} ⋯
- 2 Oct 2026 · +180 −72 lines
Show changes
# Errors - The Delivery API uses standard HTTP status codes and adds a machine-readable code to most errors. The catalog and playback endpoints return a flat error object with an `error_code` string; the creatives endpoints return an envelope with a numeric `code`. This page lists every error each endpoint returns today, what it means and what to do. + > **TL;DR** — Branch on `error_code` (creatives: the numeric `code`), never on the message. Keep a default branch: an unknown code is handled by its HTTP status class (`429` and `5xx` retry, other `4xx` do not). This page lists every status and code each endpoint returns, with what your app shows and what your backend does. When to retry, and how: the [retry policy](../reference/behaviour.md#retries). - ## Error formats + ## Error formats {#error-formats} - ### Catalog and playback + ### Flat format {#flat-format} + Catalog, playback, configuration and open video use a flat object: + ```json { ⋯ | Field | Type | Description | |---|---|---| - | `error` | string | Human-readable message. Log it, but do not parse it, except for the catalog `403` below. | - | `error_code` | string | Machine-readable code. Branch on this. Absent on some errors, listed below as "none". | - | `reason` | string | Only with `not_entitled`: the detailed reason, for logs and support. | - | `fields` | array of strings | Only with `invalid_device_info`: which device values were rejected. | - | `protection` | string | Only with `sdk_required`: the protection level of the episode. | + | `error` | string | A message for your logs. Do not show it to viewers and do not parse it, except where a row below says the code is in `error`. | + | `error_code` | string | The machine-readable code. Branch on it. Absent on a few errors (shown as **none** below). | + | `reason` | string | With `not_entitled`, and with `429` `prefetch_shed`: the detail. | + | `fields` | array of strings | With `invalid_device_info`: the device values that were rejected. | + | `protection` | string | With `sdk_required`: the protection level of the episode. | + | `supported_versions`, `latest_version`, `upgrade_to` | | With `unsupported_version`: what you can send instead. | - ### Creatives + A few `4xx` answers carry no `error_code` and put a stable code in `error` instead: `org_suspended`, `app_not_registered`, `collection_not_found` and `video_not_found`. Compare the whole string. + ### Creatives envelope {#creatives-envelope} + + The creatives endpoints wrap errors in an envelope with a numeric `code`. Errors have **no `data`** field: + ```json { ⋯ ``` - | Field | Type | Description | - |---|---|---| - | `code` | integer | `0` on success, a four-digit error code otherwise. The first three digits are the HTTP status. | - | `message` | string | Human-readable message. Do not parse it. | - | `server_time` | integer | Server time in Unix epoch seconds. | + `code` is four digits: the first three are the HTTP status. Two creatives answers use the **flat** format instead: `429` `rate_limited` and `400` `unsupported_version`. - The one exception is `429`: the creatives endpoints return it in the flat format, with `error_code` `rate_limited`. See [Rate limits](rate-limits.md). + ### Request id {#request-id} - ## Handle errors by code + > **Rolling out.** Every response carries a `Wandura-Request-Id` header (for example `req_01J9Z3W2N8V4K7D5QX6T0B1C2M`), and every error body, flat or envelope, a `request_id` field with the same value. Log it, and quote it when you contact support: it finds the exact request in Wandura's logs. - - Branch on `error_code` (or `code` for creatives), not on the HTTP status alone. Several different situations share a status. - - Always keep a default branch. Wandura may add new codes within `v1`, and an app without a default branch shows a blank screen when one appears. - - Never show `error` or `message` text to viewers. Map codes to your own copy. + Until it is live, quote the endpoint, the time in UTC to the second, the status, the code and your key prefix. - ## Catalog errors + ## Handle errors by code {#handle-by-code} - `GET /api/delivery/v1/catalog` + | You received | Do | + |---|---| + | A code listed on this page | Follow its row. | + | An unknown `error_code` with `429` | Wait `Retry-After`, then retry. | + | An unknown `error_code` with another `4xx` | Do not retry. Show a generic error and alert your engineers. | + | An unknown `error_code` with a `5xx` | Retry with backoff, like `503`. | + | No body, or a body that is not JSON | Decide by status class, the same way. | - | Status | `error_code` | Message | Meaning | What to do | + New codes can appear within `v1` at any time; an app without a default branch shows a blank screen when one does. Never show `error` or `message` to viewers: map codes to your own text. + + ### `Retry-After` {#retry-after} + + Sent with every `429`, and with the `503`s marked "after `Retry-After`" below. Never retry sooner. `503` `not_ready`, creatives `503`s, `502` and `500` carry none: use backoff. + + ## Messages for viewers {#viewer-messages} + + The "Your app shows" column below uses these four messages. Use your own wording. + + | Message | When | + |---|---| + | **Unavailable** — "This title is no longer available" | The licence is not active. | + | **Not yet** — "Not available yet" | The episode is not playable right now. | + | **Busy** — "Busy, try again in a moment" | Retried and still failing temporarily. | + | **Broken** — "Couldn't load this episode" | A problem in your integration. Alert your engineers. | + + ## Every endpoint {#all-endpoints} + + ### Every path {#every-path} + + | Status | Code | When | Retry? | Do | |---|---|---|---|---| - | 401 | none | `Missing Bearer API key` | No `Authorization: Bearer` header. | Fix the request. Do not retry. | - | 401 | none | `Invalid or revoked API key` | The key is unknown or revoked. | Check the key. Do not retry. | - | 403 | none | `This API key does not have the metadata:read scope` | The key lacks the scope. | Use a key with `metadata:read`. | - | 403 | none | `org_suspended` | Your organisation's access is suspended. | Contact Wandura. Do not retry. | - | 429 | `rate_limited` | | Rate limit reached. | Wait for `Retry-After`. | - | 503 | `service_unavailable` | `Could not verify the API key right now — retry` | Wandura could not check the key in time. The key is **not** wrong. | Retry after `Retry-After` seconds, with backoff. Do not rotate the key. | + | 404 | `not_found` | Unknown path under `/api/delivery/v1/`, any method. No `Wandura-Version` header. | No | Fix the path. | + | 405 | none (empty body) | A real path with the wrong method, for example `GET /playback`. The `Allow` header lists the right one. | No | Fix the method. | - ## Playback errors + ### Catalog {#catalog-errors} - `POST /api/delivery/v1/playback` + `GET /api/delivery/v1/catalog` - | Status | `error_code` | Meaning | What to do | - |---|---|---|---| - | 400 | none | The body is not valid JSON, or has neither `episode_id` nor `film_ref` with a numeric `episode_no`. | Fix the request. | - | 400 | `invalid_request` | Only one of `union_info` and `drm_kek` was sent. | Send both. | - | 400 | `invalid_device_info` | A device value looks empty or invalid, for example the string `"(null)"`. `fields` names the bad values. | Fix how your app reads the values from the Player SDK. | - | 401 | none | Missing, invalid or revoked API key. | Check the key. Do not retry. | - | 403 | none | The key lacks the `playback` scope. | Use a playback key: create one on **Developers** → **Stream films in an app**. | - | 403 | `not_entitled` | Your organisation is not licensed to stream this film right now. `reason` gives the detail. | Show your locked or unavailable state. Do not retry. | - | 404 | `not_ready` | The episode or film is unknown, not published, or not ready to stream. | Show "not available yet". Refresh your catalog. | - | 409 | `sdk_required` | The episode is encrypted and the request had no device values. | Resend with `union_info` and `drm_kek`. | - | 429 | `rate_limited` | Rate limit reached. | Wait for `Retry-After`. | - | 502 | none | Wandura could not prepare the stream or sign the play token. | Retry with backoff. | - | 503 | `not_ready` | No playable version of this episode is available right now. | Show "not available yet". Retry later. | - | 503 | `service_unavailable` | Wandura could not check the API key in time. The key is **not** wrong. | Retry after `Retry-After` seconds, with backoff. Do not rotate the key. | - | 503 | `service_unavailable` | The video provider did not answer in time (playback only). Nothing is wrong with the episode or your key. | Retry after `Retry-After` seconds (2), with backoff. | + | Status | Code | When | Retry? | Your app shows | Your backend does | + |---|---|---|---|---|---| + | 400 | `invalid_request` | `limit` is not 1–200, or `cursor` was changed. | No | — | Fix the request. Restart the walk from page one. | + | 400 | `unsupported_version` | The `Wandura-Version` header, or your key's pin, is unknown or retired. | No | — | Send a supported version; see [Migrating versions](../guides/migrating-versions.md). | + | 401 | none | Header missing, or key unknown or revoked. | No | — | Check the key. Keep your previous catalog. | + | 403 | none | The key lacks `metadata:read`. | No | — | Use a key with that scope. | + | 403 | `org_suspended` (in `error`) | Your organisation is suspended. | No | — | Keep your copy, stop selling, contact Wandura. | + | 429 | `rate_limited` | Catalog limit reached. | After `Retry-After` | — | Wait, then find the loop. | + | 503 | `service_unavailable` | Wandura could not check the key in time. The key is fine. | After `Retry-After` | — | Retry. Never rotate the key for this. | + | 503 | `service_unavailable` | Wandura could not read the catalog in time (`Could not read the catalog in time — retry`). | After `Retry-After` | — | Retry. **Never** treat it as an empty catalog. | - ### `not_entitled` reasons + ### Playback {#playback-errors} - With `not_entitled`, the `reason` field says why. Log it and quote it to support, but do not build different screens for each reason: they all mean "not licensed right now", and new reasons may be added. + `POST /api/delivery/v1/playback`. In a batch, the request-level rows answer the whole request; the per-ticket rows come back inside `items[]`, each with its own `status` (and `retry_after` instead of the header). + **Whole request** + + | Status | Code | When | Retry? | Your app shows | Your backend does | + |---|---|---|---|---|---| + | 400 | none | The body is not JSON, or has neither `episode_id` nor `film_ref` with a numeric `episode_no`. | No | Broken | Fix the request. | + | 400 | `invalid_request` | Only one of `union_info` and `drm_kek`, or a malformed batch. | No | Broken | Fix the request. | + | 400 | `invalid_device_info` | A device value is empty-looking, for example `"(null)"`, or under 8 characters. `fields` names it. | No | Broken | Fix how the app reads the Player SDK values. Alert. | + | 400 | `unsupported_version` | Unknown or retired dated version. | No | Broken | See [Migrating versions](../guides/migrating-versions.md). | + | 401 | none | Header missing, or key unknown or revoked. | No | Broken | Check the key. Alert. | + | 403 | none | The key lacks `playback`. | No | Broken | Use a playback key. | + | 429 | `rate_limited` | Playback limit reached for every item. With `"reason": "prefetch_shed"`: prefetch is paused near the limit. | After `Retry-After` | Busy (play); nothing (prefetch) | Retry plays; drop prefetches. See [Rate limits](rate-limits.md). | + | 503 | `service_unavailable` | Wandura could not check the key in time. | After `Retry-After` | Busy | Retry. Do not rotate the key. | + | 500 | none (rare) | An unexpected error. | Backoff | Busy | Retry once; report it with the time. | + + **Per ticket** (top level for a single episode, or one item of a batch) + + | Status | Code | When | Retry? | Your app shows | Your backend does | + |---|---|---|---|---|---| + | 403 | `not_entitled` | Your organisation may not stream this film right now. `reason` says why (table below). | No | Unavailable | Log `reason`. Stop selling the film once your next catalog sync confirms it is gone. | + | 404 | `not_ready` | The episode or film is unknown, not published or not ready. | No | Not yet | Refresh your catalog. | + | 409 | `sdk_required` | Encrypted episode and no device values. | No | Broken | Send `union_info` and `drm_kek`. Alert. | + | 429 | `rate_limited` | This item could not be admitted (batch only). `reason` `prefetch_shed` for a paused prefetch. | After `retry_after` | Nothing for a prefetch | Retry this item only. | + | 502 | none | The stream could not be prepared, or the play token could not be signed. | Backoff | Busy | Retry, then report. | + | 503 | `not_ready` | No playable version of the episode right now. No `Retry-After`. | Not now | Not yet | Try again later (next play or sync). | + | 503 | `service_unavailable` | The video provider did not answer in time (`The video provider did not answer in time — retry`). | After `Retry-After` (2 s) | Busy | Retry. The episode is not missing. | + | 503 | `service_unavailable` | The ticket could not be issued right now (`Could not issue this grant right now — retry`). | After `Retry-After` | Busy | Retry. | + + > **Do not take irreversible steps on one answer.** Before you stop selling a film or refund viewers because of `not_entitled` or `not_ready`, confirm it with your next catalog sync: a withdrawn film disappears from the catalog. + + > **Rolling out.** If Wandura cannot check your licence or the episode in time while issuing a ticket, it answers `503` `service_unavailable` (`Could not check the licence or episode right now — retry`) with `Retry-After`, never `404` or `403`. Keep selling, and retry. + + #### `not_entitled` reasons {#not-entitled-reasons} + + Log `reason` and quote it to support, but show the same screen for all of them: new reasons may be added. + | `reason` | Meaning | |---|---| ⋯ | `entitlement_suspended` | Your licence for this film is suspended. | | `streaming_disabled` | Your licence for this film does not include streaming. | - | `outside_window` | Today is outside your licence window for this film. | + | `outside_window` | Today (UTC) is outside your licence window. | - ## Creatives errors + ### Configuration {#config-errors} - `GET /api/delivery/v1/creatives` and `GET /api/delivery/v1/creatives/{id}` + `GET /api/delivery/v1/config` - | Status | `code` | Message | Meaning | What to do | + | Status | Code | When | Retry? | Your backend does | |---|---|---|---|---| - | 401 | 4010 | `Missing Bearer API key` | No `Authorization: Bearer` header. | Fix the request. | - | 401 | 4011 | `Invalid or revoked API key` | The key is unknown or revoked. | Check the key. Do not retry. | - | 403 | 4030 | `This API key does not have the creatives:export scope` | The key lacks the scope. | Use a key with `creatives:export`. | - | 403 | 4031 | `org_suspended` | Your organisation's access is suspended. | Contact Wandura. | - | 404 | 4040 | `Creative not found` | `GET /creatives/{id}` only. The creative does not exist, belongs to another organisation or is not approved. | Remove it from your active set. | - | 422 | 4220 | `limit must be an integer between 1 and 100` | List only. Bad `limit`. | Fix the request. | - | 422 | 4221 | `updated_since must be epoch seconds` | List only. Bad `updated_since`. | Fix the request. | - | 422 | 4222 | `cursor is not valid` | List only. The cursor was changed or truncated. | Restart from your last checkpoint. | - | 429 | none (flat body, `error_code` `rate_limited`) | | Rate limit reached. | Wait for `Retry-After`. | - | 503 | 5030 | `The creative feed is temporarily unavailable` | Temporary problem on Wandura's side. | Retry with backoff. | - | 503 | 5031 | `Could not verify the API key right now — retry` | Wandura could not check the key in time. The key is **not** wrong. | Retry with backoff. Do not rotate the key. | + | 400 | `unsupported_version` | Unknown or retired dated version. | No | Send a supported version. | + | 401 | none | Header missing, or key unknown or revoked. | No | Check the key. | + | 403 | `org_suspended` (in `error`) | Your organisation is suspended. | No | Contact Wandura. | + | 429 | `rate_limited` | Configuration limit reached. | After `Retry-After` | Read it less often: at start-up or every few hours. | + | 503 | `service_unavailable` | Wandura could not check the key in time. | After `Retry-After` | Retry; keep the configuration you had. | + | 503 | `service_unavailable` | Wandura could not read your configuration in time (`Could not read your configuration in time — retry`). | After `Retry-After` | Retry; keep the configuration you had. | - ## Retry guidance + ### List creatives {#creatives-errors} - | Situation | Retry? | - |---|---| - | `400`, `401`, `403`, `409`, `422` | No. Fix the request, the key or the licence first. | - | `404` / `not_ready` | Not immediately. Refresh your catalog and try again later. | - | `429` | Yes, after the number of seconds in `Retry-After`. | - | `502`, `503`, other `5xx` | Yes, with exponential backoff and a cap, for example 1 s, 2 s, 4 s, then give up. | + `GET /api/delivery/v1/creatives` - During playback, request a fresh ticket once when the Player SDK fails, then follow this table. See [Playback](playback.md#expiry). For jitter, a retry budget and sample code, see [Smooth playback under load](../guides/smooth-playback.md#retry). + | Status | Code | When | Retry? | Your backend does | + |---|---|---|---|---| + | 400 | `unsupported_version` (flat) | Unknown or retired dated version. | No | Send a supported version. | + | 401 | `4010` | No `Authorization: Bearer` header. | No | Fix the request. | + | 401 | `4011` | Key unknown or revoked. | No | Check the key. | + | 403 | `4030` | The key lacks `creatives:export`. | No | Use a key with that scope. | + | 403 | `4031` | Your organisation is suspended. | No | Contact Wandura. | + | 422 | `4220` | `limit` is not 1–100 (an empty `limit=` counts as 0). | No | Fix the request. | + | 422 | `4221` | `updated_since` is not epoch seconds. | No | Fix the request. | + | 422 | `4222` | `cursor` is not valid. | No | Restart from your last checkpoint. | + | 429 | `rate_limited` (flat) | Creatives limit reached. | After `Retry-After` | Wait; sync less often. | + | 503 | `5030` | The feed is temporarily unavailable. | Backoff | Retry; keep your checkpoint. | + | 503 | `5031` | Wandura could not check the key in time. | Backoff | Retry. Do not rotate the key. | - ## Getting help + ### Get a creative {#creative-get-errors} - If an error persists, contact support with the details listed in [Support](../help/support.md). + `GET /api/delivery/v1/creatives/{id}` + + | Status | Code | When | Retry? | Your backend does | + |---|---|---|---|---| + | 400 | `unsupported_version` (flat) | Unknown or retired dated version. | No | Send a supported version. | + | 401 | `4010` | No `Authorization: Bearer` header. | No | Fix the request. | + | 401 | `4011` | Key unknown or revoked. | No | Check the key. | + | 403 | `4030` | The key lacks `creatives:export`. | No | Use a key with that scope. | + | 403 | `4031` | Your organisation is suspended. | No | Contact Wandura. | + | 404 | `4040` | No approved creative with this ID for your organisation (also: the ID is not a UUID). | No | Remove it from your active set. | + | 429 | `rate_limited` (flat) | Creatives limit reached (shared with the list). | After `Retry-After` | Wait. | + | 503 | `5030` | The feed is temporarily unavailable. | Backoff | Retry. | + | 503 | `5031` | Wandura could not check the key in time. | Backoff | Retry. | + + ### List open videos {#videos-errors} + + `GET /api/delivery/v1/videos` + + | Status | Code | When | Retry? | Your backend does | + |---|---|---|---|---| + | 400 | none | `cursor` is not a token this endpoint issued, or `limit` is not 1–200. | No | Fix the request; restart the sweep. | + | 400 | `unsupported_version` | Unknown or retired dated version. | No | Send a supported version. | + | 401 | none | Header missing, or key unknown or revoked. | No | Check the key. | + | 403 | none | The key lacks `videos:read`. | No | Use the app's open video key. | + | 403 | `app_not_registered` (in `error`) | The key is not bound to an app. | No | Register the app; use that app's key. | + | 403 | `org_suspended` (in `error`) | Your organisation is suspended. | No | Contact Wandura. | + | 404 | `collection_not_found` (in `error`) | Unknown, unpublished or not granted to you. | No | Check the collection code. | + | 429 | `rate_limited` | List limit reached. | After `Retry-After` | Wait; cache the list. | + | 500 | none | The list could not be read. | Backoff | Retry; keep serving your previous list. | + | 503 | `service_unavailable` | Wandura could not check the key in time. | After `Retry-After` | Retry. | + + ### Get one open video {#video-errors} + + `GET /api/delivery/v1/videos/{id}` + + | Status | Code | When | Retry? | Your backend does | + |---|---|---|---|---| + | 400 | none | `id` is not a UUID. | No | Use the `id` from the list. | + | 400 | `unsupported_version` | Unknown or retired dated version. | No | Send a supported version. | + | 401 | none | Header missing, or key unknown or revoked. | No | Check the key. | + | 403 | none | The key lacks `videos:read`. | No | Use the app's open video key. | + | 403 | `app_not_registered` (in `error`) | The key is not bound to an app. | No | Register the app. | + | 403 | `org_suspended` (in `error`) | Your organisation is suspended. | No | Contact Wandura. | + | 404 | `video_not_found` (in `error`) | Unknown, not granted, unpublished, archived or not playable yet. | No | Drop it from your list. | + | 429 | `rate_limited` | Single-video limit reached (its own counter). | After `Retry-After` | Wait; prefer the cached list. | + | 500 | none | The video could not be read. | Backoff | Retry. | + | 503 | `service_unavailable` | Wandura could not check the key in time. | After `Retry-After` | Retry. | + + ## Related {#related} + + - [Canonical API behaviour](../reference/behaviour.md): retry policy, rate limits, freshness. + - [Troubleshooting](../production/troubleshooting.md): from a symptom to a fix. + - [Support](../help/support.md): what to send when an error persists.
- 2 Oct 2026 · +1 −1 lines
Show changes
⋯ | `502`, `503`, other `5xx` | Yes, with exponential backoff and a cap, for example 1 s, 2 s, 4 s, then give up. | - During playback, request a fresh ticket once when the Player SDK fails, then follow this table. See [Playback](playback.md#expiry). + During playback, request a fresh ticket once when the Player SDK fails, then follow this table. See [Playback](playback.md#expiry). For jitter, a retry budget and sample code, see [Smooth playback under load](../guides/smooth-playback.md#retry). ## Getting help ⋯
- 1 Oct 2026 · +2 −1 lines
Show changes
⋯ | 400 | `invalid_device_info` | A device value looks empty or invalid, for example the string `"(null)"`. `fields` names the bad values. | Fix how your app reads the values from the Player SDK. | | 401 | none | Missing, invalid or revoked API key. | Check the key. Do not retry. | - | 403 | none | The key lacks the `playback` scope. | Use the playback key Wandura issued to you. | + | 403 | none | The key lacks the `playback` scope. | Use a playback key: create one on **Developers** → **Stream films in an app**. | | 403 | `not_entitled` | Your organisation is not licensed to stream this film right now. `reason` gives the detail. | Show your locked or unavailable state. Do not retry. | | 404 | `not_ready` | The episode or film is unknown, not published, or not ready to stream. | Show "not available yet". Refresh your catalog. | ⋯ | 503 | `not_ready` | No playable version of this episode is available right now. | Show "not available yet". Retry later. | | 503 | `service_unavailable` | Wandura could not check the API key in time. The key is **not** wrong. | Retry after `Retry-After` seconds, with backoff. Do not rotate the key. | + | 503 | `service_unavailable` | The video provider did not answer in time (playback only). Nothing is wrong with the episode or your key. | Retry after `Retry-After` seconds (2), with backoff. | ### `not_entitled` reasons ⋯
- 1 Oct 2026 · +1 −1 lines
Show changes
⋯ | Field | Type | Description | |---|---|---| - | `error` | string | Human-readable message. Log it, but do not parse it. | + | `error` | string | Human-readable message. Log it, but do not parse it, except for the catalog `403` below. | | `error_code` | string | Machine-readable code. Branch on this. Absent on some errors, listed below as "none". | | `reason` | string | Only with `not_entitled`: the detailed reason, for logs and support. | ⋯
- 1 Oct 2026 · +2 −2 lines
Show changes
⋯ | 401 | none | `Invalid or revoked API key` | The key is unknown or revoked. | Check the key. Do not retry. | | 403 | none | `This API key does not have the metadata:read scope` | The key lacks the scope. | Use a key with `metadata:read`. | - | 403 | none | `org_suspended` | Your organisation's access is suspended. | Contact your Wandura partner contact. Do not retry. | + | 403 | none | `org_suspended` | Your organisation's access is suspended. | Contact Wandura. Do not retry. | | 429 | `rate_limited` | | Rate limit reached. | Wait for `Retry-After`. | | 503 | `service_unavailable` | `Could not verify the API key right now — retry` | Wandura could not check the key in time. The key is **not** wrong. | Retry after `Retry-After` seconds, with backoff. Do not rotate the key. | ⋯ | 401 | 4011 | `Invalid or revoked API key` | The key is unknown or revoked. | Check the key. Do not retry. | | 403 | 4030 | `This API key does not have the creatives:export scope` | The key lacks the scope. | Use a key with `creatives:export`. | - | 403 | 4031 | `org_suspended` | Your organisation's access is suspended. | Contact your Wandura partner contact. | + | 403 | 4031 | `org_suspended` | Your organisation's access is suspended. | Contact Wandura. | | 404 | 4040 | `Creative not found` | `GET /creatives/{id}` only. The creative does not exist, belongs to another organisation or is not approved. | Remove it from your active set. | | 422 | 4220 | `limit must be an integer between 1 and 100` | List only. Bad `limit`. | Fix the request. | ⋯
- 30 Sept 2026 · +3 −0 lines
Show changes
⋯ | 403 | none | `org_suspended` | Your organisation's access is suspended. | Contact your Wandura partner contact. Do not retry. | | 429 | `rate_limited` | | Rate limit reached. | Wait for `Retry-After`. | + | 503 | `service_unavailable` | `Could not verify the API key right now — retry` | Wandura could not check the key in time. The key is **not** wrong. | Retry after `Retry-After` seconds, with backoff. Do not rotate the key. | ## Playback errors ⋯ | 502 | none | Wandura could not prepare the stream or sign the play token. | Retry with backoff. | | 503 | `not_ready` | No playable version of this episode is available right now. | Show "not available yet". Retry later. | + | 503 | `service_unavailable` | Wandura could not check the API key in time. The key is **not** wrong. | Retry after `Retry-After` seconds, with backoff. Do not rotate the key. | ### `not_entitled` reasons ⋯ | 429 | none (flat body, `error_code` `rate_limited`) | | Rate limit reached. | Wait for `Retry-After`. | | 503 | 5030 | `The creative feed is temporarily unavailable` | Temporary problem on Wandura's side. | Retry with backoff. | + | 503 | 5031 | `Could not verify the API key right now — retry` | Wandura could not check the key in time. The key is **not** wrong. | Retry with backoff. Do not rotate the key. | ## Retry guidance ⋯
- 29 Sept 2026 · Page created