{
  "openapi": "3.0.3",
  "info": {
    "title": "Wandura Delivery API",
    "version": "1.0.0",
    "description": "Server-to-server API for Wandura partners: read the films you are licensed for, request playback, sync approved ad creatives and list open video. Every path is versioned (`/api/delivery/v1/`). Within a version Wandura only ADDS: new endpoints, new optional request fields, new response fields, new enum values and new error codes may appear without notice, so ignore fields you do not recognise and keep a default branch for unknown values. The API is in beta: every response carries the header `Wandura-API-Status: beta`. Any other path under `/api/delivery/v1/` answers 404 with `error_code` `not_found`. Guides: https://docs.wandura.ai/developers",
    "x-description-vi": "API máy chủ-tới-máy chủ cho đối tác Wandura: đọc các phim bạn được cấp phép, xin playback, đồng bộ creative quảng cáo đã duyệt và liệt kê video mở. Mọi đường dẫn đều có phiên bản (`/api/delivery/v1/`). Trong một phiên bản Wandura chỉ THÊM: endpoint mới, trường request tuỳ chọn mới, trường response mới, giá trị enum mới và mã lỗi mới có thể xuất hiện không báo trước, nên hãy bỏ qua trường lạ và giữ nhánh mặc định cho giá trị lạ. API đang beta: mọi response có header `Wandura-API-Status: beta`. Đường dẫn khác dưới `/api/delivery/v1/` trả 404 với `error_code` `not_found`. Hướng dẫn: https://docs.wandura.ai/vi/developers"
  },
  "servers": [
    {
      "url": "https://www.wandura.ai",
      "description": "Production. Always use www: the bare domain redirects with 308."
    }
  ],
  "security": [
    {
      "deliveryApiKey": []
    }
  ],
  "tags": [
    {
      "name": "catalog"
    },
    {
      "name": "config"
    },
    {
      "name": "creatives"
    },
    {
      "name": "playback"
    },
    {
      "name": "playlists"
    },
    {
      "name": "series"
    },
    {
      "name": "videos"
    }
  ],
  "paths": {
    "/api/delivery/v1/catalog": {
      "get": {
        "operationId": "listCatalog",
        "x-scope": "metadata:read",
        "tags": [
          "catalog"
        ],
        "security": [
          {
            "deliveryApiKey": []
          }
        ],
        "summary": "List licensed catalog",
        "x-summary-vi": "Lấy catalog được cấp phép",
        "description": "Returns every film and episode your organisation may stream right now. Metadata only: never a video URL — request playback per episode with POST /api/delivery/v1/playback. A film is included when your organisation has an active streaming licence for it, today (UTC) is inside the licence window, and the film is published. For each included film, all published episodes are returned, plus a placeholder row for each planned episode that is not published yet. The response may be up to 30 seconds old: poll on a schedule (minutes), not faster. Pagination is optional: without `limit` and `cursor` the whole catalog comes back in one response; with `limit` you get that many films per page, the episodes of those films, `next_cursor` and `links.next`. `links` carries ready-made absolute URLs, and each episode has `poster_url`, a directly loadable image URL or `null`. Each film has `images`, its artwork by kind (`poster_3_4`, `cover_9_16`, …) with size in pixels. Each film also carries partner metadata: `code`, titles and synopses by language (`*_i18n`), `content_rating`, `is_completed`, `release_date`, `default_lang`, coded `tags` (one main genre), `people`, `trailer`, and `metadata_version` / `metadata_updated_at`; each episode has `title_i18n` and its subtitle languages. Missing values are `null` or empty and never block a licence. Your CMS may override any field; Wandura never overwrites your edits.",
        "x-description-vi": "Trả về mọi phim và tập mà tổ chức của bạn đang được phép stream. Chỉ có metadata: không bao giờ có URL video — xin playback từng tập bằng POST /api/delivery/v1/playback. Một phim có mặt khi tổ chức của bạn có giấy phép stream đang hiệu lực, hôm nay (UTC) nằm trong thời hạn giấy phép, và phim đã publish. Với mỗi phim, mọi tập đã publish được trả về, kèm một dòng placeholder cho mỗi tập dự kiến chưa publish. Response có thể cũ tối đa 30 giây: hãy gọi theo lịch (vài phút một lần), đừng gọi dày hơn. Chia trang là tuỳ chọn: không gửi `limit` và `cursor` thì nhận cả catalog trong một response; gửi `limit` thì nhận ngần ấy phim mỗi trang, các tập của chúng, `next_cursor` và `links.next`. `links` mang URL tuyệt đối dựng sẵn, và mỗi tập có `poster_url`, URL ảnh tải thẳng được hoặc `null`. Mỗi phim có `images`, ảnh của phim theo loại (`poster_3_4`, `cover_9_16`, …) kèm kích thước pixel. Mỗi phim còn có metadata cho đối tác: `code`, tên và tóm tắt theo ngôn ngữ (`*_i18n`), `content_rating`, `is_completed`, `release_date`, `default_lang`, `tags` có mã (một thể loại chính), `people`, `trailer`, và `metadata_version` / `metadata_updated_at`; mỗi tập có `title_i18n` và các ngôn ngữ phụ đề. Giá trị thiếu là `null` hoặc rỗng và không bao giờ chặn giấy phép. CMS của bạn được sửa đè bất kỳ trường nào; Wandura không bao giờ ghi đè bản sửa của bạn.",
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "curl",
            "source": "curl -s https://www.wandura.ai/api/delivery/v1/catalog \\\n  -H \"Authorization: Bearer $WANDURA_API_KEY\""
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            },
            "description": "Optional. Films per page, 1 to 200. Omit it (and `cursor`) to receive the whole catalog in one response.",
            "x-description-vi": "Tuỳ chọn. Số phim mỗi trang, từ 1 đến 200. Bỏ trống (cùng `cursor`) để nhận cả catalog trong một response."
          },
          {
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional. The `next_cursor` (or the cursor inside `links.next`) of the previous page. Opaque; pass it back unchanged. With a cursor and no `limit` the page size is 50.",
            "x-description-vi": "Tuỳ chọn. `next_cursor` (hoặc cursor trong `links.next`) của trang trước. Opaque; truyền lại nguyên vẹn. Có cursor mà không có `limit` thì mỗi trang 50 phim."
          },
          {
            "$ref": "#/components/parameters/WanduraVersionHeader"
          },
          {
            "$ref": "#/components/parameters/WanduraSimulateHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "Licensed catalog. Both arrays are empty when your organisation has no current licence.",
            "x-description-vi": "Catalog được cấp phép. Cả hai mảng đều rỗng khi tổ chức của bạn không có giấy phép nào đang hiệu lực.",
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationLink"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Catalog"
                },
                "example": {
                  "films": [
                    {
                      "id": "3f6c1a52-8d0e-4b7a-9c21-5e4f2a7b9d10",
                      "title": "The Heiress Returns",
                      "slug": "the-heiress-returns",
                      "synopsis_short": "Cast out on her wedding day, a disowned heiress comes back to take everything.",
                      "synopsis_full": "Cast out on her wedding day, Mia loses her family, her name and her fortune. Five years later she returns with a new identity and one goal.",
                      "genres": [
                        "romance",
                        "revenge"
                      ],
                      "language": "en",
                      "poster_url": "https://cdn.example.com/posters/the-heiress-returns.jpg",
                      "status": "published",
                      "display_order": 10,
                      "planned_episodes": 3,
                      "free_until_episode": 1,
                      "audience": "women",
                      "form": "live_action",
                      "setting": "modern",
                      "code": "WD000016",
                      "title_i18n": {
                        "en": "The Heiress Returns"
                      },
                      "synopsis_short_i18n": {
                        "en": "Cast out on her wedding day, a disowned heiress comes back to take everything."
                      },
                      "synopsis_i18n": {
                        "en": "Cast out on her wedding day, Mia loses her family, her name and her fortune. Five years later she returns with a new identity and one goal."
                      },
                      "content_rating": "13+",
                      "is_completed": false,
                      "release_date": null,
                      "default_lang": "en",
                      "tags": [
                        {
                          "slug": "revenge",
                          "name": "Revenge",
                          "name_i18n": {
                            "en": "Revenge"
                          },
                          "type": "genre",
                          "is_main": true
                        },
                        {
                          "slug": "romance",
                          "name": "Romance",
                          "name_i18n": {
                            "en": "Romance"
                          },
                          "type": "genre",
                          "is_main": false
                        },
                        {
                          "slug": "heiress",
                          "name": "Heiress",
                          "name_i18n": {
                            "en": "Heiress"
                          },
                          "type": "theme",
                          "is_main": false
                        }
                      ],
                      "people": [
                        {
                          "name": "Mia",
                          "role": "lead",
                          "description": "A disowned heiress back for what is hers.",
                          "avatar_url": null
                        }
                      ],
                      "trailer": null,
                      "metadata_version": "9a0b1c2d3e4f5061728394a5b6c7d8e9",
                      "metadata_updated_at": "2026-10-03T09:00:00.000Z",
                      "images": [
                        {
                          "kind": "poster_3_4",
                          "lang": null,
                          "url": "https://cdn.example.com/posters/the-heiress-returns-3x4.jpg",
                          "width": 1080,
                          "height": 1440
                        },
                        {
                          "kind": "cover_9_16",
                          "lang": null,
                          "url": "https://cdn.example.com/posters/the-heiress-returns.jpg",
                          "width": 1080,
                          "height": 1920
                        }
                      ]
                    }
                  ],
                  "episodes": [
                    {
                      "id": "b2a4d6e8-1c3f-4a5b-8d7e-9f0a1b2c3d4e",
                      "film_id": "3f6c1a52-8d0e-4b7a-9c21-5e4f2a7b9d10",
                      "episode_no": 1,
                      "title": "Episode 1",
                      "synopsis": null,
                      "duration_ms": 94000,
                      "poster_uri": null,
                      "status": "published",
                      "stream_ready": true,
                      "title_i18n": {
                        "en": "Episode 1"
                      },
                      "subtitles": [
                        {
                          "lang": "en"
                        }
                      ]
                    },
                    {
                      "id": "c7d9e1f3-5a6b-4c8d-9e0f-1a2b3c4d5e6f",
                      "film_id": "3f6c1a52-8d0e-4b7a-9c21-5e4f2a7b9d10",
                      "episode_no": 2,
                      "title": "Episode 2",
                      "synopsis": null,
                      "duration_ms": 101000,
                      "poster_uri": null,
                      "status": "published",
                      "stream_ready": false
                    },
                    {
                      "id": null,
                      "film_id": "3f6c1a52-8d0e-4b7a-9c21-5e4f2a7b9d10",
                      "episode_no": 3,
                      "title": null,
                      "synopsis": null,
                      "duration_ms": null,
                      "poster_uri": null,
                      "status": "planned",
                      "stream_ready": false
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "A query parameter or the requested API version is invalid.",
            "x-description-vi": "Một tham số query hoặc phiên bản API được yêu cầu không hợp lệ.",
            "x-error-codes": [
              {
                "error_code": "invalid_request",
                "description": "`limit` is not a whole number from 1 to 200, or `cursor` is not a value this endpoint returned.",
                "x-description-vi": "`limit` không phải số nguyên từ 1 đến 200, hoặc `cursor` không phải giá trị endpoint này trả về."
              },
              {
                "error_code": "unsupported_version",
                "description": "The `Wandura-Version` header, or the version pinned to your key, is unknown or retired. The body adds `supported_versions` and `latest_version` (and `upgrade_to` when your pin was retired).",
                "x-description-vi": "Header `Wandura-Version`, hoặc bản ghim của key, là bản lạ hay đã ngừng. Body có thêm `supported_versions` và `latest_version` (và `upgrade_to` khi bản ghim đã ngừng)."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked API key.",
            "x-description-vi": "Thiếu API key, key không tồn tại hoặc đã bị thu hồi.",
            "x-error-codes": [
              {
                "error_code": null,
                "description": "The `Authorization: Bearer` header is missing, or the key is unknown or revoked.",
                "x-description-vi": "Thiếu header `Authorization: Bearer`, hoặc key không tồn tại hay đã bị thu hồi."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, or your organisation is suspended. These two carry no `error_code`; tell them apart by comparing the whole `error` string with `org_suspended`.",
            "x-description-vi": "Key thiếu scope, hoặc tổ chức của bạn bị tạm ngưng. Hai trường hợp này không có `error_code`; phân biệt bằng cách so nguyên chuỗi `error` với `org_suspended`.",
            "x-error-codes": [
              {
                "error_code": null,
                "description": "The key lacks the `metadata:read` scope.",
                "x-description-vi": "Key thiếu scope `metadata:read`."
              },
              {
                "error": "org_suspended",
                "description": "Your organisation's access is suspended.",
                "x-description-vi": "Quyền truy cập của tổ chức bạn đang bị tạm ngưng."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "429": {
            "description": "Abuse ceiling for your organisation reached (3000 catalog requests per 60-second window). Wait `Retry-After` seconds — the allowance refills continuously, so this is usually 1–2 seconds rather than the rest of a minute.",
            "x-description-vi": "Đã chạm trần chống lạm dụng của tổ chức bạn (3000 request catalog mỗi cửa sổ 60 giây). Chờ số giây trong `Retry-After` — hạn mức được nạp lại liên tục, nên thường chỉ 1–2 giây chứ không phải phần còn lại của một phút.",
            "x-error-codes": [
              {
                "error_code": "rate_limited",
                "description": "The rate limit for the catalog was reached.",
                "x-description-vi": "Đã chạm rate limit của catalog."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Window": {
                "$ref": "#/components/headers/RateLimitWindow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "503": {
            "description": "Wandura could not verify the API key, or could not read the catalog, in time. Your request and key are fine — retry after `Retry-After` seconds, with backoff. An empty `films` list on a 200 always means you have no licensed titles, never an outage.",
            "x-description-vi": "Wandura chưa kiểm được API key, hoặc chưa đọc được catalog, kịp thời. Request và key của bạn không sai — thử lại sau số giây trong `Retry-After`, có backoff. `films` rỗng trong một câu 200 luôn có nghĩa là bạn chưa có phim nào được cấp quyền, không bao giờ là sự cố.",
            "x-error-codes": [
              {
                "error_code": "service_unavailable",
                "description": "Wandura could not verify the API key or read the catalog in time. Nothing is wrong on your side: retry after `Retry-After` seconds, with backoff.",
                "x-description-vi": "Wandura chưa kiểm được API key hoặc chưa đọc được catalog kịp thời. Phía bạn không sai gì: thử lại sau số giây trong `Retry-After`, có backoff."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          }
        }
      }
    },
    "/api/delivery/v1/config": {
      "get": {
        "operationId": "getConfig",
        "x-scope": "any",
        "tags": [
          "config"
        ],
        "security": [
          {
            "deliveryApiKey": []
          }
        ],
        "summary": "Get configuration",
        "x-summary-vi": "Lấy cấu hình",
        "description": "Returns the configuration that applies to the calling API key and its organisation: the rate limits that apply to you, your playback domains and protection levels, how many episodes one playback call may request, the subtitle languages on your films, the API versions (pinned, latest, supported) and every announced deprecation. Read it when your backend starts, or every few hours, instead of hard-coding these values. Callable with any valid API key, whatever its scopes. Contains nothing secret. Ignore fields you do not recognise.",
        "x-description-vi": "Trả về cấu hình áp cho API key đang gọi và tổ chức của nó: rate limit đang áp cho bạn, tên miền phát và mức bảo vệ, một lần gọi playback xin được bao nhiêu tập, ngôn ngữ phụ đề trên phim của bạn, các phiên bản API (đang ghim, mới nhất, còn hỗ trợ) và mọi thông báo ngừng hỗ trợ. Đọc nó khi backend khởi động, hoặc vài giờ một lần, thay vì viết cứng các giá trị này. Gọi được bằng mọi API key hợp lệ, bất kể scope. Không chứa gì bí mật. Bỏ qua các trường bạn không nhận ra.",
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "curl",
            "source": "curl -s https://www.wandura.ai/api/delivery/v1/config \\\n  -H \"Authorization: Bearer $WANDURA_API_KEY\""
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/WanduraVersionHeader"
          },
          {
            "$ref": "#/components/parameters/WanduraSimulateHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "Your configuration.",
            "x-description-vi": "Cấu hình của bạn.",
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationLink"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryConfig"
                },
                "example": {
                  "api": {
                    "status": "beta",
                    "path_version": "v1",
                    "version": "2026-10-02",
                    "pinned_version": "2026-10-02",
                    "latest_version": "2026-10-02",
                    "version_header": "Wandura-Version",
                    "supported_versions": [
                      {
                        "version": "2026-10-02",
                        "released_at": "2026-10-02",
                        "deprecated_at": null,
                        "sunset_at": null,
                        "summary": "First dated version: the /v1 response shapes as of 2 October 2026."
                      }
                    ]
                  },
                  "key": {
                    "scopes": [
                      "metadata:read",
                      "playback"
                    ]
                  },
                  "rate_limits": {
                    "window_seconds": 60,
                    "doors": {
                      "catalog": {
                        "limit": 3000,
                        "unit": "requests"
                      },
                      "playback": {
                        "limit": 10800,
                        "unit": "tickets"
                      },
                      "creatives": {
                        "limit": 3000,
                        "unit": "requests"
                      },
                      "videos": {
                        "limit": 3000,
                        "unit": "requests"
                      },
                      "config": {
                        "limit": 600,
                        "unit": "requests"
                      }
                    }
                  },
                  "playback": {
                    "domains": [
                      "playback.example.com"
                    ],
                    "protection": [
                      "private-encrypted"
                    ],
                    "grant_ttl_seconds": 1800,
                    "manifest_url_ttl_seconds": 900,
                    "batch": {
                      "max_episodes": 3,
                      "max_next": 2
                    }
                  },
                  "subtitles": {
                    "format": "webvtt",
                    "languages": [
                      "en"
                    ]
                  },
                  "deprecations": [],
                  "links": {
                    "self": "https://www.wandura.ai/api/delivery/v1/config",
                    "catalog": "https://www.wandura.ai/api/delivery/v1/catalog",
                    "playback": "https://www.wandura.ai/api/delivery/v1/playback",
                    "creatives": "https://www.wandura.ai/api/delivery/v1/creatives",
                    "videos": "https://www.wandura.ai/api/delivery/v1/videos",
                    "config": "https://www.wandura.ai/api/delivery/v1/config",
                    "docs": "https://docs.wandura.ai/developers/reference/overview",
                    "evolution": "https://docs.wandura.ai/developers/concepts/api-evolution",
                    "changes": "https://docs.wandura.ai/changes.json"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The requested API version is unknown or retired.",
            "x-description-vi": "Phiên bản API được yêu cầu là bản lạ hoặc đã ngừng.",
            "x-error-codes": [
              {
                "error_code": "unsupported_version",
                "description": "The `Wandura-Version` header, or the version pinned to your key, is unknown or retired. The body adds `supported_versions` and `latest_version` (and `upgrade_to` when your pin was retired).",
                "x-description-vi": "Header `Wandura-Version`, hoặc bản ghim của key, là bản lạ hay đã ngừng. Body có thêm `supported_versions` và `latest_version` (và `upgrade_to` khi bản ghim đã ngừng)."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked API key.",
            "x-description-vi": "Thiếu API key, key không tồn tại hoặc đã bị thu hồi.",
            "x-error-codes": [
              {
                "error_code": null,
                "description": "The `Authorization: Bearer` header is missing, or the key is unknown or revoked.",
                "x-description-vi": "Thiếu header `Authorization: Bearer`, hoặc key không tồn tại hay đã bị thu hồi."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "403": {
            "description": "Your organisation is suspended.",
            "x-description-vi": "Tổ chức của bạn đang bị tạm ngưng.",
            "x-error-codes": [
              {
                "error": "org_suspended",
                "description": "Your organisation's access is suspended.",
                "x-description-vi": "Quyền truy cập của tổ chức bạn đang bị tạm ngưng."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "429": {
            "description": "Abuse ceiling for your organisation reached (600 configuration requests per 60-second window). Wait `Retry-After` seconds.",
            "x-description-vi": "Đã chạm trần chống lạm dụng của tổ chức bạn (600 request cấu hình mỗi cửa sổ 60 giây). Chờ số giây trong `Retry-After`.",
            "x-error-codes": [
              {
                "error_code": "rate_limited",
                "description": "The rate limit for the configuration endpoint was reached.",
                "x-description-vi": "Đã chạm rate limit của endpoint cấu hình."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Window": {
                "$ref": "#/components/headers/RateLimitWindow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "503": {
            "description": "Wandura could not verify the API key in time. The key is not wrong — retry after `Retry-After` seconds, with backoff.",
            "x-description-vi": "Wandura chưa kiểm được API key kịp thời. Key không sai — thử lại sau số giây trong `Retry-After`, có backoff.",
            "x-error-codes": [
              {
                "error_code": "service_unavailable",
                "description": "Wandura could not verify the API key in time. The key is not wrong: retry after `Retry-After` seconds, with backoff.",
                "x-description-vi": "Wandura chưa kiểm được API key kịp thời. Key không sai: thử lại sau số giây trong `Retry-After`, có backoff."
              },
              {
                "error_code": "service_unavailable",
                "description": "Wandura could not read your configuration in time (`Could not read your configuration in time — retry`). Retry after `Retry-After` seconds, with backoff.",
                "x-description-vi": "Wandura chưa đọc được cấu hình của bạn kịp thời (`Could not read your configuration in time — retry`). Thử lại sau số giây trong `Retry-After`, có backoff."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          }
        }
      }
    },
    "/api/delivery/v1/creatives": {
      "get": {
        "operationId": "listCreatives",
        "x-scope": "creatives:export",
        "tags": [
          "creatives"
        ],
        "security": [
          {
            "deliveryApiKey": []
          }
        ],
        "summary": "List creatives",
        "x-summary-vi": "Liệt kê creative",
        "description": "Returns a page of approved creatives of your organisation, each as a full data record — use it to sync creatives into your marketing systems, incrementally with `updated_since`. Returns creatives with status `approved` or `pushed`, ordered by `updated_at` ascending and then by ID. Records never contain a media URL. Every response, success or error (except 429), is wrapped in the `{code, message, data, server_time}` envelope.",
        "x-description-vi": "Trả về một trang các creative đã duyệt của tổ chức bạn, mỗi creative là một bản ghi dữ liệu đầy đủ — dùng để đồng bộ creative vào hệ thống marketing, theo kiểu incremental với `updated_since`. Trả về creative có status `approved` hoặc `pushed`, sắp theo `updated_at` tăng dần rồi theo ID. Bản ghi không bao giờ chứa URL media. Mọi response, dù thành công hay lỗi (trừ 429), đều được bọc trong envelope `{code, message, data, server_time}`.",
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "curl",
            "source": "curl -s \"https://www.wandura.ai/api/delivery/v1/creatives?updated_since=1790600000&limit=2\" \\\n  -H \"Authorization: Bearer $WANDURA_API_KEY\""
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "updated_since",
            "schema": {
              "type": "integer"
            },
            "description": "Unix epoch seconds (up to 11 digits). Returns only records whose exact update time is strictly after this second (`>`, not `>=`).",
            "x-description-vi": "Giây Unix epoch (tối đa 11 chữ số). Chỉ trả về bản ghi có thời điểm cập nhật chính xác lớn hơn hẳn giây này (`>`, không phải `>=`)."
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            },
            "description": "The `next_cursor` from the previous page. Opaque; pass it back unchanged.",
            "x-description-vi": "`next_cursor` của trang trước. Opaque; truyền lại nguyên vẹn."
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Records per page, 1 to 100. Default `50`.",
            "x-description-vi": "Số bản ghi mỗi trang, từ 1 đến 100. Mặc định `50`."
          },
          {
            "$ref": "#/components/parameters/WanduraVersionHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of creative records.",
            "x-description-vi": "Một trang bản ghi creative.",
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationLink"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "code",
                    "message",
                    "data",
                    "server_time"
                  ],
                  "properties": {
                    "code": {
                      "type": "integer",
                      "description": "`0`.",
                      "x-description-vi": "`0`."
                    },
                    "message": {
                      "type": "string",
                      "description": "`\"ok\"`.",
                      "x-description-vi": "`\"ok\"`."
                    },
                    "data": {
                      "type": "object",
                      "required": [
                        "items",
                        "next_cursor"
                      ],
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "The creative records on this page.",
                          "x-description-vi": "Các bản ghi creative trên trang này.",
                          "items": {
                            "$ref": "#/components/schemas/CreativeRecord"
                          }
                        },
                        "next_cursor": {
                          "type": "string",
                          "nullable": true,
                          "description": "Pass it as `cursor` to get the next page. `null` on the last page.",
                          "x-description-vi": "Truyền làm `cursor` để lấy trang kế tiếp. `null` ở trang cuối."
                        }
                      }
                    },
                    "server_time": {
                      "type": "integer",
                      "description": "Server time in Unix epoch seconds.",
                      "x-description-vi": "Giờ server tính bằng giây Unix epoch."
                    },
                    "links": {
                      "description": "Ready-made absolute URLs of this page — follow `links.next` instead of building the next-page URL.",
                      "x-description-vi": "URL tuyệt đối dựng sẵn của trang này — đi theo `links.next` thay vì tự ráp URL trang kế.",
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/PageLinks"
                        }
                      ]
                    }
                  }
                },
                "example": {
                  "code": 0,
                  "message": "ok",
                  "data": {
                    "items": [
                      {
                        "schema_version": 1,
                        "creative_id": "5e1b7c3a-2d4f-4a6b-9c8d-0e1f2a3b4c5d",
                        "creative_code": "ACV001-FILM0001-EP1-2-F1-TEAM",
                        "name": "Heiress returns - wedding hook",
                        "status": "approved",
                        "created_at": 1790500000,
                        "approved_at": 1790590000,
                        "updated_at": 1790610000,
                        "org": {
                          "slug": "your-org"
                        },
                        "film": {
                          "code": "FILM0001",
                          "title": "The Heiress Returns",
                          "language": "en",
                          "genres": [
                            "romance",
                            "revenge"
                          ]
                        },
                        "episodes": [
                          1,
                          2
                        ],
                        "placement": "reels_9x16",
                        "aspect": "9:16",
                        "duration_ms": 21500,
                        "scenes": [
                          {
                            "episode_no": 1,
                            "start_ms": 12000,
                            "end_ms": 19500,
                            "duration_ms": 7500,
                            "action": "reveal",
                            "line": "You threw me out. Now watch me take it all back."
                          },
                          {
                            "episode_no": 2,
                            "start_ms": 40000,
                            "end_ms": 54000,
                            "duration_ms": 14000,
                            "action": null,
                            "line": null
                          }
                        ],
                        "hook": {
                          "action": "reveal",
                          "line": "You threw me out. Now watch me take it all back."
                        },
                        "formula": {
                          "name": "Cliffhanger opener"
                        },
                        "rights": {
                          "ends_at": 1798675200
                        },
                        "lineage": {
                          "parent_creative_id": null
                        },
                        "final": {
                          "ready": true,
                          "target": {
                            "width": 1080,
                            "height": 1920,
                            "fps": 30,
                            "codec": "h264"
                          }
                        }
                      }
                    ],
                    "next_cursor": "eyJwIjoiZXhhbXBsZS1jdXJzb3IifQ"
                  },
                  "server_time": 1790679032
                }
              }
            }
          },
          "400": {
            "description": "The requested API version is unknown or retired. This error uses the flat error body, not the creatives envelope.",
            "x-description-vi": "Phiên bản API được yêu cầu là bản lạ hoặc đã ngừng. Lỗi này dùng body lỗi phẳng, không phải envelope của creatives.",
            "x-error-codes": [
              {
                "error_code": "unsupported_version",
                "description": "The `Wandura-Version` header, or the version pinned to your key, is unknown or retired. The body adds `supported_versions` and `latest_version` (and `upgrade_to` when your pin was retired).",
                "x-description-vi": "Header `Wandura-Version`, hoặc bản ghim của key, là bản lạ hay đã ngừng. Body có thêm `supported_versions` và `latest_version` (và `upgrade_to` khi bản ghim đã ngừng)."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked API key.",
            "x-description-vi": "Thiếu API key, key không tồn tại hoặc đã bị thu hồi.",
            "x-error-codes": [
              {
                "code": 4010,
                "description": "The `Authorization: Bearer` header is missing.",
                "x-description-vi": "Thiếu header `Authorization: Bearer`."
              },
              {
                "code": 4011,
                "description": "The key is unknown or revoked.",
                "x-description-vi": "Key không tồn tại hoặc đã bị thu hồi."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks `creatives:export`, or your organisation is suspended.",
            "x-description-vi": "Key thiếu `creatives:export`, hoặc tổ chức của bạn bị tạm ngưng.",
            "x-error-codes": [
              {
                "code": 4030,
                "description": "The key lacks the `creatives:export` scope.",
                "x-description-vi": "Key thiếu scope `creatives:export`."
              },
              {
                "code": 4031,
                "description": "Your organisation's access is suspended.",
                "x-description-vi": "Quyền truy cập của tổ chức bạn đang bị tạm ngưng."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            }
          },
          "422": {
            "description": "A query parameter is invalid.",
            "x-description-vi": "Một query parameter không hợp lệ.",
            "x-error-codes": [
              {
                "code": 4220,
                "description": "`limit` is not an integer between 1 and 100.",
                "x-description-vi": "`limit` không phải số nguyên từ 1 đến 100."
              },
              {
                "code": 4221,
                "description": "`updated_since` is not epoch seconds.",
                "x-description-vi": "`updated_since` không phải giây epoch."
              },
              {
                "code": 4222,
                "description": "`cursor` is not valid.",
                "x-description-vi": "`cursor` không hợp lệ."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            }
          },
          "429": {
            "description": "Abuse ceiling for your organisation reached (3000 creatives requests per 60-second window, separate from the catalog). Flat body, not the envelope. Wait `Retry-After` seconds.",
            "x-description-vi": "Đã chạm trần chống lạm dụng của tổ chức bạn (3000 request creatives mỗi cửa sổ 60 giây, tách khỏi catalog). Body phẳng, không phải envelope. Chờ số giây trong `Retry-After`.",
            "x-error-codes": [
              {
                "error_code": "rate_limited",
                "description": "Rate limit reached. Flat body with `error_code` `rate_limited`, not the envelope.",
                "x-description-vi": "Đã chạm rate limit. Body phẳng với `error_code` `rate_limited`, không phải envelope."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Window": {
                "$ref": "#/components/headers/RateLimitWindow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable. Retry with backoff.",
            "x-description-vi": "Tạm thời không khả dụng. Retry với backoff.",
            "x-error-codes": [
              {
                "code": 5030,
                "description": "The feed is temporarily unavailable. Retry with backoff.",
                "x-description-vi": "Feed tạm thời không khả dụng. Retry với backoff."
              },
              {
                "code": 5031,
                "description": "Wandura could not verify the API key in time. The key is not wrong: retry with backoff.",
                "x-description-vi": "Wandura chưa kiểm được API key kịp thời. Key không sai: retry với backoff."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            }
          }
        }
      }
    },
    "/api/delivery/v1/creatives/{id}": {
      "get": {
        "operationId": "getCreative",
        "x-scope": "creatives:export",
        "tags": [
          "creatives"
        ],
        "security": [
          {
            "deliveryApiKey": []
          }
        ],
        "summary": "Get a creative",
        "x-summary-vi": "Lấy một creative",
        "description": "Returns the data record of one approved creative of your organisation, wrapped in the `{code, message, data, server_time}` envelope. Use it to fetch a single creative, or to check whether a creative you stored is still available. A creative that does not exist, belongs to another organisation, or is not in status `approved` or `pushed` returns the same 404, so you cannot tell these cases apart.",
        "x-description-vi": "Trả về bản ghi dữ liệu của một creative đã duyệt của tổ chức bạn, bọc trong envelope `{code, message, data, server_time}`. Dùng để lấy một creative, hoặc kiểm một creative bạn đã lưu còn dùng được không. Creative không tồn tại, thuộc tổ chức khác, hoặc không ở status `approved` hay `pushed` đều trả cùng một 404, nên bạn không phân biệt được các trường hợp này.",
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "curl",
            "source": "curl -s https://www.wandura.ai/api/delivery/v1/creatives/5e1b7c3a-2d4f-4a6b-9c8d-0e1f2a3b4c5d \\\n  -H \"Authorization: Bearer $WANDURA_API_KEY\""
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The `creative_id` of the creative. A value that is not a UUID returns 404.",
            "x-description-vi": "`creative_id` của creative. Giá trị không phải UUID sẽ trả 404."
          },
          {
            "$ref": "#/components/parameters/WanduraVersionHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "The creative record.",
            "x-description-vi": "Bản ghi creative.",
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationLink"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "code",
                    "message",
                    "data",
                    "server_time"
                  ],
                  "properties": {
                    "code": {
                      "type": "integer",
                      "description": "`0`.",
                      "x-description-vi": "`0`."
                    },
                    "message": {
                      "type": "string",
                      "description": "`\"ok\"`.",
                      "x-description-vi": "`\"ok\"`."
                    },
                    "data": {
                      "$ref": "#/components/schemas/CreativeRecord"
                    },
                    "server_time": {
                      "type": "integer",
                      "description": "Server time in Unix epoch seconds.",
                      "x-description-vi": "Giờ server tính bằng giây Unix epoch."
                    },
                    "links": {
                      "description": "Ready-made absolute URL of this response.",
                      "x-description-vi": "URL tuyệt đối dựng sẵn của response này.",
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/SelfLink"
                        }
                      ]
                    }
                  }
                },
                "example": {
                  "code": 0,
                  "message": "ok",
                  "data": {
                    "schema_version": 1,
                    "creative_id": "5e1b7c3a-2d4f-4a6b-9c8d-0e1f2a3b4c5d",
                    "creative_code": "ACV001-FILM0001-EP1-2-F1-TEAM",
                    "name": "Heiress returns - wedding hook",
                    "status": "pushed",
                    "created_at": 1790500000,
                    "approved_at": 1790590000,
                    "updated_at": 1790640000,
                    "org": {
                      "slug": "your-org"
                    },
                    "film": {
                      "code": "FILM0001",
                      "title": "The Heiress Returns",
                      "language": "en",
                      "genres": [
                        "romance",
                        "revenge"
                      ]
                    },
                    "episodes": [
                      1,
                      2
                    ],
                    "placement": "reels_9x16",
                    "aspect": "9:16",
                    "duration_ms": 21500,
                    "scenes": [
                      {
                        "episode_no": 1,
                        "start_ms": 12000,
                        "end_ms": 19500,
                        "duration_ms": 7500,
                        "action": "reveal",
                        "line": "You threw me out. Now watch me take it all back."
                      },
                      {
                        "episode_no": 2,
                        "start_ms": 40000,
                        "end_ms": 54000,
                        "duration_ms": 14000,
                        "action": null,
                        "line": null
                      }
                    ],
                    "hook": {
                      "action": "reveal",
                      "line": "You threw me out. Now watch me take it all back."
                    },
                    "formula": {
                      "name": "Cliffhanger opener"
                    },
                    "rights": {
                      "ends_at": 1798675200
                    },
                    "lineage": {
                      "parent_creative_id": null
                    },
                    "final": {
                      "ready": true,
                      "target": {
                        "width": 1080,
                        "height": 1920,
                        "fps": 30,
                        "codec": "h264"
                      }
                    }
                  },
                  "server_time": 1790679032
                }
              }
            }
          },
          "400": {
            "description": "The requested API version is unknown or retired. This error uses the flat error body, not the creatives envelope.",
            "x-description-vi": "Phiên bản API được yêu cầu là bản lạ hoặc đã ngừng. Lỗi này dùng body lỗi phẳng, không phải envelope của creatives.",
            "x-error-codes": [
              {
                "error_code": "unsupported_version",
                "description": "The `Wandura-Version` header, or the version pinned to your key, is unknown or retired. The body adds `supported_versions` and `latest_version` (and `upgrade_to` when your pin was retired).",
                "x-description-vi": "Header `Wandura-Version`, hoặc bản ghim của key, là bản lạ hay đã ngừng. Body có thêm `supported_versions` và `latest_version` (và `upgrade_to` khi bản ghim đã ngừng)."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked API key.",
            "x-description-vi": "Thiếu API key, key không tồn tại hoặc đã bị thu hồi.",
            "x-error-codes": [
              {
                "code": 4010,
                "description": "The `Authorization: Bearer` header is missing.",
                "x-description-vi": "Thiếu header `Authorization: Bearer`."
              },
              {
                "code": 4011,
                "description": "The key is unknown or revoked.",
                "x-description-vi": "Key không tồn tại hoặc đã bị thu hồi."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks `creatives:export`, or your organisation is suspended.",
            "x-description-vi": "Key thiếu `creatives:export`, hoặc tổ chức của bạn bị tạm ngưng.",
            "x-error-codes": [
              {
                "code": 4030,
                "description": "The key lacks the `creatives:export` scope.",
                "x-description-vi": "Key thiếu scope `creatives:export`."
              },
              {
                "code": 4031,
                "description": "Your organisation's access is suspended.",
                "x-description-vi": "Quyền truy cập của tổ chức bạn đang bị tạm ngưng."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            }
          },
          "404": {
            "description": "Not found for this key.",
            "x-description-vi": "Không tìm thấy với key này.",
            "x-error-codes": [
              {
                "code": 4040,
                "description": "No approved creative with this ID exists for your organisation, or the ID is not a UUID.",
                "x-description-vi": "Không có creative đã duyệt nào với ID này cho tổ chức của bạn, hoặc ID không phải UUID."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            }
          },
          "429": {
            "description": "Abuse ceiling for your organisation reached (shared with List creatives). Flat body, not the envelope. Wait `Retry-After` seconds.",
            "x-description-vi": "Đã chạm trần chống lạm dụng của tổ chức bạn (dùng chung với Liệt kê creative). Body phẳng, không phải envelope. Chờ số giây trong `Retry-After`.",
            "x-error-codes": [
              {
                "error_code": "rate_limited",
                "description": "Rate limit reached. Flat body with `error_code` `rate_limited`, not the envelope.",
                "x-description-vi": "Đã chạm rate limit. Body phẳng với `error_code` `rate_limited`, không phải envelope."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Window": {
                "$ref": "#/components/headers/RateLimitWindow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable. Retry with backoff.",
            "x-description-vi": "Tạm thời không khả dụng. Retry với backoff.",
            "x-error-codes": [
              {
                "code": 5030,
                "description": "The feed is temporarily unavailable. Retry with backoff.",
                "x-description-vi": "Feed tạm thời không khả dụng. Retry với backoff."
              },
              {
                "code": 5031,
                "description": "Wandura could not verify the API key in time. The key is not wrong: retry with backoff.",
                "x-description-vi": "Wandura chưa kiểm được API key kịp thời. Key không sai: retry với backoff."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            }
          }
        }
      }
    },
    "/api/delivery/v1/playback": {
      "post": {
        "operationId": "requestPlayback",
        "x-scope": "playback",
        "tags": [
          "playback"
        ],
        "security": [
          {
            "deliveryApiKey": []
          }
        ],
        "summary": "Request playback",
        "x-summary-vi": "Xin playback",
        "description": "Issues a short-lived playback ticket for one episode, or for a batch of up to 3. Your backend calls this each time a viewer starts an episode and passes the result to the Player SDK on the viewer's device. Wandura checks, on every request, that your organisation is active and that your streaming licence for the episode's film is active and inside its window. Licensed episodes are encrypted and play only through the Player SDK, so send the device values `union_info` and `drm_kek`: the response then carries `vid` + `play_auth_token`, bound to that device. Call it only from your backend — the endpoint sends no CORS headers. BATCH: send `episodes` (1–3 items) or an anchor episode plus `next` (0–2 following episodes of the same film) to get up to 3 tickets in one call; the answer is then `{ items: [...] }` in request order, each item with its own `status` and either a ticket or that item's error. Request-level failures (401, 403 scope, 400, 429, 503 key check) still answer with that status and no `items`.",
        "x-description-vi": "Cấp một vé playback ngắn hạn cho một tập, hoặc cho một lô tối đa 3 tập. Backend của bạn gọi endpoint này mỗi khi người xem bắt đầu một tập và chuyển kết quả cho Player SDK trên thiết bị của người xem. Ở mọi request, Wandura kiểm tổ chức của bạn còn hoạt động và giấy phép stream phim của tập còn hiệu lực, còn trong thời hạn. Tập được cấp phép đã mã hoá và chỉ phát được qua Player SDK, nên hãy gửi giá trị thiết bị `union_info` và `drm_kek`: response khi đó có `vid` + `play_auth_token`, gắn với thiết bị đó. Chỉ gọi từ backend — endpoint không gửi header CORS. LÔ: gửi `episodes` (1–3 phần tử) hoặc một tập neo kèm `next` (0–2 tập kế tiếp của cùng phim) để nhận tới 3 vé trong một lần gọi; khi đó câu trả lời là `{ items: [...] }` theo đúng thứ tự hỏi, mỗi phần tử có `status` riêng và hoặc vé, hoặc lỗi của phần tử đó. Lỗi cấp request (401, 403 scope, 400, 429, 503 kiểm key) vẫn trả đúng status đó và không có `items`.",
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "curl",
            "source": "curl -s -X POST https://www.wandura.ai/api/delivery/v1/playback \\\n  -H \"Authorization: Bearer $WANDURA_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"film_ref\": \"the-heiress-returns\",\n    \"episode_no\": 1,\n    \"union_info\": \"<device ID from the Player SDK>\",\n    \"drm_kek\": \"<device public key from the Player SDK>\",\n    \"intent\": \"play\",\n    \"viewer_ref\": \"u_48213:dev_9a7c\"\n  }'"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/WanduraVersionHeader"
          },
          {
            "$ref": "#/components/parameters/WanduraSimulateHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "episode_id": {
                    "type": "string",
                    "format": "uuid",
                    "x-required-text": "One of `episode_id` or `film_ref` + `episode_no`",
                    "x-required-text-vi": "Một trong hai: `episode_id` hoặc `film_ref` + `episode_no`",
                    "description": "The episode's `id` from the catalog. Takes precedence if you also send `film_ref`.",
                    "x-description-vi": "`id` của tập lấy từ catalog. Được ưu tiên nếu bạn gửi kèm `film_ref`."
                  },
                  "film_ref": {
                    "type": "string",
                    "x-required-text": "With `episode_no`, if no `episode_id`",
                    "x-required-text-vi": "Cùng `episode_no`, nếu không có `episode_id`",
                    "description": "The film's `id` or `slug`.",
                    "x-description-vi": "`id` hoặc `slug` của phim."
                  },
                  "episode_no": {
                    "type": "integer",
                    "x-required-text": "With `film_ref`, if no `episode_id`",
                    "x-required-text-vi": "Cùng `film_ref`, nếu không có `episode_id`",
                    "description": "Episode number, starting at 1. Must be a JSON number, not a string.",
                    "x-description-vi": "Số tập, bắt đầu từ 1. Phải là số JSON, không phải string."
                  },
                  "union_info": {
                    "type": "string",
                    "x-required-text": "Yes, for encrypted episodes",
                    "x-required-text-vi": "Có, với tập đã mã hoá",
                    "description": "Device ID generated by the Player SDK on the viewer's device. Send together with `drm_kek`. Rejected with 400 `invalid_device_info` when, after trimming spaces, it is shorter than 8 characters or is one of `(null)`, `null`, `nil`, `undefined`, `nan`, `<null>` or `none` (any letter case).",
                    "x-description-vi": "Device ID do Player SDK tạo ra trên thiết bị của người xem. Gửi cùng `drm_kek`. Bị từ chối với 400 `invalid_device_info` khi, sau khi bỏ khoảng trắng hai đầu, giá trị ngắn hơn 8 ký tự hoặc là một trong `(null)`, `null`, `nil`, `undefined`, `nan`, `<null>`, `none` (chữ hoa hay thường đều tính)."
                  },
                  "drm_kek": {
                    "type": "string",
                    "x-required-text": "Yes, for encrypted episodes",
                    "x-required-text-vi": "Có, với tập đã mã hoá",
                    "description": "Device public key generated by the Player SDK. Send together with `union_info`. Same validation as `union_info`. The matching private key never leaves the device.",
                    "x-description-vi": "Device public key do Player SDK tạo ra. Gửi cùng `union_info`. Kiểm như `union_info`. Khoá riêng tương ứng không bao giờ rời thiết bị."
                  },
                  "intent": {
                    "type": "string",
                    "enum": [
                      "play",
                      "prefetch"
                    ],
                    "description": "`play` when the viewer tapped the episode, `prefetch` when your app loads it ahead of a tap. Prefetch requests are not counted as plays. A missing or other value is recorded as unknown, never rejected, and counted as a play.",
                    "x-description-vi": "`play` khi người xem chạm vào tập, `prefetch` khi app tải tập trước khi người xem chạm. Request prefetch không tính là lượt phát. Thiếu trường này hoặc giá trị khác được ghi nhận là không xác định, không bao giờ bị từ chối, và vẫn tính là lượt phát."
                  },
                  "viewer_ref": {
                    "type": "string",
                    "description": "Your own opaque session or device identifier, stored with the ticket for support lookups (up to 256 characters). Do not include personal data. When you send it, asking again for the same episode with the same `viewer_ref`, intent (`play` or `prefetch`) and device values while your previous ticket still has at least 5 minutes left returns that same ticket (same `expires_at`, not extended) and does not count against your rate limit.",
                    "x-description-vi": "Định danh phiên hoặc thiết bị opaque của riêng bạn, được lưu cùng vé để tra cứu khi hỗ trợ (tối đa 256 ký tự). Đừng đưa dữ liệu cá nhân vào. Khi bạn gửi trường này, xin lại cùng một tập với cùng `viewer_ref`, intent (`play` hay `prefetch`) và giá trị thiết bị trong lúc vé trước còn ít nhất 5 phút sẽ nhận lại đúng vé đó (cùng `expires_at`, không gia hạn) và không bị tính vào rate limit."
                  },
                  "episodes": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 3,
                    "description": "Batch form A — up to 3 episodes in one call (typically the one being watched + the next 2). Cannot be combined with top-level `episode_id` / `film_ref` / `episode_no` or with `next`. Item 0 takes the top-level `intent`; later items default to `prefetch` unless they carry their own.",
                    "x-description-vi": "Lô dạng A — tối đa 3 tập trong một lần gọi (thường là tập đang xem + 2 tập kế). Không dùng chung với `episode_id` / `film_ref` / `episode_no` ở cấp ngoài hay với `next`. Phần tử 0 lấy `intent` cấp ngoài; các phần tử sau mặc định là `prefetch` trừ khi tự khai.",
                    "items": {
                      "type": "object",
                      "properties": {
                        "episode_id": {
                          "type": "string",
                          "format": "uuid",
                          "description": "Episode ID. Or send `film_ref` + `episode_no`.",
                          "x-description-vi": "Episode ID. Hoặc gửi `film_ref` + `episode_no`."
                        },
                        "film_ref": {
                          "type": "string",
                          "description": "Film `id` or `slug`, with `episode_no`.",
                          "x-description-vi": "`id` hoặc `slug` của phim, đi cùng `episode_no`."
                        },
                        "episode_no": {
                          "type": "integer",
                          "minimum": 1,
                          "description": "Episode number (whole number from 1), with `film_ref`.",
                          "x-description-vi": "Số tập (số nguyên từ 1), đi cùng `film_ref`."
                        },
                        "intent": {
                          "type": "string",
                          "enum": [
                            "play",
                            "prefetch"
                          ],
                          "description": "Intent of this item.",
                          "x-description-vi": "Intent của phần tử này."
                        }
                      }
                    }
                  },
                  "next": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 2,
                    "description": "Batch form B — with an anchor episode (`episode_id`, or `film_ref` + `episode_no`), also issue tickets for the next N episodes of the same film. Following episodes are recorded as `prefetch`. An episode past the end of the film, or not published yet, is a per-item 404 `not_ready`. Sending `next` (even 0) switches the response to the batch shape.",
                    "x-description-vi": "Lô dạng B — cùng một tập neo (`episode_id`, hoặc `film_ref` + `episode_no`), cấp thêm vé cho N tập kế tiếp của cùng phim. Các tập kế được ghi là `prefetch`. Tập vượt quá cuối phim hoặc chưa publish là lỗi 404 `not_ready` của riêng phần tử đó. Gửi `next` (kể cả 0) là chuyển response sang dạng lô."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ticket issued. Single request: the ticket fields at the top level. Batch request (`episodes` or `next` sent): only `items`.",
            "x-description-vi": "Đã cấp vé. Request một tập: các trường của vé nằm ở cấp ngoài. Request lô (có `episodes` hoặc `next`): chỉ có `items`.",
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationLink"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "vid": {
                      "type": "string",
                      "description": "Video ID to load in the Player SDK. Present with `play_auth_token`.",
                      "x-description-vi": "Video ID để tải trong Player SDK. Đi cùng `play_auth_token`."
                    },
                    "play_auth_token": {
                      "type": "string",
                      "description": "Play token to hand to the Player SDK. Keep it secret and do not log it.",
                      "x-description-vi": "Play token để đưa cho Player SDK. Giữ bí mật và không ghi log."
                    },
                    "manifest_url": {
                      "type": "string",
                      "description": "Signed stream link, present instead of `vid` + `play_auth_token` only for content Wandura delivers to your organisation without encryption, by agreement (then `protection` is `signed-url-only`). Valid until `expires_at`.",
                      "x-description-vi": "Link stream đã ký, thay cho `vid` + `play_auth_token`, chỉ có với nội dung Wandura giao cho tổ chức của bạn không mã hoá theo thoả thuận (khi đó `protection` là `signed-url-only`). Dùng được tới `expires_at`."
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the ticket expires: 30 minutes after it was issued (15 minutes for `manifest_url`).",
                      "x-description-vi": "Thời điểm vé hết hạn: 30 phút sau khi được cấp (15 phút với `manifest_url`)."
                    },
                    "protection": {
                      "type": "string",
                      "enum": [
                        "private-encrypted",
                        "signed-url-only"
                      ],
                      "description": "Protection level of what you received. `private-encrypted` for licensed episodes. Only sell paid episodes when it is `private-encrypted`.",
                      "x-description-vi": "Mức bảo vệ của nội dung bạn nhận được. `private-encrypted` với các tập được cấp phép. Chỉ bán tập trả phí khi giá trị là `private-encrypted`."
                    },
                    "subtitles": {
                      "type": "array",
                      "description": "Subtitles attached to the episode, in `lang` order. Empty when none are attached.",
                      "x-description-vi": "Phụ đề gắn với tập, theo thứ tự `lang`. Rỗng khi chưa có phụ đề nào.",
                      "items": {
                        "$ref": "#/components/schemas/Subtitle"
                      }
                    },
                    "items": {
                      "type": "array",
                      "description": "Batch only — one entry per requested episode, in request order. On `status` 200 the entry also has `episode_id`, `episode_no` and the same ticket fields as a single response; otherwise `error`, `error_code`, optional `reason` (403, and 429 `prefetch_shed`) and `retry_after` (503 and 429), and no episode data.",
                      "x-description-vi": "Chỉ có ở lô — mỗi tập được hỏi một phần tử, theo thứ tự hỏi. Khi `status` là 200 phần tử còn có `episode_id`, `episode_no` và các trường vé như response một tập; ngược lại có `error`, `error_code`, `reason` (403, và 429 `prefetch_shed`, nếu có) và `retry_after` (503 và 429), không có dữ liệu tập.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "request": {
                            "type": "object",
                            "description": "Echo of what was asked for this item.",
                            "x-description-vi": "Lặp lại thứ đã hỏi cho phần tử này.",
                            "properties": {
                              "episode_id": {
                                "type": "string"
                              },
                              "film_ref": {
                                "type": "string"
                              },
                              "episode_no": {
                                "type": "integer"
                              },
                              "next_offset": {
                                "type": "integer"
                              }
                            }
                          },
                          "status": {
                            "type": "integer",
                            "description": "This item's own HTTP-equivalent status.",
                            "x-description-vi": "Status kiểu HTTP của riêng phần tử này."
                          },
                          "episode_id": {
                            "type": "string"
                          },
                          "episode_no": {
                            "type": "integer"
                          },
                          "vid": {
                            "type": "string"
                          },
                          "play_auth_token": {
                            "type": "string"
                          },
                          "manifest_url": {
                            "type": "string"
                          },
                          "expires_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "protection": {
                            "type": "string"
                          },
                          "subtitles": {
                            "type": "array",
                            "items": {
                              "$ref": "#/components/schemas/Subtitle"
                            }
                          },
                          "error": {
                            "type": "string"
                          },
                          "error_code": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string"
                          },
                          "retry_after": {
                            "type": "integer",
                            "description": "Seconds to wait before retrying this item (503 and 429).",
                            "x-description-vi": "Số giây cần chờ trước khi thử lại phần tử này (503 và 429)."
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "vid": "v0d2f8c1a9e4b7",
                  "play_auth_token": "eyJhbGciOi...placeholder...",
                  "expires_at": "2026-10-01T12:30:00.000Z",
                  "protection": "private-encrypted",
                  "subtitles": [
                    {
                      "lang": "en",
                      "url": "https://cdn.example.com/subtitles/heiress-ep1-en.vtt",
                      "format": "webvtt"
                    },
                    {
                      "lang": "vi",
                      "url": "https://cdn.example.com/subtitles/heiress-ep1-vi.vtt",
                      "format": "webvtt"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request body is invalid.",
            "x-description-vi": "Body của request không hợp lệ.",
            "x-error-codes": [
              {
                "error_code": null,
                "description": "The body is not valid JSON, or has neither `episode_id` nor `film_ref` with a numeric `episode_no`.",
                "x-description-vi": "Body không phải JSON hợp lệ, hoặc không có `episode_id` cũng không có `film_ref` kèm `episode_no` dạng số."
              },
              {
                "error_code": "invalid_request",
                "description": "Only one of `union_info` and `drm_kek` was sent, or a malformed batch (`episodes` empty or longer than 3, an item without an episode, `episodes` mixed with top-level episode fields or with `next`, `next` outside 0–2 or without an anchor).",
                "x-description-vi": "Chỉ gửi một trong hai `union_info` và `drm_kek`, hoặc lô sai dạng (`episodes` rỗng hay dài hơn 3, một phần tử không chỉ tập nào, `episodes` dùng chung với trường tập cấp ngoài hay với `next`, `next` ngoài 0–2 hoặc không có tập neo)."
              },
              {
                "error_code": "invalid_device_info",
                "description": "A device value looks empty or is too short. The `fields` array names it.",
                "x-description-vi": "Một giá trị thiết bị trông như rỗng hoặc quá ngắn. Mảng `fields` nêu tên giá trị đó."
              },
              {
                "error_code": "unsupported_version",
                "description": "The `Wandura-Version` header, or the version pinned to your key, is unknown or retired. The body adds `supported_versions` and `latest_version` (and `upgrade_to` when your pin was retired).",
                "x-description-vi": "Header `Wandura-Version`, hoặc bản ghim của key, là bản lạ hay đã ngừng. Body có thêm `supported_versions` và `latest_version` (và `upgrade_to` khi bản ghim đã ngừng)."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked API key. A revoked key, or the old secret of a rotated key, is normally refused within about 1 second, and always within 30 seconds. Tickets already issued keep playing until they expire.",
            "x-description-vi": "Thiếu API key, key không tồn tại hoặc đã bị thu hồi. Key bị thu hồi, hoặc bí mật cũ của key vừa xoay vòng, thường bị từ chối trong khoảng 1 giây, và luôn trong vòng 30 giây. Vé đã cấp vẫn phát tới khi hết hạn.",
            "x-error-codes": [
              {
                "error_code": null,
                "description": "The `Authorization: Bearer` header is missing, or the key is unknown or revoked.",
                "x-description-vi": "Thiếu header `Authorization: Bearer`, hoặc key không tồn tại hay đã bị thu hồi."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks the scope, or your organisation may not stream this film right now.",
            "x-description-vi": "Key thiếu scope, hoặc tổ chức của bạn hiện không được phép stream phim này.",
            "x-error-codes": [
              {
                "error_code": null,
                "description": "The key lacks the `playback` scope.",
                "x-description-vi": "Key thiếu scope `playback`."
              },
              {
                "error_code": "not_entitled",
                "description": "Your organisation may not stream this film right now. `reason` is one of `org_suspended`, `entitlement_not_found`, `entitlement_suspended`, `streaming_disabled`, `outside_window`.",
                "x-description-vi": "Tổ chức của bạn hiện không được phép stream phim này. `reason` là một trong `org_suspended`, `entitlement_not_found`, `entitlement_suspended`, `streaming_disabled`, `outside_window`."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                },
                "example": {
                  "error": "outside_window",
                  "error_code": "not_entitled",
                  "reason": "outside_window"
                }
              }
            }
          },
          "404": {
            "description": "The film or episode is unknown, not published, or not ready to stream.",
            "x-description-vi": "Phim hoặc tập không tồn tại, chưa publish, hoặc chưa sẵn sàng để stream.",
            "x-error-codes": [
              {
                "error_code": "not_ready",
                "description": "The film or episode is unknown, not published, or not ready to stream.",
                "x-description-vi": "Phim hoặc tập không tồn tại, chưa publish, hoặc chưa sẵn sàng để stream."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "409": {
            "description": "The episode is encrypted and the request had no device values. Resend with `union_info` + `drm_kek`.",
            "x-description-vi": "Tập đã mã hoá mà request không có giá trị thiết bị. Gửi lại kèm `union_info` + `drm_kek`.",
            "x-error-codes": [
              {
                "error_code": "sdk_required",
                "description": "The episode is encrypted and the request had no device values. The body also carries `protection`.",
                "x-description-vi": "Tập đã mã hoá và request không có giá trị thiết bị. Body còn kèm `protection`."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/DeliveryError"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "protection": {
                          "type": "string",
                          "enum": [
                            "private-encrypted",
                            "signed-url-only"
                          ],
                          "description": "Protection level of the episode.",
                          "x-description-vi": "Mức bảo vệ của tập."
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Abuse ceiling for your organisation reached (10,800 tickets per 60-second window; a batch of 3 counts 3). Wait `Retry-After` seconds — the allowance refills continuously, so this is usually 1–2 seconds; when the next ticket is only a few hundred milliseconds away, a `play` request waits for it instead of failing (the response is then a little slower). Close to the ceiling, `prefetch` tickets are paused first while `play` tickets are still issued: such a refusal carries `reason: prefetch_shed`. In a batch, each item that cannot be issued gets its own per-item 429 (prefetch items first); the request itself is 429 only when nothing in it could be issued.",
            "x-description-vi": "Đã chạm trần chống lạm dụng của tổ chức bạn (10.800 vé mỗi cửa sổ 60 giây; một lô 3 tập tính 3). Chờ số giây trong `Retry-After` — hạn mức được nạp lại liên tục, nên thường chỉ 1–2 giây; khi vé kế chỉ còn vài trăm mili giây, request `play` chờ vé đó thay vì lỗi (response khi đó chậm hơn một chút). Khi gần trần, vé `prefetch` bị tạm dừng trước trong khi vé `play` vẫn được cấp: lần từ chối đó có `reason: prefetch_shed`. Trong lô, phần tử nào không cấp được nhận 429 riêng (phần tử prefetch trước); cả request chỉ 429 khi không phần tử nào cấp được.",
            "x-error-codes": [
              {
                "error_code": "rate_limited",
                "description": "The playback rate limit was reached, or (with `reason` `prefetch_shed`) prefetch is paused because your playback traffic is close to it. See `Retry-After`.",
                "x-description-vi": "Đã chạm rate limit của playback, hoặc (khi `reason` là `prefetch_shed`) prefetch bị tạm dừng vì lưu lượng playback của bạn đã gần mức đó. Xem `Retry-After`."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Window": {
                "$ref": "#/components/headers/RateLimitWindow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "502": {
            "description": "The stream could not be prepared or the play token could not be signed. Retry with backoff.",
            "x-description-vi": "Không chuẩn bị được stream hoặc không ký được play token. Retry với backoff.",
            "x-error-codes": [
              {
                "error_code": null,
                "description": "The stream could not be prepared or the play token could not be signed. Retry with backoff.",
                "x-description-vi": "Không chuẩn bị được stream hoặc không ký được play token. Retry với backoff."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unable to issue the ticket. `service_unavailable` means retry after `Retry-After`; `not_ready` means the episode has no playable version right now.",
            "x-description-vi": "Tạm thời không cấp được vé. `service_unavailable` nghĩa là thử lại sau `Retry-After`; `not_ready` nghĩa là tập hiện chưa có phiên bản phát được.",
            "x-error-codes": [
              {
                "error_code": "not_ready",
                "description": "No playable version of the episode is available right now.",
                "x-description-vi": "Hiện chưa có phiên bản phát được của tập."
              },
              {
                "error_code": "service_unavailable",
                "description": "Wandura could not verify the API key in time. The key is not wrong: retry after `Retry-After` seconds, with backoff.",
                "x-description-vi": "Wandura chưa kiểm được API key kịp thời. Key không sai: thử lại sau số giây trong `Retry-After`, có backoff."
              },
              {
                "error_code": "service_unavailable",
                "description": "The video provider did not answer in time (`The video provider did not answer in time — retry`). The episode is not missing: retry after `Retry-After` seconds, with backoff.",
                "x-description-vi": "Nhà cung cấp video chưa trả lời kịp (`The video provider did not answer in time — retry`). Tập phim không bị thiếu: thử lại sau số giây trong `Retry-After`, có backoff."
              },
              {
                "error_code": "service_unavailable",
                "description": "Wandura could not check the licence or the episode in time (`Could not check the licence or episode right now — retry`). This says nothing about your licence or the episode: keep selling, and retry after `Retry-After` seconds, with backoff.",
                "x-description-vi": "Wandura chưa kiểm được giấy phép hoặc tập phim kịp thời (`Could not check the licence or episode right now — retry`). Điều này không nói gì về giấy phép hay tập phim: cứ tiếp tục bán, và thử lại sau số giây trong `Retry-After`, có backoff."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          }
        }
      }
    },
    "/api/delivery/v1/playlists": {
      "get": {
        "operationId": "listPlaylists",
        "x-scope": "metadata:read",
        "tags": [
          "playlists"
        ],
        "security": [
          {
            "deliveryApiKey": []
          }
        ],
        "summary": "List video playlists for metadata sync",
        "x-summary-vi": "Liệt kê playlist video để đồng bộ metadata",
        "description": "For video partners. Lists every playlist in the video library (all Categories, or one with `category`), in the `{code, message, data, server_time}` envelope, for a CMS sync job. You see the whole library: `granted` says which playlists your organisation is licensed for. Each item carries `content_version`: when it equals the value you stored, nothing changed and you do not need GET /api/delivery/v1/playlists/{code}. Pass `updated_since` (epoch seconds) to get only playlists changed after that moment. A playlist Wandura takes out of the library (the playlist itself, or its whole Category) stays in the list as `withdrawn` for 30 days. Read-only: there are no playback links here; your app plays the copies in your own media space. A film partner's key gets 403.",
        "x-description-vi": "Dành cho đối tác video. Liệt kê mọi playlist trong kho video (mọi Category, hoặc một Category với `category`), trong envelope `{code, message, data, server_time}`, cho job đồng bộ CMS. Bạn thấy cả kho: `granted` cho biết playlist nào tổ chức của bạn được cấp. Mỗi phần tử có `content_version`: bằng giá trị bạn đã lưu thì không có gì đổi và bạn không cần gọi GET /api/delivery/v1/playlists/{code}. Gửi `updated_since` (giây epoch) để chỉ nhận playlist đổi sau mốc đó. Playlist Wandura rút khỏi kho (chính playlist, hoặc cả Category của nó) còn trong danh sách dạng `withdrawn` 30 ngày. Chỉ đọc: không có link phát ở đây; app của bạn phát bản chép trong media space của chính bạn. Key của đối tác phim nhận 403.",
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "curl",
            "source": "curl -s \"https://www.wandura.ai/api/delivery/v1/playlists?category=fitness&updated_since=1790390000&limit=50\" \\\n  -H \"Authorization: Bearer $WANDURA_API_KEY\""
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "category",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional. Category slug, for example `fitness`. Omitted = every Category. A Category taken out of the library less than 30 days ago still works here (its playlists come back `withdrawn`).",
            "x-description-vi": "Tuỳ chọn. Slug Category, ví dụ `fitness`. Bỏ trống = mọi Category. Category bị rút khỏi kho chưa quá 30 ngày vẫn lọc được (playlist của nó trả về `withdrawn`)."
          },
          {
            "in": "query",
            "name": "updated_since",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Optional. Unix epoch seconds; only playlists changed after this moment are returned.",
            "x-description-vi": "Tuỳ chọn. Giây Unix epoch; chỉ trả playlist đổi sau mốc này."
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Optional. Items per page, 1 to 100. Default 50.",
            "x-description-vi": "Tuỳ chọn. Số phần tử mỗi trang, 1 đến 100. Mặc định 50."
          },
          {
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional. `next_cursor` of the previous page, passed back unchanged.",
            "x-description-vi": "Tuỳ chọn. `next_cursor` của trang trước, truyền lại nguyên vẹn."
          },
          {
            "$ref": "#/components/parameters/WanduraVersionHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of playlists, oldest change first.",
            "x-description-vi": "Một trang playlist, thay đổi cũ nhất trước.",
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "code",
                    "message",
                    "data",
                    "server_time"
                  ],
                  "properties": {
                    "code": {
                      "type": "integer",
                      "description": "`0`.",
                      "x-description-vi": "`0`."
                    },
                    "message": {
                      "type": "string",
                      "description": "`\"ok\"`.",
                      "x-description-vi": "`\"ok\"`."
                    },
                    "data": {
                      "type": "object",
                      "required": [
                        "items",
                        "next_cursor"
                      ],
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/PlaylistSummary"
                          }
                        },
                        "next_cursor": {
                          "type": "string",
                          "nullable": true,
                          "description": "Pass it back as `cursor`; `null` on the last page.",
                          "x-description-vi": "Gửi lại làm `cursor`; `null` ở trang cuối."
                        }
                      }
                    },
                    "server_time": {
                      "type": "integer",
                      "description": "Server time in Unix epoch seconds.",
                      "x-description-vi": "Giờ server tính bằng giây Unix epoch."
                    },
                    "links": {
                      "$ref": "#/components/schemas/PageLinks"
                    }
                  }
                },
                "example": {
                  "code": 0,
                  "message": "ok",
                  "data": {
                    "items": [
                      {
                        "code": "wall-pilates-01",
                        "status": "active",
                        "title": "Wall Pilates",
                        "category": {
                          "slug": "fitness",
                          "name": "Fitness"
                        },
                        "group": {
                          "code": "pilates",
                          "title": "Pilates"
                        },
                        "clip_count": 24,
                        "granted": true,
                        "updated_at": 1790390000,
                        "content_version": "5d41402abc4b2a76b9719d911017c592",
                        "withdrawn_at": null
                      },
                      {
                        "code": "gym-basics-02",
                        "status": "active",
                        "title": "Gym Basics",
                        "category": {
                          "slug": "fitness",
                          "name": "Fitness"
                        },
                        "group": {
                          "code": "gym",
                          "title": "Gym"
                        },
                        "clip_count": 18,
                        "granted": false,
                        "updated_at": 1790391000,
                        "content_version": "7d793037a0760186574b0282f2f435e7",
                        "withdrawn_at": null
                      }
                    ],
                    "next_cursor": null
                  },
                  "server_time": 1790400000
                }
              }
            }
          },
          "400": {
            "description": "The requested API version is unknown or retired. Flat error body, not the envelope.",
            "x-description-vi": "Phiên bản API được yêu cầu là bản lạ hoặc đã ngừng. Body lỗi phẳng, không phải envelope.",
            "x-error-codes": [
              {
                "error_code": "unsupported_version",
                "description": "The `Wandura-Version` header, or the version pinned to your key, is unknown or retired.",
                "x-description-vi": "Header `Wandura-Version`, hoặc bản ghim của key, là bản lạ hay đã ngừng."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked API key.",
            "x-description-vi": "Thiếu API key, key không tồn tại hoặc đã bị thu hồi.",
            "x-error-codes": [
              {
                "code": 4010,
                "description": "The `Authorization: Bearer` header is missing.",
                "x-description-vi": "Thiếu header `Authorization: Bearer`."
              },
              {
                "code": 4011,
                "description": "The key is unknown or revoked.",
                "x-description-vi": "Key không tồn tại hoặc đã bị thu hồi."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "403": {
            "description": "The key lacks `metadata:read`, your organisation is suspended, or it is a film partner.",
            "x-description-vi": "Key thiếu `metadata:read`, tổ chức của bạn bị tạm ngưng, hoặc là đối tác phim.",
            "x-error-codes": [
              {
                "code": 4030,
                "description": "The key lacks the `metadata:read` scope.",
                "x-description-vi": "Key thiếu scope `metadata:read`."
              },
              {
                "code": 4031,
                "description": "Your organisation's access is suspended.",
                "x-description-vi": "Quyền truy cập của tổ chức bạn đang bị tạm ngưng."
              },
              {
                "code": 4032,
                "description": "Playlist metadata is for video partners; your organisation licenses films (use the series endpoints).",
                "x-description-vi": "Metadata playlist dành cho đối tác video; tổ chức của bạn lấy phim (dùng các endpoint series)."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "404": {
            "description": "The `category` is not in the library.",
            "x-description-vi": "`category` không có trong kho.",
            "x-error-codes": [
              {
                "code": 4040,
                "description": "No such Category.",
                "x-description-vi": "Không có Category này."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "422": {
            "description": "A query parameter is invalid.",
            "x-description-vi": "Một tham số query không hợp lệ.",
            "x-error-codes": [
              {
                "code": 4220,
                "description": "`limit` is not 1 to 100, `updated_since` is not epoch seconds, `category` is not a slug, or `cursor` is not a value this endpoint returned.",
                "x-description-vi": "`limit` không trong 1 đến 100, `updated_since` không phải giây epoch, `category` không phải slug, hoặc `cursor` không phải giá trị endpoint này trả về."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "429": {
            "description": "Abuse ceiling reached (shared with the catalog). Flat body, not the envelope. Wait `Retry-After` seconds.",
            "x-description-vi": "Đã chạm trần chống lạm dụng (dùng chung với catalog). Body phẳng, không phải envelope. Chờ số giây trong `Retry-After`.",
            "x-error-codes": [
              {
                "error_code": "rate_limited",
                "description": "Rate limit reached.",
                "x-description-vi": "Đã chạm rate limit."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable. Retry with backoff.",
            "x-description-vi": "Tạm thời không khả dụng. Retry với backoff.",
            "x-error-codes": [
              {
                "code": 5030,
                "description": "The list is temporarily unavailable.",
                "x-description-vi": "Danh sách tạm thời không khả dụng."
              },
              {
                "code": 5031,
                "description": "Wandura could not verify the API key in time.",
                "x-description-vi": "Wandura chưa kiểm được API key kịp thời."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          }
        }
      }
    },
    "/api/delivery/v1/playlists/{code}": {
      "get": {
        "operationId": "getPlaylist",
        "x-scope": "metadata:read",
        "tags": [
          "playlists"
        ],
        "security": [
          {
            "deliveryApiKey": []
          }
        ],
        "summary": "Get video playlist metadata",
        "x-summary-vi": "Lấy metadata một playlist video",
        "description": "For video partners. Returns one playlist for your CMS: Category, group, title, description, artwork, and its clips in order with title, description, duration, muscle groups, equipment, difficulty, calories, poster and production order code. When your organisation is licensed for it (`granted`), it also tells you where your copies live: your media space, the video ID and format (`hls` or `mp4`) of each copied clip, and the playlist ID in your space. No playback links: your app plays the copies straight from your media space. A playlist that is not in the library returns 404; a film partner's key gets 403.",
        "x-description-vi": "Dành cho đối tác video. Trả một playlist cho CMS của bạn: Category, nhóm, tên, mô tả, ảnh, và các clip theo thứ tự kèm tên, mô tả, thời lượng, nhóm cơ, dụng cụ, độ khó, calo, poster và mã đơn sản xuất. Khi tổ chức của bạn được cấp (`granted`), nó cho biết thêm bản chép của bạn nằm ở đâu: media space của bạn, video ID và định dạng (`hls` hoặc `mp4`) của từng clip đã chép, và ID playlist trong space của bạn. Không có link phát: app của bạn phát bản chép thẳng từ media space của bạn. Playlist không có trong kho trả 404; key của đối tác phim nhận 403.",
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "curl",
            "source": "curl -s https://www.wandura.ai/api/delivery/v1/playlists/wall-pilates-01 \\\n  -H \"Authorization: Bearer $WANDURA_API_KEY\""
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "code",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Playlist code, for example `wall-pilates-01` (the `code` from the list).",
            "x-description-vi": "Mã playlist, ví dụ `wall-pilates-01` (`code` trong danh sách)."
          },
          {
            "$ref": "#/components/parameters/WanduraVersionHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "The playlist.",
            "x-description-vi": "Playlist.",
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "code",
                    "message",
                    "data",
                    "server_time"
                  ],
                  "properties": {
                    "code": {
                      "type": "integer",
                      "description": "`0`.",
                      "x-description-vi": "`0`."
                    },
                    "message": {
                      "type": "string",
                      "description": "`\"ok\"`.",
                      "x-description-vi": "`\"ok\"`."
                    },
                    "data": {
                      "$ref": "#/components/schemas/PlaylistDetail"
                    },
                    "server_time": {
                      "type": "integer",
                      "description": "Server time in Unix epoch seconds.",
                      "x-description-vi": "Giờ server tính bằng giây Unix epoch."
                    },
                    "links": {
                      "$ref": "#/components/schemas/SelfLink"
                    }
                  }
                },
                "example": {
                  "code": 0,
                  "message": "ok",
                  "data": {
                    "code": "wall-pilates-01",
                    "status": "active",
                    "title": "Wall Pilates",
                    "description": "Ten low-impact moves using a wall for support.",
                    "category": {
                      "slug": "fitness",
                      "name": "Fitness"
                    },
                    "group": {
                      "code": "pilates",
                      "title": "Pilates"
                    },
                    "updated_at": 1790390000,
                    "content_version": "5d41402abc4b2a76b9719d911017c592",
                    "metadata_version": "0cc175b9c0f1b6a831c399e269772661",
                    "granted": true,
                    "media": {
                      "space": "your-fitness-space",
                      "playlist_id": "9f8e7d6c5b4a3921",
                      "playlist_name": "wall-pilates-01",
                      "playlists": [
                        {
                          "id": "9f8e7d6c5b4a3921",
                          "name": "wall-pilates-01"
                        }
                      ]
                    },
                    "images": [
                      {
                        "kind": "cover",
                        "source": "media_space",
                        "url": null,
                        "expires_at": null,
                        "space": "your-fitness-space",
                        "uri": "your-fitness-space/fit-3f2a1b4c.jpg"
                      }
                    ],
                    "clips": [
                      {
                        "position": 1,
                        "id": "3f2a1b4c-5d6e-4f70-8a9b-0c1d2e3f4a5b",
                        "title": "Wall Squat",
                        "description": "Slide down the wall and hold.",
                        "duration_ms": 45000,
                        "muscle_groups": [
                          "quads",
                          "glutes"
                        ],
                        "equipment": [
                          "wall"
                        ],
                        "difficulty": "intermediate",
                        "kcal_est": 9,
                        "order_code": "ABS_01_E01",
                        "poster": {
                          "source": "media_space",
                          "url": null,
                          "expires_at": null,
                          "space": "your-fitness-space",
                          "uri": "your-fitness-space/fit-3f2a1b4c.jpg"
                        },
                        "ready": true,
                        "vid": "v0a1b2c3d4e5f6",
                        "format": "hls",
                        "playlist_id": "9f8e7d6c5b4a3921",
                        "playlist_name": "wall-pilates-01"
                      }
                    ]
                  },
                  "server_time": 1790400000
                }
              }
            }
          },
          "400": {
            "description": "The requested API version is unknown or retired. Flat error body, not the envelope.",
            "x-description-vi": "Phiên bản API được yêu cầu là bản lạ hoặc đã ngừng. Body lỗi phẳng, không phải envelope.",
            "x-error-codes": [
              {
                "error_code": "unsupported_version",
                "description": "The `Wandura-Version` header, or the version pinned to your key, is unknown or retired.",
                "x-description-vi": "Header `Wandura-Version`, hoặc bản ghim của key, là bản lạ hay đã ngừng."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked API key.",
            "x-description-vi": "Thiếu API key, key không tồn tại hoặc đã bị thu hồi.",
            "x-error-codes": [
              {
                "code": 4010,
                "description": "The `Authorization: Bearer` header is missing.",
                "x-description-vi": "Thiếu header `Authorization: Bearer`."
              },
              {
                "code": 4011,
                "description": "The key is unknown or revoked.",
                "x-description-vi": "Key không tồn tại hoặc đã bị thu hồi."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "403": {
            "description": "The key lacks `metadata:read`, your organisation is suspended, or it is a film partner.",
            "x-description-vi": "Key thiếu `metadata:read`, tổ chức của bạn bị tạm ngưng, hoặc là đối tác phim.",
            "x-error-codes": [
              {
                "code": 4030,
                "description": "The key lacks the `metadata:read` scope.",
                "x-description-vi": "Key thiếu scope `metadata:read`."
              },
              {
                "code": 4031,
                "description": "Your organisation's access is suspended.",
                "x-description-vi": "Quyền truy cập của tổ chức bạn đang bị tạm ngưng."
              },
              {
                "code": 4032,
                "description": "Playlist metadata is for video partners; your organisation licenses films (use the series endpoints).",
                "x-description-vi": "Metadata playlist dành cho đối tác video; tổ chức của bạn lấy phim (dùng các endpoint series)."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "404": {
            "description": "No such playlist in the library.",
            "x-description-vi": "Không có playlist này trong kho.",
            "x-error-codes": [
              {
                "code": 4040,
                "description": "No such playlist, or it is not in the library (the same answer on purpose).",
                "x-description-vi": "Không có playlist này, hoặc nó không có trong kho (cố ý trả cùng một câu)."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "429": {
            "description": "Abuse ceiling reached (shared with the catalog). Flat body, not the envelope. Wait `Retry-After` seconds.",
            "x-description-vi": "Đã chạm trần chống lạm dụng (dùng chung với catalog). Body phẳng, không phải envelope. Chờ số giây trong `Retry-After`.",
            "x-error-codes": [
              {
                "error_code": "rate_limited",
                "description": "Rate limit reached.",
                "x-description-vi": "Đã chạm rate limit."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable. Retry with backoff.",
            "x-description-vi": "Tạm thời không khả dụng. Retry với backoff.",
            "x-error-codes": [
              {
                "code": 5030,
                "description": "The playlist is temporarily unavailable.",
                "x-description-vi": "Playlist tạm thời không khả dụng."
              },
              {
                "code": 5031,
                "description": "Wandura could not verify the API key in time.",
                "x-description-vi": "Wandura chưa kiểm được API key kịp thời."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          }
        }
      }
    },
    "/api/delivery/v1/series": {
      "get": {
        "operationId": "listSeries",
        "x-scope": "metadata:read",
        "tags": [
          "series"
        ],
        "security": [
          {
            "deliveryApiKey": []
          }
        ],
        "summary": "List series for metadata sync",
        "x-summary-vi": "Liệt kê bộ phim để đồng bộ metadata",
        "description": "Lists the series your organisation is licensed for, for a CMS sync job, in the `{code, message, data, server_time}` envelope. Each item carries `content_version`: when it equals the value you stored, nothing changed and you do not need GET /api/delivery/v1/series/{code}. Pass `updated_since` (epoch seconds) to get only series changed after that moment. A series whose licence ended stays in the list as `withdrawn` for 30 days. Your CMS may override any field; Wandura never overwrites your edits — store the `content_version` you imported and keep your own edits.",
        "x-description-vi": "Liệt kê các bộ tổ chức của bạn được cấp quyền, cho job đồng bộ CMS, trong envelope `{code, message, data, server_time}`. Mỗi phần tử có `content_version`: bằng giá trị bạn đã lưu thì không có gì đổi và bạn không cần gọi GET /api/delivery/v1/series/{code}. Gửi `updated_since` (giây epoch) để chỉ nhận bộ đổi sau mốc đó. Bộ hết giấy phép còn trong danh sách dạng `withdrawn` 30 ngày. CMS của bạn được sửa đè bất kỳ trường nào; Wandura không bao giờ ghi đè bản sửa của bạn — lưu `content_version` đã nhập và giữ bản sửa của bạn.",
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "curl",
            "source": "curl -s \"https://www.wandura.ai/api/delivery/v1/series?updated_since=1790390000&limit=50\" \\\n  -H \"Authorization: Bearer $WANDURA_API_KEY\""
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "updated_since",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Optional. Unix epoch seconds; only series changed after this moment are returned.",
            "x-description-vi": "Tuỳ chọn. Giây Unix epoch; chỉ trả bộ đổi sau mốc này."
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Optional. Items per page, 1 to 100. Default 50.",
            "x-description-vi": "Tuỳ chọn. Số phần tử mỗi trang, 1 đến 100. Mặc định 50."
          },
          {
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional. `next_cursor` of the previous page, passed back unchanged.",
            "x-description-vi": "Tuỳ chọn. `next_cursor` của trang trước, truyền lại nguyên vẹn."
          },
          {
            "$ref": "#/components/parameters/WanduraVersionHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of series, oldest change first.",
            "x-description-vi": "Một trang bộ phim, thay đổi cũ nhất trước.",
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "code",
                    "message",
                    "data",
                    "server_time"
                  ],
                  "properties": {
                    "code": {
                      "type": "integer",
                      "description": "`0`.",
                      "x-description-vi": "`0`."
                    },
                    "message": {
                      "type": "string",
                      "description": "`\"ok\"`.",
                      "x-description-vi": "`\"ok\"`."
                    },
                    "data": {
                      "type": "object",
                      "required": [
                        "items",
                        "next_cursor"
                      ],
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/SeriesSummary"
                          }
                        },
                        "next_cursor": {
                          "type": "string",
                          "nullable": true,
                          "description": "Pass it back as `cursor`; `null` on the last page.",
                          "x-description-vi": "Gửi lại làm `cursor`; `null` ở trang cuối."
                        }
                      }
                    },
                    "server_time": {
                      "type": "integer",
                      "description": "Server time in Unix epoch seconds.",
                      "x-description-vi": "Giờ server tính bằng giây Unix epoch."
                    },
                    "links": {
                      "$ref": "#/components/schemas/PageLinks"
                    }
                  }
                },
                "example": {
                  "code": 0,
                  "message": "ok",
                  "data": {
                    "items": [
                      {
                        "code": "WD000016",
                        "status": "active",
                        "updated_at": 1790390000,
                        "content_version": "c3f1a9e07b2d4c5e8f9a0b1c2d3e4f50",
                        "episode_count": 60,
                        "withdrawn_at": null,
                        "film_status": "published",
                        "tier": "A"
                      },
                      {
                        "code": "WD000021",
                        "status": "withdrawn",
                        "updated_at": 1790395000,
                        "content_version": null,
                        "episode_count": null,
                        "withdrawn_at": 1790395000,
                        "film_status": "archived",
                        "tier": null
                      }
                    ],
                    "next_cursor": null
                  },
                  "server_time": 1790400000
                }
              }
            }
          },
          "400": {
            "description": "The requested API version is unknown or retired. Flat error body, not the envelope.",
            "x-description-vi": "Phiên bản API được yêu cầu là bản lạ hoặc đã ngừng. Body lỗi phẳng, không phải envelope.",
            "x-error-codes": [
              {
                "error_code": "unsupported_version",
                "description": "The `Wandura-Version` header, or the version pinned to your key, is unknown or retired.",
                "x-description-vi": "Header `Wandura-Version`, hoặc bản ghim của key, là bản lạ hay đã ngừng."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked API key.",
            "x-description-vi": "Thiếu API key, key không tồn tại hoặc đã bị thu hồi.",
            "x-error-codes": [
              {
                "code": 4010,
                "description": "The `Authorization: Bearer` header is missing.",
                "x-description-vi": "Thiếu header `Authorization: Bearer`."
              },
              {
                "code": 4011,
                "description": "The key is unknown or revoked.",
                "x-description-vi": "Key không tồn tại hoặc đã bị thu hồi."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "403": {
            "description": "The key lacks `metadata:read`, or your organisation is suspended.",
            "x-description-vi": "Key thiếu `metadata:read`, hoặc tổ chức của bạn bị tạm ngưng.",
            "x-error-codes": [
              {
                "code": 4030,
                "description": "The key lacks the `metadata:read` scope.",
                "x-description-vi": "Key thiếu scope `metadata:read`."
              },
              {
                "code": 4031,
                "description": "Your organisation's access is suspended.",
                "x-description-vi": "Quyền truy cập của tổ chức bạn đang bị tạm ngưng."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "422": {
            "description": "A query parameter is invalid.",
            "x-description-vi": "Một tham số query không hợp lệ.",
            "x-error-codes": [
              {
                "code": 4220,
                "description": "`limit` is not 1 to 100, `updated_since` is not epoch seconds, or `cursor` is not a value this endpoint returned.",
                "x-description-vi": "`limit` không trong 1 đến 100, `updated_since` không phải giây epoch, hoặc `cursor` không phải giá trị endpoint này trả về."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "429": {
            "description": "Abuse ceiling reached (shared with the catalog). Flat body, not the envelope. Wait `Retry-After` seconds.",
            "x-description-vi": "Đã chạm trần chống lạm dụng (dùng chung với catalog). Body phẳng, không phải envelope. Chờ số giây trong `Retry-After`.",
            "x-error-codes": [
              {
                "error_code": "rate_limited",
                "description": "Rate limit reached.",
                "x-description-vi": "Đã chạm rate limit."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable. Retry with backoff.",
            "x-description-vi": "Tạm thời không khả dụng. Retry với backoff.",
            "x-error-codes": [
              {
                "code": 5030,
                "description": "The list is temporarily unavailable.",
                "x-description-vi": "Danh sách tạm thời không khả dụng."
              },
              {
                "code": 5031,
                "description": "Wandura could not verify the API key in time.",
                "x-description-vi": "Wandura chưa kiểm được API key kịp thời."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          }
        }
      }
    },
    "/api/delivery/v1/series/{code}": {
      "get": {
        "operationId": "getSeries",
        "x-scope": "metadata:read",
        "tags": [
          "series"
        ],
        "security": [
          {
            "deliveryApiKey": []
          }
        ],
        "summary": "Get series metadata",
        "x-summary-vi": "Lấy metadata một bộ phim",
        "description": "Returns everything Wandura knows about one series for your CMS: titles and synopses by language, coded genre and theme tags, age rating, main cast or characters, artwork with sizes, audio language, completion flag, and the published episodes with the video ID in your media space and their subtitle files. Missing values are `null` or empty — they never block a licence. A series that does not exist and a series you may not stream return the same 404, so codes cannot be probed. Your CMS may override any field; Wandura never overwrites your edits.",
        "x-description-vi": "Trả mọi thứ Wandura biết về một bộ cho CMS của bạn: tên và tóm tắt theo ngôn ngữ, thẻ thể loại và chủ đề có mã, độ tuổi, diễn viên hoặc nhân vật chính, ảnh kèm kích thước, ngôn ngữ âm thanh, cờ đã ra hết tập, và các tập đã phát hành kèm video ID trong media space của bạn và file phụ đề. Giá trị thiếu là `null` hoặc rỗng — không bao giờ chặn giấy phép. Bộ không tồn tại và bộ bạn không được stream trả cùng một 404, nên không dò được mã bộ. CMS của bạn được sửa đè bất kỳ trường nào; Wandura không bao giờ ghi đè bản sửa của bạn.",
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "curl",
            "source": "curl -s https://www.wandura.ai/api/delivery/v1/series/WD000016 \\\n  -H \"Authorization: Bearer $WANDURA_API_KEY\""
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "code",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Series code, for example `WD000016` (the `code` from the catalog or the list).",
            "x-description-vi": "Mã bộ, ví dụ `WD000016` (`code` trong catalog hoặc danh sách)."
          },
          {
            "$ref": "#/components/parameters/WanduraVersionHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "The series.",
            "x-description-vi": "Bộ phim.",
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "code",
                    "message",
                    "data",
                    "server_time"
                  ],
                  "properties": {
                    "code": {
                      "type": "integer",
                      "description": "`0`.",
                      "x-description-vi": "`0`."
                    },
                    "message": {
                      "type": "string",
                      "description": "`\"ok\"`.",
                      "x-description-vi": "`\"ok\"`."
                    },
                    "data": {
                      "$ref": "#/components/schemas/SeriesDetail"
                    },
                    "server_time": {
                      "type": "integer",
                      "description": "Server time in Unix epoch seconds.",
                      "x-description-vi": "Giờ server tính bằng giây Unix epoch."
                    },
                    "links": {
                      "$ref": "#/components/schemas/SelfLink"
                    }
                  }
                },
                "example": {
                  "code": 0,
                  "message": "ok",
                  "data": {
                    "code": "WD000016",
                    "status": "active",
                    "film_status": "published",
                    "tier": "A",
                    "updated_at": 1790390000,
                    "content_version": "c3f1a9e07b2d4c5e8f9a0b1c2d3e4f50",
                    "language": "en",
                    "default_lang": "en",
                    "title_i18n": {
                      "en": "The Hidden Alpha Bride"
                    },
                    "synopsis_short_i18n": {
                      "en": "Rejected by her mate, a runaway omega returns as the bride of his rival."
                    },
                    "synopsis_i18n": {
                      "en": "Rejected by her mate on the night of the bond, Luna runs. Three years later she returns as the bride of his rival."
                    },
                    "genres": [
                      "Werewolf",
                      "Romance"
                    ],
                    "poster": {
                      "url": "https://static.example.com/wd000016/cover.jpg"
                    },
                    "content_rating": "13+",
                    "is_completed": true,
                    "release_date": null,
                    "planned_episodes": 60,
                    "tags": [
                      {
                        "slug": "werewolf",
                        "name": "Werewolf",
                        "name_i18n": {
                          "en": "Werewolf"
                        },
                        "type": "genre",
                        "is_main": true
                      },
                      {
                        "slug": "fated-mates",
                        "name": "Fated Mates",
                        "name_i18n": {
                          "en": "Fated Mates"
                        },
                        "type": "theme",
                        "is_main": false
                      }
                    ],
                    "people": [
                      {
                        "name": "Luna",
                        "role": "lead",
                        "description": "A rejected omega who returns to claim her place.",
                        "avatar_url": null
                      }
                    ],
                    "images": [
                      {
                        "kind": "poster_3_4",
                        "lang": null,
                        "url": "https://static.example.com/wd000016/poster-3x4.jpg",
                        "width": 1080,
                        "height": 1440
                      },
                      {
                        "kind": "cover_9_16",
                        "lang": null,
                        "url": "https://static.example.com/wd000016/cover.jpg",
                        "width": 1080,
                        "height": 1920
                      }
                    ],
                    "trailer": null,
                    "metadata_version": "9a0b1c2d3e4f5061728394a5b6c7d8e9",
                    "metadata_updated_at": 1790380000,
                    "media": {
                      "space": "your-space",
                      "playlist_name": "WD000016",
                      "playlist_id": "1f2e3d4c5b6a7980",
                      "playlists": [
                        {
                          "id": "1f2e3d4c5b6a7980",
                          "name": "WD000016"
                        }
                      ]
                    },
                    "episodes": [
                      {
                        "ep_number": 1,
                        "vid": "v0d2f8c1a9e4b7",
                        "ready": true,
                        "title_i18n": {
                          "en": "The Night of the Bond"
                        },
                        "duration_sec": 137,
                        "subtitles": [
                          {
                            "lang": "en",
                            "url": "https://static.example.com/wd000016/1.en.vtt",
                            "updated_at": 1790380000
                          }
                        ]
                      }
                    ]
                  },
                  "server_time": 1790400000
                }
              }
            }
          },
          "400": {
            "description": "The requested API version is unknown or retired. Flat error body, not the envelope.",
            "x-description-vi": "Phiên bản API được yêu cầu là bản lạ hoặc đã ngừng. Body lỗi phẳng, không phải envelope.",
            "x-error-codes": [
              {
                "error_code": "unsupported_version",
                "description": "The `Wandura-Version` header, or the version pinned to your key, is unknown or retired.",
                "x-description-vi": "Header `Wandura-Version`, hoặc bản ghim của key, là bản lạ hay đã ngừng."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked API key.",
            "x-description-vi": "Thiếu API key, key không tồn tại hoặc đã bị thu hồi.",
            "x-error-codes": [
              {
                "code": 4010,
                "description": "The `Authorization: Bearer` header is missing.",
                "x-description-vi": "Thiếu header `Authorization: Bearer`."
              },
              {
                "code": 4011,
                "description": "The key is unknown or revoked.",
                "x-description-vi": "Key không tồn tại hoặc đã bị thu hồi."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "403": {
            "description": "The key lacks `metadata:read`, or your organisation is suspended.",
            "x-description-vi": "Key thiếu `metadata:read`, hoặc tổ chức của bạn bị tạm ngưng.",
            "x-error-codes": [
              {
                "code": 4030,
                "description": "The key lacks the `metadata:read` scope.",
                "x-description-vi": "Key thiếu scope `metadata:read`."
              },
              {
                "code": 4031,
                "description": "Your organisation's access is suspended.",
                "x-description-vi": "Quyền truy cập của tổ chức bạn đang bị tạm ngưng."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "404": {
            "description": "Not found for this key.",
            "x-description-vi": "Không tìm thấy với key này.",
            "x-error-codes": [
              {
                "code": 4040,
                "description": "No such series, or your organisation may not stream it (the same answer on purpose).",
                "x-description-vi": "Không có bộ này, hoặc tổ chức của bạn không được stream nó (cố ý trả cùng một câu)."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          },
          "429": {
            "description": "Abuse ceiling reached (shared with the catalog). Flat body, not the envelope. Wait `Retry-After` seconds.",
            "x-description-vi": "Đã chạm trần chống lạm dụng (dùng chung với catalog). Body phẳng, không phải envelope. Chờ số giây trong `Retry-After`.",
            "x-error-codes": [
              {
                "error_code": "rate_limited",
                "description": "Rate limit reached.",
                "x-description-vi": "Đã chạm rate limit."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable. Retry with backoff.",
            "x-description-vi": "Tạm thời không khả dụng. Retry với backoff.",
            "x-error-codes": [
              {
                "code": 5030,
                "description": "The series is temporarily unavailable.",
                "x-description-vi": "Bộ phim tạm thời không khả dụng."
              },
              {
                "code": 5031,
                "description": "Wandura could not verify the API key in time.",
                "x-description-vi": "Wandura chưa kiểm được API key kịp thời."
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreativeFeedError"
                }
              }
            },
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              }
            }
          }
        }
      }
    },
    "/api/delivery/v1/videos": {
      "get": {
        "operationId": "listVideos",
        "x-scope": "videos:read",
        "tags": [
          "videos"
        ],
        "security": [
          {
            "deliveryApiKey": []
          }
        ],
        "summary": "List open videos",
        "x-summary-vi": "Liệt kê video mở",
        "description": "Open video — a page of ready-to-play MP4 links, with no per-view ticket, for content that needs no copyright protection (short fitness clips and the like). Requires a key with the `videos:read` scope that is bound to a registered app: every link carries that app's identity, which is how data usage is reported per app. You see only the collections granted to your organisation, and only once they are published. Videos come back in the order the collection defines. Links expire after 1 hour: call this again at least every 45 minutes (`refresh_before`) and never cache a link inside your app. Walk pages with `next_cursor`.\n\nFrozen: this endpoint keeps working exactly as it does today for apps already integrated, but it gets no new features and takes no new apps. New integrations play open video straight from their own category space and browse the catalog on the partner portal Marketplace.",
        "x-description-vi": "Video mở — một trang link MP4 phát ngay được, không cần vé cho từng lượt xem, dành cho nội dung không cần bảo vệ bản quyền (clip tập luyện ngắn và tương tự). Cần key có scope `videos:read` đã gắn với một app đã đăng ký: mỗi link mang danh tính của app đó, nhờ vậy lưu lượng được báo theo từng app. Bạn chỉ thấy các bộ sưu tập đã cấp cho tổ chức của bạn, và chỉ khi bộ đã publish. Video trả về theo thứ tự của bộ sưu tập. Link hết hạn sau 1 giờ: gọi lại ít nhất mỗi 45 phút (`refresh_before`) và đừng bao giờ lưu link trong app. Đi qua các trang bằng `next_cursor`.\n\nĐóng băng: cửa này vẫn chạy y như hôm nay cho app đã tích hợp, nhưng không thêm tính năng và không nhận app mới. Tích hợp mới phát video mở thẳng từ category space của chính mình và xem danh mục ở Marketplace trên cổng đối tác.",
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "curl",
            "source": "curl -s \"https://www.wandura.ai/api/delivery/v1/videos?limit=50\" \\\n  -H \"Authorization: Bearer $WANDURA_API_KEY\""
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "collection",
            "schema": {
              "type": "string"
            },
            "description": "Limit the page to one collection, by its code.",
            "x-description-vi": "Chỉ lấy một bộ sưu tập, theo mã của bộ."
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            },
            "description": "Opaque token from a previous `next_cursor`. Do not construct or parse it.",
            "x-description-vi": "Token opaque lấy từ `next_cursor` trước đó. Đừng tự dựng hay phân tích nó."
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 200
            },
            "description": "Rows per page, 1 to 200. Default `50`.",
            "x-description-vi": "Số dòng mỗi trang, từ 1 đến 200. Mặc định `50`."
          },
          {
            "$ref": "#/components/parameters/WanduraVersionHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of videos. A page can be shorter than `limit` while `next_cursor` is still set — keep walking until it is `null`.",
            "x-description-vi": "Một trang video. Trang có thể ngắn hơn `limit` trong khi `next_cursor` vẫn còn — đi tiếp tới khi nó là `null`.",
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              },
              "Link": {
                "$ref": "#/components/headers/DeprecationLink"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "rows",
                    "next_cursor",
                    "expires_at",
                    "refresh_before"
                  ],
                  "properties": {
                    "rows": {
                      "type": "array",
                      "description": "Videos on this page, in collection order.",
                      "x-description-vi": "Các video trên trang này, theo thứ tự của bộ sưu tập.",
                      "items": {
                        "$ref": "#/components/schemas/OpenVideo"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true,
                      "description": "Pass it as `cursor` for the next page. `null` means you have everything.",
                      "x-description-vi": "Truyền làm `cursor` để lấy trang kế. `null` nghĩa là bạn đã có đủ."
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When every link on this page stops working (1 hour after the response).",
                      "x-description-vi": "Thời điểm mọi link trên trang này ngừng chạy (1 giờ sau response)."
                    },
                    "refresh_before": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Call the list again before this time (45 minutes after the response).",
                      "x-description-vi": "Gọi lại danh sách trước thời điểm này (45 phút sau response)."
                    },
                    "links": {
                      "description": "Ready-made absolute URLs — follow `links.next` instead of building the next-page URL.",
                      "x-description-vi": "URL tuyệt đối dựng sẵn — đi theo `links.next` thay vì tự ráp URL trang kế.",
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/PageLinks"
                        }
                      ]
                    }
                  }
                },
                "example": {
                  "rows": [
                    {
                      "id": "3f6c1a52-8d0e-4b7a-9c21-5e4f2a7b9d10",
                      "slug": "core-abs-01",
                      "title": "Core abs 01",
                      "order_code": "ABS_01_E01",
                      "collection": "ABS_01",
                      "position": 1,
                      "duration_ms": 42000,
                      "muscle_groups": [
                        "abs"
                      ],
                      "equipment": [],
                      "difficulty": "beginner",
                      "kcal_est": 12,
                      "mp4": {
                        "p480": "https://cdn.example.com/v/core-abs-01-480.mp4?sign=example",
                        "p720": null
                      }
                    }
                  ],
                  "next_cursor": null,
                  "expires_at": "2026-10-01T13:00:00.000Z",
                  "refresh_before": "2026-10-01T12:45:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "A query parameter is malformed.",
            "x-description-vi": "Một query parameter sai dạng.",
            "x-error-codes": [
              {
                "error_code": null,
                "description": "`cursor` is not a token this endpoint issued, or `limit` is not an integer between 1 and 200.",
                "x-description-vi": "`cursor` không phải token do endpoint này cấp, hoặc `limit` không phải số nguyên từ 1 đến 200."
              },
              {
                "error_code": "unsupported_version",
                "description": "The `Wandura-Version` header, or the version pinned to your key, is unknown or retired. The body adds `supported_versions` and `latest_version` (and `upgrade_to` when your pin was retired).",
                "x-description-vi": "Header `Wandura-Version`, hoặc bản ghim của key, là bản lạ hay đã ngừng. Body có thêm `supported_versions` và `latest_version` (và `upgrade_to` khi bản ghim đã ngừng)."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked API key.",
            "x-description-vi": "Thiếu API key, key không tồn tại hoặc đã bị thu hồi.",
            "x-error-codes": [
              {
                "error_code": null,
                "description": "The `Authorization: Bearer` header is missing, or the key is unknown or revoked.",
                "x-description-vi": "Thiếu header `Authorization: Bearer`, hoặc key không tồn tại hay đã bị thu hồi."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks `videos:read`, is not bound to an app, or your organisation is suspended.",
            "x-description-vi": "Key thiếu `videos:read`, chưa gắn với app, hoặc tổ chức của bạn bị tạm ngưng.",
            "x-error-codes": [
              {
                "error_code": null,
                "description": "The key lacks the `videos:read` scope.",
                "x-description-vi": "Key thiếu scope `videos:read`."
              },
              {
                "error": "app_not_registered",
                "description": "The key is not bound to an app. Register the app on the Developers page and use that app's key.",
                "x-description-vi": "Key chưa gắn với app nào. Đăng ký app ở trang Developers rồi dùng key của app đó."
              },
              {
                "error": "org_suspended",
                "description": "Your organisation's access is suspended.",
                "x-description-vi": "Quyền truy cập của tổ chức bạn đang bị tạm ngưng."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "404": {
            "description": "The `collection` is unknown, not published, or not granted to your organisation — one answer for all three.",
            "x-description-vi": "`collection` không tồn tại, chưa publish, hoặc chưa cấp cho tổ chức của bạn — cùng một câu trả lời cho cả ba.",
            "x-error-codes": [
              {
                "error": "collection_not_found",
                "description": "The collection is unknown, not published, or not granted to your organisation.",
                "x-description-vi": "Bộ sưu tập không tồn tại, chưa publish, hoặc chưa cấp cho tổ chức của bạn."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "429": {
            "description": "Abuse ceiling for your organisation reached (3000 requests per 60-second window). Wait `Retry-After` seconds.",
            "x-description-vi": "Đã chạm trần chống lạm dụng của tổ chức bạn (3000 request mỗi cửa sổ 60 giây). Chờ số giây trong `Retry-After`.",
            "x-error-codes": [
              {
                "error_code": "rate_limited",
                "description": "The rate limit for this endpoint was reached.",
                "x-description-vi": "Đã chạm rate limit của endpoint này."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Window": {
                "$ref": "#/components/headers/RateLimitWindow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "500": {
            "description": "The video list could not be read. Retry with backoff.",
            "x-description-vi": "Không đọc được danh sách video. Retry với backoff.",
            "x-error-codes": [
              {
                "error_code": null,
                "description": "The video list could not be read. Retry with backoff.",
                "x-description-vi": "Không đọc được danh sách video. Retry với backoff."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "503": {
            "description": "Wandura could not verify the API key in time. The key is not wrong — retry after `Retry-After` seconds.",
            "x-description-vi": "Wandura chưa kiểm được API key kịp thời. Key không sai — thử lại sau số giây trong `Retry-After`.",
            "x-error-codes": [
              {
                "error_code": "service_unavailable",
                "description": "Wandura could not verify the API key in time. The key is not wrong: retry after `Retry-After` seconds, with backoff.",
                "x-description-vi": "Wandura chưa kiểm được API key kịp thời. Key không sai: thử lại sau số giây trong `Retry-After`, có backoff."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          }
        }
      }
    },
    "/api/delivery/v1/videos/{id}": {
      "get": {
        "operationId": "getOpenVideo",
        "x-scope": "videos:read",
        "tags": [
          "videos"
        ],
        "summary": "One open video with a fresh playback link",
        "x-summary-vi": "Một video mở kèm link phát mới",
        "description": "Returns exactly one row, in the same shape as an entry of `rows[]` from `GET /api/delivery/v1/videos`, so you never need two parsers. Use it to refresh the link for a video you already know — your sync key is `id`, which never changes, while the link lives one hour.\n\nPrefer the list endpoint plus a cache on your server. The list gives you every link in 7 requests and keeps your app playing for up to 45 minutes even if Wandura is unreachable. If you call this endpoint once per view instead, your playback availability becomes tied to ours — when Wandura is down, your app stops playing. This endpoint has its own rate limit, separate from the list, so heavy use here can never throttle the list call your server depends on.\n\nFrozen: this endpoint keeps working exactly as it does today for apps already integrated, but it gets no new features and takes no new apps. New integrations play open video straight from their own category space and browse the catalog on the partner portal Marketplace.",
        "x-description-vi": "Trả về đúng một dòng, cùng hình dạng với một phần tử của `rows[]` ở `GET /api/delivery/v1/videos`, nên bạn không phải viết hai bộ đọc. Dùng để lấy link mới cho một video đã biết — khoá đồng bộ là `id` (không bao giờ đổi), còn link chỉ sống một giờ.\n\nNên ưu tiên cửa danh sách kèm cache ở máy chủ của bạn: nó cho toàn bộ link trong 7 request và giữ app chạy được tới 45 phút ngay cả khi không gọi được Wandura. Nếu gọi cửa này mỗi lượt xem thì khả năng phát của bạn gắn vào khả năng phục vụ của chúng tôi — Wandura hỏng là app bạn ngừng phát. Cửa này có rate limit RIÊNG, tách khỏi cửa danh sách, nên dùng nhiều ở đây không bao giờ làm nghẹt lời gọi danh sách mà máy chủ bạn dựa vào.\n\nĐóng băng: cửa này vẫn chạy y như hôm nay cho app đã tích hợp, nhưng không thêm tính năng và không nhận app mới. Tích hợp mới phát video mở thẳng từ category space của chính mình và xem danh mục ở Marketplace trên cổng đối tác.",
        "security": [
          {
            "vendorApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The video `id` from the list endpoint. Not the slug, and not `order_code`.",
            "x-description-vi": "`id` của video lấy từ cửa danh sách. Không phải slug, cũng không phải `order_code`."
          }
        ],
        "responses": {
          "200": {
            "description": "The video, with links that expire in one hour.",
            "x-description-vi": "Video, kèm link hết hạn sau một giờ.",
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "row": {
                      "type": "object",
                      "description": "Same shape as one entry of `rows[]` in the list endpoint.",
                      "x-description-vi": "Cùng hình dạng với một phần tử của `rows[]` ở cửa danh sách."
                    },
                    "collections": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Every granted collection this video belongs to. `row.collection` is the first of them.",
                      "x-description-vi": "Mọi bộ sưu tập đã cấp có chứa video này. `row.collection` là cái đầu tiên."
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "refresh_before": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "row": {
                    "id": "3f6c1a52-8d0e-4b7a-9c21-5e4f2a7b9d10",
                    "slug": "dead-bug",
                    "title": "Dead bug",
                    "order_code": null,
                    "collection": "abs-program",
                    "position": 4,
                    "duration_ms": 42000,
                    "muscle_groups": [
                      "core"
                    ],
                    "equipment": [],
                    "difficulty": "beginner",
                    "kcal_est": null,
                    "mp4": {
                      "p480": "https://fit-playback.wandura.ai/…/480.mp4?auth_key=…",
                      "p720": null
                    }
                  },
                  "collections": [
                    "abs-program"
                  ],
                  "expires_at": "2026-10-02T13:00:00.000Z",
                  "refresh_before": "2026-10-02T12:45:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "`id` is not a uuid.",
            "x-description-vi": "`id` không phải uuid.",
            "x-error-codes": [
              {
                "error_code": null,
                "description": "`id` in the path is not a uuid.",
                "x-description-vi": "`id` trên đường dẫn không phải uuid."
              },
              {
                "error_code": "unsupported_version",
                "description": "The `Wandura-Version` header, or the version pinned to your key, is unknown or retired. The body adds `supported_versions` and `latest_version` (and `upgrade_to` when your pin was retired).",
                "x-description-vi": "Header `Wandura-Version`, hoặc bản ghim của key, là bản lạ hay đã ngừng. Body có thêm `supported_versions` và `latest_version` (và `upgrade_to` khi bản ghim đã ngừng)."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or revoked API key.",
            "x-description-vi": "Thiếu API key, key không tồn tại hoặc đã bị thu hồi.",
            "x-error-codes": [
              {
                "error_code": null,
                "description": "The `Authorization: Bearer` header is missing, or the key is unknown or revoked.",
                "x-description-vi": "Thiếu header `Authorization: Bearer`, hoặc key không tồn tại hay đã bị thu hồi."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "403": {
            "description": "The key lacks `videos:read`, is not bound to an app, or your organisation is suspended.",
            "x-description-vi": "Key thiếu `videos:read`, chưa gắn với app, hoặc tổ chức của bạn bị tạm ngưng.",
            "x-error-codes": [
              {
                "error_code": null,
                "description": "The key lacks the `videos:read` scope.",
                "x-description-vi": "Key thiếu scope `videos:read`."
              },
              {
                "error": "app_not_registered",
                "description": "The key is not bound to an app. Register the app on the Developers page and use that app's key.",
                "x-description-vi": "Key chưa gắn với app nào. Đăng ký app ở trang Developers rồi dùng key của app đó."
              },
              {
                "error": "org_suspended",
                "description": "Your organisation's access is suspended.",
                "x-description-vi": "Quyền truy cập của tổ chức bạn đang bị tạm ngưng."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "404": {
            "description": "Unknown, not granted to your organisation, in a collection that is not published, archived, or not playable yet — one answer for all of them, so the reply never tells you whether a video exists.",
            "x-description-vi": "Không tồn tại, chưa cấp cho tổ chức bạn, nằm trong bộ chưa publish, đã cất, hoặc chưa phát được — cùng một câu trả lời cho tất cả, nên câu trả lời không bao giờ nói cho bạn biết một video có tồn tại hay không.",
            "x-error-codes": [
              {
                "error": "video_not_found",
                "description": "Unknown, not granted, not published, archived, or not playable yet.",
                "x-description-vi": "Không tồn tại, chưa cấp, chưa publish, đã cất, hoặc chưa phát được."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "429": {
            "description": "Abuse ceiling reached (10800 requests per 60-second window, counted separately from the list endpoint).",
            "x-description-vi": "Đã chạm trần chống lạm dụng (10800 request mỗi cửa sổ 60 giây, đếm RIÊNG với cửa danh sách).",
            "x-error-codes": [
              {
                "error_code": "rate_limited",
                "description": "The rate limit for this endpoint was reached. It is counted separately from the list endpoint.",
                "x-description-vi": "Đã chạm rate limit của endpoint này. Trần này đếm riêng với cửa danh sách."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Window": {
                "$ref": "#/components/headers/RateLimitWindow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "500": {
            "description": "The video could not be read. Retry with backoff.",
            "x-description-vi": "Không đọc được video. Retry với backoff.",
            "x-error-codes": [
              {
                "error_code": null,
                "description": "The video could not be read. Retry with backoff.",
                "x-description-vi": "Không đọc được video. Retry với backoff."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          },
          "503": {
            "description": "Wandura could not verify the API key in time. The key is not wrong — retry after `Retry-After` seconds.",
            "x-description-vi": "Wandura chưa kiểm được API key kịp thời. Key không sai — thử lại sau số giây trong `Retry-After`.",
            "x-error-codes": [
              {
                "error_code": "service_unavailable",
                "description": "Wandura could not verify the API key in time. The key is not wrong: retry after `Retry-After` seconds, with backoff.",
                "x-description-vi": "Wandura chưa kiểm được API key kịp thời. Key không sai: thử lại sau số giây trong `Retry-After`, có backoff."
              }
            ],
            "headers": {
              "Wandura-API-Status": {
                "$ref": "#/components/headers/WanduraApiStatus"
              },
              "Wandura-Version": {
                "$ref": "#/components/headers/WanduraVersion"
              },
              "Wandura-Request-Id": {
                "$ref": "#/components/headers/WanduraRequestId"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControlNoStore"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "headers": {
      "CacheControlNoStore": {
        "description": "Always `no-store`. Do not cache these responses in shared caches.",
        "x-description-vi": "Luôn là `no-store`. Đừng lưu các response này trong cache dùng chung.",
        "schema": {
          "type": "string",
          "enum": [
            "no-store"
          ]
        }
      },
      "Deprecation": {
        "description": "Present only when this endpoint, or the dated version serving this response, is deprecated (RFC 9745): `@<unix seconds>` of the deprecation date. Log it in your monitoring.",
        "x-description-vi": "Chỉ có khi endpoint này, hoặc phiên bản ngày đang phục vụ response, đã bị ngừng hỗ trợ (RFC 9745): `@<giây unix>` của ngày ngừng. Hãy ghi nó vào hệ giám sát.",
        "schema": {
          "type": "string",
          "example": "@1798761600"
        }
      },
      "DeprecationLink": {
        "description": "Present with `Deprecation`: `<https://docs.wandura.ai/…>; rel=\"deprecation\"` (and `rel=\"sunset\"`) pointing to the page that explains what to use instead.",
        "x-description-vi": "Có kèm `Deprecation`: `<https://docs.wandura.ai/…>; rel=\"deprecation\"` (và `rel=\"sunset\"`) trỏ tới trang giải thích dùng gì thay thế.",
        "schema": {
          "type": "string"
        }
      },
      "RateLimitLimit": {
        "description": "Requests (for playback, tickets) allowed per window for this endpoint group and your organisation.",
        "x-description-vi": "Số request (với playback là số vé) được phép trong một cửa sổ cho nhóm endpoint này và tổ chức của bạn.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitWindow": {
        "description": "Window length in seconds.",
        "x-description-vi": "Độ dài cửa sổ tính bằng giây.",
        "schema": {
          "type": "integer"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying. Sent with 429 and with retryable 503 responses.",
        "x-description-vi": "Số giây cần chờ trước khi thử lại. Có ở response 429 và các response 503 nên thử lại.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "Sunset": {
        "description": "Present only when a deprecated endpoint or dated version has a date when it stops working (RFC 8594), as an HTTP date.",
        "x-description-vi": "Chỉ có khi endpoint hay phiên bản ngày đã bị ngừng có ngày thôi chạy (RFC 8594), dạng ngày HTTP.",
        "schema": {
          "type": "string",
          "example": "Fri, 01 Jan 2027 00:00:00 GMT"
        }
      },
      "WanduraApiStatus": {
        "description": "API status. `beta` today, on every response, success or error.",
        "x-description-vi": "Trạng thái API. Hiện là `beta`, ở mọi response, thành công hay lỗi.",
        "schema": {
          "type": "string",
          "example": "beta"
        }
      },
      "WanduraRequestId": {
        "description": "An opaque id for this request (`req_` followed by 26 characters). Every response carries it, errors included, and error bodies repeat it as `request_id`. Log it, and quote it when you contact Wandura about a request: it finds the exact request in our logs. Do not parse it.",
        "x-description-vi": "Mã định danh mờ của request này (`req_` và 26 ký tự). Mọi response đều có, kể cả lỗi, và body lỗi lặp lại nó ở `request_id`. Hãy ghi log nó, và gửi kèm khi liên hệ Wandura về một request: nó tìm đúng request đó trong log của chúng tôi. Đừng phân tích chuỗi này.",
        "schema": {
          "type": "string",
          "example": "req_01K6HZ3V1E8Q2N7T4W9XG5RBCD"
        }
      },
      "WanduraVersion": {
        "description": "The dated API version (YYYY-MM-DD) this response was produced in: the version pinned to your API key, or the one you sent in the `Wandura-Version` request header. Keys created before dated versions existed use `2026-10-02`.",
        "x-description-vi": "Phiên bản API theo ngày (YYYY-MM-DD) mà response này dùng: bản ghim vào API key của bạn, hoặc bản bạn gửi trong header request `Wandura-Version`. Key tạo trước khi có phiên bản ngày dùng `2026-10-02`.",
        "schema": {
          "type": "string",
          "example": "2026-10-02"
        }
      }
    },
    "parameters": {
      "WanduraSimulateHeader": {
        "in": "header",
        "name": "Wandura-Simulate",
        "required": false,
        "description": "Optional, test organisations only. Asks Wandura to answer with a simulated error so you can test how your app handles it, for example `rate_limited`, `service_unavailable` or `not_entitled:outside_window`. The simulated response has the same status, body and headers as the real one, plus a `Wandura-Simulated` header naming the scenario. It issues no ticket and does not count against your rate limits, except `partial_batch`, whose first item is a real ticket. Simulated responses have their own limit of 300 per minute per organisation. Keys of production organisations ignore this header completely. An unknown value, or a scenario this endpoint does not support, is ignored and the request runs normally. See the Testing guide for the list of scenarios.",
        "x-description-vi": "Tuỳ chọn, chỉ với tổ chức thử. Yêu cầu Wandura trả một lỗi giả lập để bạn thử cách app xử lý nó, ví dụ `rate_limited`, `service_unavailable` hay `not_entitled:outside_window`. Response giả lập có cùng status, body và header với response thật, cộng header `Wandura-Simulated` nêu tên kịch bản. Nó không cấp vé và không tính vào rate limit, trừ `partial_batch` (phần tử đầu là vé thật). Response giả lập có giới hạn riêng 300 lần mỗi phút mỗi tổ chức. Key của tổ chức production bỏ qua hoàn toàn header này. Giá trị lạ, hoặc kịch bản endpoint không hỗ trợ, bị bỏ qua và request chạy bình thường. Xem hướng dẫn Testing để biết danh sách kịch bản.",
        "schema": {
          "type": "string",
          "maxLength": 64,
          "example": "rate_limited"
        }
      },
      "WanduraVersionHeader": {
        "in": "header",
        "name": "Wandura-Version",
        "required": false,
        "description": "Optional. A dated API version (YYYY-MM-DD) to use for this request instead of the version pinned to your API key. Unknown or retired values answer 400 with `error_code` `unsupported_version`.",
        "x-description-vi": "Tuỳ chọn. Một phiên bản API theo ngày (YYYY-MM-DD) dùng cho request này thay cho bản ghim vào API key của bạn. Giá trị lạ hoặc đã ngừng trả 400 với `error_code` `unsupported_version`.",
        "schema": {
          "type": "string",
          "example": "2026-10-02"
        }
      }
    },
    "schemas": {
      "ApiVersionInfo": {
        "type": "object",
        "required": [
          "version",
          "released_at",
          "deprecated_at",
          "sunset_at",
          "summary"
        ],
        "properties": {
          "version": {
            "type": "string",
            "description": "Dated version, YYYY-MM-DD.",
            "x-description-vi": "Phiên bản ngày, YYYY-MM-DD."
          },
          "released_at": {
            "type": "string",
            "description": "Release date.",
            "x-description-vi": "Ngày phát hành."
          },
          "deprecated_at": {
            "type": "string",
            "nullable": true,
            "description": "Date it was deprecated, or `null`.",
            "x-description-vi": "Ngày bị ngừng hỗ trợ, hoặc `null`."
          },
          "sunset_at": {
            "type": "string",
            "nullable": true,
            "description": "Date it stops working, or `null`.",
            "x-description-vi": "Ngày thôi chạy, hoặc `null`."
          },
          "summary": {
            "type": "string",
            "description": "What changed in this version.",
            "x-description-vi": "Bản này đổi gì."
          }
        }
      },
      "Catalog": {
        "type": "object",
        "required": [
          "films",
          "episodes"
        ],
        "properties": {
          "films": {
            "type": "array",
            "description": "Films, sorted by `display_order`. Empty when your organisation has no current licence.",
            "x-description-vi": "Các phim, đã sắp theo `display_order`. Rỗng khi tổ chức của bạn không có giấy phép nào đang hiệu lực.",
            "items": {
              "$ref": "#/components/schemas/CatalogFilm"
            }
          },
          "episodes": {
            "type": "array",
            "description": "Episodes of those films, grouped by film and sorted by `episode_no` within each film.",
            "x-description-vi": "Các tập của những phim đó, nhóm theo phim và sắp theo `episode_no` trong mỗi phim.",
            "items": {
              "$ref": "#/components/schemas/CatalogEpisode"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Present only when you page the catalog (`limit` or `cursor` sent). Pass it back as `cursor`; `null` on the last page.",
            "x-description-vi": "Chỉ có khi bạn chia trang catalog (gửi `limit` hoặc `cursor`). Gửi lại làm `cursor`; `null` ở trang cuối."
          },
          "links": {
            "type": "object",
            "description": "Ready-made absolute URLs. Follow them instead of building URLs.",
            "x-description-vi": "URL tuyệt đối dựng sẵn. Đi theo chúng thay vì tự ráp URL.",
            "properties": {
              "self": {
                "type": "string",
                "description": "This request.",
                "x-description-vi": "Chính request này."
              },
              "next": {
                "type": "string",
                "nullable": true,
                "description": "The next page, or `null` (last page, or no paging).",
                "x-description-vi": "Trang sau, hoặc `null` (trang cuối, hoặc không chia trang)."
              },
              "playback": {
                "type": "string",
                "description": "Where to request playback.",
                "x-description-vi": "Nơi xin playback."
              },
              "config": {
                "type": "string",
                "description": "Your configuration.",
                "x-description-vi": "Cấu hình của bạn."
              }
            }
          }
        }
      },
      "CatalogEpisode": {
        "type": "object",
        "description": "A published episode, or a placeholder row (`status` `planned`, `id` null) for a planned episode that is not published yet.",
        "x-description-vi": "Một tập đã phát hành, hoặc một dòng placeholder (`status` là `planned`, `id` null) cho tập dự kiến chưa phát hành.",
        "required": [
          "id",
          "film_id",
          "episode_no",
          "title",
          "synopsis",
          "duration_ms",
          "poster_uri",
          "status",
          "stream_ready"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Episode ID. Use it as `episode_id` in playback. `null` on placeholder rows.",
            "x-description-vi": "Episode ID. Dùng làm `episode_id` khi xin playback. `null` ở dòng placeholder."
          },
          "film_id": {
            "type": "string",
            "format": "uuid",
            "description": "ID of the film the episode belongs to.",
            "x-description-vi": "ID của phim mà tập thuộc về."
          },
          "episode_no": {
            "type": "integer",
            "description": "Episode number, starting at 1.",
            "x-description-vi": "Số tập, bắt đầu từ 1."
          },
          "title": {
            "type": "string",
            "nullable": true,
            "description": "Episode title.",
            "x-description-vi": "Tên tập."
          },
          "synopsis": {
            "type": "string",
            "nullable": true,
            "description": "Episode description. Often `null`.",
            "x-description-vi": "Mô tả tập. Thường là `null`."
          },
          "duration_ms": {
            "type": "integer",
            "nullable": true,
            "description": "Duration in milliseconds.",
            "x-description-vi": "Thời lượng tính bằng mili giây."
          },
          "poster_uri": {
            "type": "string",
            "nullable": true,
            "description": "Episode thumbnail reference. Not guaranteed to be a directly loadable URL — use `poster_url`.",
            "x-description-vi": "Tham chiếu tới ảnh thumbnail của tập. Không đảm bảo là URL tải trực tiếp được — dùng `poster_url`."
          },
          "poster_url": {
            "type": "string",
            "nullable": true,
            "description": "The episode image as an absolute `https` URL you can load directly, or `null` (then use the film's `poster_url`).",
            "x-description-vi": "Ảnh tập dạng URL `https` tuyệt đối tải thẳng được, hoặc `null` (khi đó dùng `poster_url` của phim)."
          },
          "status": {
            "type": "string",
            "enum": [
              "published",
              "planned"
            ],
            "description": "`published`, or `planned` for a placeholder row.",
            "x-description-vi": "`published`, hoặc `planned` với dòng placeholder."
          },
          "stream_ready": {
            "type": "boolean",
            "description": "`true` when a ticket can be issued for the episode. Always `false` on placeholder rows.",
            "x-description-vi": "`true` khi có thể cấp vé cho tập. Luôn là `false` ở dòng placeholder."
          },
          "title_i18n": {
            "$ref": "#/components/schemas/I18nText"
          },
          "subtitles": {
            "type": "array",
            "description": "Subtitle languages available for the episode (the files come with the playback ticket). Empty on placeholder rows.",
            "x-description-vi": "Các ngôn ngữ phụ đề có cho tập (file đi kèm vé playback). Rỗng ở dòng placeholder.",
            "items": {
              "type": "object",
              "required": [
                "lang"
              ],
              "properties": {
                "lang": {
                  "type": "string",
                  "description": "BCP-47 code, for example `en`.",
                  "x-description-vi": "Mã BCP-47, ví dụ `en`."
                }
              }
            }
          }
        }
      },
      "CatalogFilm": {
        "type": "object",
        "description": "A film your organisation may stream right now.",
        "x-description-vi": "Một phim tổ chức của bạn đang được phép stream.",
        "required": [
          "id",
          "title",
          "slug",
          "synopsis_short",
          "synopsis_full",
          "genres",
          "language",
          "poster_url",
          "status",
          "display_order",
          "planned_episodes",
          "free_until_episode",
          "audience",
          "form",
          "setting",
          "images"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Film ID. It never changes: use it as your primary key, and as `film_ref` in playback.",
            "x-description-vi": "Film ID. Không bao giờ đổi: dùng làm khoá chính bên bạn, và làm `film_ref` khi xin playback."
          },
          "title": {
            "type": "string",
            "description": "Film title.",
            "x-description-vi": "Tên phim."
          },
          "slug": {
            "type": "string",
            "description": "URL-safe film identifier, unique across films. Wandura does not change it after the film is created. Also accepted as `film_ref` in playback.",
            "x-description-vi": "Định danh phim an toàn cho URL, không trùng giữa các phim. Wandura không đổi nó sau khi tạo phim. Cũng được chấp nhận làm `film_ref` khi xin playback."
          },
          "synopsis_short": {
            "type": "string",
            "nullable": true,
            "description": "Short description for cards. Length varies.",
            "x-description-vi": "Mô tả ngắn cho thẻ. Độ dài không cố định."
          },
          "synopsis_full": {
            "type": "string",
            "nullable": true,
            "description": "Full description for detail pages.",
            "x-description-vi": "Mô tả đầy đủ cho trang chi tiết."
          },
          "genres": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Genre labels. Can be empty.",
            "x-description-vi": "Nhãn thể loại. Có thể rỗng."
          },
          "language": {
            "type": "string",
            "nullable": true,
            "description": "Language of the film, as free text written by Wandura's content team (for example `en` or `English`). It is not a fixed list of codes: display it, do not parse it.",
            "x-description-vi": "Ngôn ngữ của phim, là chữ tự do do đội nội dung Wandura ghi (ví dụ `en` hoặc `English`). Không phải danh sách mã cố định: hiển thị nguyên, đừng phân tích."
          },
          "poster_url": {
            "type": "string",
            "nullable": true,
            "description": "Film poster URL.",
            "x-description-vi": "URL poster phim."
          },
          "status": {
            "type": "string",
            "description": "Always `published`.",
            "x-description-vi": "Luôn là `published`."
          },
          "display_order": {
            "type": "integer",
            "description": "Display position, smallest first. The array is already sorted by it.",
            "x-description-vi": "Vị trí hiển thị, nhỏ nhất trước. Mảng đã được sắp theo trường này."
          },
          "planned_episodes": {
            "type": "integer",
            "nullable": true,
            "description": "Total number of episodes planned. `null` when not set: then the film gets no placeholder rows, and `episodes` lists only its published episodes.",
            "x-description-vi": "Tổng số tập dự kiến. `null` khi chưa đặt: khi đó phim không có dòng placeholder nào, và `episodes` chỉ liệt kê các tập đã phát hành."
          },
          "free_until_episode": {
            "type": "integer",
            "nullable": true,
            "description": "Episodes with `episode_no` up to and including this number are free. `0` means no free episode. `null` means no number is set for the film: treat every episode as paid, and ask Wandura if you expected free episodes.",
            "x-description-vi": "Các tập có `episode_no` nhỏ hơn hoặc bằng số này là miễn phí. `0` nghĩa là không có tập miễn phí. `null` nghĩa là phim chưa được đặt số này: coi mọi tập là trả phí, và hỏi Wandura nếu bạn nghĩ phim có tập miễn phí."
          },
          "audience": {
            "type": "string",
            "nullable": true,
            "description": "`women`, `men`, `lgbtq`, or `null` if unclassified. New values may be added.",
            "x-description-vi": "`women`, `men`, `lgbtq`, hoặc `null` nếu chưa phân loại. Có thể có giá trị mới."
          },
          "form": {
            "type": "string",
            "nullable": true,
            "description": "`live_action`, `anime`, or `null` if unclassified. New values may be added.",
            "x-description-vi": "`live_action`, `anime`, hoặc `null` nếu chưa phân loại. Có thể có giá trị mới."
          },
          "setting": {
            "type": "string",
            "nullable": true,
            "description": "`modern`, `city`, `fantasy`, `campus`, `palace`, or `null` if unclassified. New values may be added.",
            "x-description-vi": "`modern`, `city`, `fantasy`, `campus`, `palace`, hoặc `null` nếu chưa phân loại. Có thể có giá trị mới."
          },
          "code": {
            "type": "string",
            "nullable": true,
            "description": "Wandura series code, for example `WD000016`. Stable and shared across every Wandura system (the playlist in your media space has this name). Use it with GET /api/delivery/v1/series/{code}.",
            "x-description-vi": "Mã bộ của Wandura, ví dụ `WD000016`. Ổn định và dùng chung trong mọi hệ thống của Wandura (playlist trong media space của bạn mang đúng tên này). Dùng nó với GET /api/delivery/v1/series/{code}."
          },
          "title_i18n": {
            "$ref": "#/components/schemas/I18nText"
          },
          "synopsis_short_i18n": {
            "$ref": "#/components/schemas/I18nText"
          },
          "synopsis_i18n": {
            "$ref": "#/components/schemas/I18nText"
          },
          "content_rating": {
            "type": "string",
            "nullable": true,
            "enum": [
              "all",
              "13+",
              "16+",
              "18+"
            ],
            "description": "Age rating: `all`, `13+`, `16+` or `18+`. `null` when not rated yet.",
            "x-description-vi": "Độ tuổi: `all`, `13+`, `16+` hoặc `18+`. `null` khi chưa chấm."
          },
          "is_completed": {
            "type": "boolean",
            "nullable": true,
            "description": "`true` when every planned episode is released, `false` while more are coming, `null` when the planned count is unknown.",
            "x-description-vi": "`true` khi mọi tập dự kiến đã ra, `false` khi còn tập sắp ra, `null` khi chưa biết tổng số tập dự kiến."
          },
          "release_date": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "Original release date, `YYYY-MM-DD`, or `null` when unknown.",
            "x-description-vi": "Ngày phát hành gốc, `YYYY-MM-DD`, hoặc `null` khi chưa biết."
          },
          "default_lang": {
            "type": "string",
            "nullable": true,
            "description": "Audio language of the film as a BCP-47 code (`en`, `vi`, `pt-BR`), or `null` when unknown. Prefer it over the free-text `language`.",
            "x-description-vi": "Ngôn ngữ âm thanh của phim dạng mã BCP-47 (`en`, `vi`, `pt-BR`), hoặc `null` khi chưa biết. Nên dùng trường này thay cho `language` (chữ tự do)."
          },
          "tags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MetadataTag"
            },
            "description": "Coded genre and theme tags. At most one has `is_main` `true` (the main genre, shown under the title). Can be empty.",
            "x-description-vi": "Thẻ thể loại và chủ đề có mã. Tối đa một thẻ có `is_main` là `true` (thể loại chính, hiện dưới tên phim). Có thể rỗng."
          },
          "people": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MetadataPerson"
            },
            "description": "Main cast or characters, leads first. Can be empty.",
            "x-description-vi": "Diễn viên hoặc nhân vật chính, vai chính trước. Có thể rỗng."
          },
          "trailer": {
            "type": "object",
            "nullable": true,
            "description": "The trailer video, or `null`. `vid` is the video ID in Wandura's media space.",
            "x-description-vi": "Video trailer, hoặc `null`. `vid` là video ID trong media space của Wandura.",
            "properties": {
              "vid": {
                "type": "string"
              }
            }
          },
          "metadata_version": {
            "type": "string",
            "nullable": true,
            "description": "Hash of the film's metadata (titles, synopses, rating, tags, people, artwork, episodes and their subtitles). Unchanged hash = nothing to update on your side.",
            "x-description-vi": "Mã băm metadata của phim (tên, tóm tắt, độ tuổi, thẻ, người, ảnh, các tập và phụ đề). Băm không đổi = bên bạn không có gì phải cập nhật."
          },
          "metadata_updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When `metadata_version` last changed.",
            "x-description-vi": "Lúc `metadata_version` đổi lần gần nhất."
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FilmImage"
            },
            "description": "Artwork for the film, one entry per kind Wandura has. A kind Wandura does not have is left out (never a `null` entry), so the array can be empty. Every `url` is an absolute `https` URL on a Wandura host that you can load directly. `poster_url` stays as it is.",
            "x-description-vi": "Ảnh của phim, mỗi loại Wandura có là một phần tử. Loại Wandura chưa có thì không có mặt (không bao giờ là phần tử `null`), nên mảng có thể rỗng. Mọi `url` là URL `https` tuyệt đối trên host của Wandura, tải thẳng được. `poster_url` giữ nguyên như cũ."
          }
        }
      },
      "CreativeFeedError": {
        "type": "object",
        "description": "Error envelope of the creatives endpoints (all errors except 429).",
        "x-description-vi": "Envelope lỗi của các endpoint creatives (mọi lỗi trừ 429).",
        "required": [
          "code",
          "message",
          "server_time"
        ],
        "properties": {
          "code": {
            "type": "integer",
            "description": "A four-digit error code. Branch on this.",
            "x-description-vi": "Mã lỗi bốn chữ số. Rẽ nhánh theo trường này."
          },
          "message": {
            "type": "string",
            "description": "Human-readable message. Do not parse it.",
            "x-description-vi": "Thông điệp cho người đọc. Đừng phân tích chuỗi này."
          },
          "server_time": {
            "type": "integer",
            "description": "Server time in Unix epoch seconds.",
            "x-description-vi": "Giờ server tính bằng giây Unix epoch."
          },
          "request_id": {
            "type": "string",
            "description": "The same value as the `Wandura-Request-Id` response header. Quote it when you contact Wandura.",
            "x-description-vi": "Cùng giá trị với header response `Wandura-Request-Id`. Gửi kèm khi liên hệ Wandura."
          }
        }
      },
      "CreativeRecord": {
        "type": "object",
        "description": "The data record of one approved creative. Never contains a media URL.",
        "x-description-vi": "Bản ghi dữ liệu của một creative đã duyệt. Không bao giờ chứa URL media.",
        "required": [
          "schema_version",
          "creative_id",
          "creative_code",
          "name",
          "status",
          "created_at",
          "approved_at",
          "updated_at",
          "org",
          "film",
          "episodes",
          "placement",
          "aspect",
          "duration_ms",
          "scenes",
          "hook",
          "formula",
          "rights",
          "lineage",
          "final"
        ],
        "properties": {
          "links": {
            "description": "Ready-made absolute URL of this creative's own endpoint (an API link, not a media URL).",
            "x-description-vi": "URL tuyệt đối dựng sẵn tới endpoint của chính creative này (link API, không phải URL media).",
            "allOf": [
              {
                "$ref": "#/components/schemas/SelfLink"
              }
            ]
          },
          "schema_version": {
            "type": "integer",
            "description": "Record schema version. Currently `1`. Additive fields do not change it.",
            "x-description-vi": "Phiên bản schema của bản ghi. Hiện là `1`. Thêm trường mới không làm đổi số này."
          },
          "creative_id": {
            "type": "string",
            "format": "uuid",
            "description": "Stable creative ID.",
            "x-description-vi": "ID ổn định của creative."
          },
          "creative_code": {
            "type": "string",
            "nullable": true,
            "description": "Immutable human-readable code. Treat it as opaque.",
            "x-description-vi": "Mã đọc được, không đổi. Coi nó là opaque."
          },
          "name": {
            "type": "string",
            "description": "Creative name.",
            "x-description-vi": "Tên creative."
          },
          "status": {
            "type": "string",
            "enum": [
              "approved",
              "pushed"
            ],
            "description": "`approved`, or `pushed` once delivered.",
            "x-description-vi": "`approved`, hoặc `pushed` khi đã giao."
          },
          "created_at": {
            "type": "integer",
            "description": "Creation time, Unix seconds.",
            "x-description-vi": "Thời điểm tạo, giây Unix."
          },
          "approved_at": {
            "type": "integer",
            "nullable": true,
            "description": "Approval time, Unix seconds.",
            "x-description-vi": "Thời điểm duyệt, giây Unix."
          },
          "updated_at": {
            "type": "integer",
            "description": "Last update, Unix seconds, rounded down.",
            "x-description-vi": "Lần cập nhật gần nhất, giây Unix, làm tròn xuống."
          },
          "org": {
            "type": "object",
            "required": [
              "slug"
            ],
            "properties": {
              "slug": {
                "type": "string",
                "description": "Your organisation's slug.",
                "x-description-vi": "Slug của tổ chức bạn."
              }
            }
          },
          "film": {
            "type": "object",
            "nullable": true,
            "description": "The film the creative is cut from.",
            "x-description-vi": "Phim mà creative được cắt từ đó.",
            "required": [
              "code",
              "title",
              "language",
              "genres"
            ],
            "properties": {
              "code": {
                "type": "string",
                "nullable": true,
                "description": "Film code.",
                "x-description-vi": "Mã phim."
              },
              "title": {
                "type": "string",
                "nullable": true,
                "description": "Film title.",
                "x-description-vi": "Tên phim."
              },
              "language": {
                "type": "string",
                "nullable": true,
                "description": "Film language.",
                "x-description-vi": "Ngôn ngữ của phim."
              },
              "genres": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Film genres. Can be empty.",
                "x-description-vi": "Thể loại phim. Có thể rỗng."
              }
            }
          },
          "episodes": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "Episode numbers used, without duplicates.",
            "x-description-vi": "Số thứ tự các tập được dùng, không trùng lặp."
          },
          "placement": {
            "type": "string",
            "nullable": true,
            "description": "`reels_9x16`, `feed_4x5` or `square_1x1`.",
            "x-description-vi": "`reels_9x16`, `feed_4x5` hoặc `square_1x1`."
          },
          "aspect": {
            "type": "string",
            "nullable": true,
            "description": "`9:16`, `4:5` or `1:1`, matching `placement`.",
            "x-description-vi": "`9:16`, `4:5` hoặc `1:1`, khớp với `placement`."
          },
          "duration_ms": {
            "type": "integer",
            "description": "Total length of the film clips used, in milliseconds. Can be shorter than the final video, because `scenes` lists only clips taken from film episodes.",
            "x-description-vi": "Tổng độ dài các clip phim được dùng, tính bằng mili giây. Có thể ngắn hơn video hoàn chỉnh, vì `scenes` chỉ liệt kê các clip lấy từ tập phim."
          },
          "scenes": {
            "type": "array",
            "description": "The film clips used, in order.",
            "x-description-vi": "Các clip phim được dùng, theo thứ tự.",
            "items": {
              "$ref": "#/components/schemas/CreativeScene"
            }
          },
          "hook": {
            "type": "object",
            "nullable": true,
            "description": "`action` and `line` of the first scene. `null` when there are no scenes.",
            "x-description-vi": "`action` và `line` của cảnh đầu tiên. `null` khi không có cảnh nào.",
            "properties": {
              "action": {
                "type": "string",
                "nullable": true
              },
              "line": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "formula": {
            "type": "object",
            "nullable": true,
            "description": "The formula the creative was built with.",
            "x-description-vi": "Formula dùng để dựng creative.",
            "required": [
              "name"
            ],
            "properties": {
              "name": {
                "type": "string",
                "description": "Formula name.",
                "x-description-vi": "Tên formula."
              }
            }
          },
          "rights": {
            "type": "object",
            "required": [
              "ends_at"
            ],
            "properties": {
              "ends_at": {
                "type": "integer",
                "nullable": true,
                "description": "Last day of your export rights for the film, as Unix seconds at 00:00 UTC of that day. Your rights include that whole day: stop using the creative after that UTC day ends, that is at `ends_at + 86400`. `null` when Wandura has no end date on record for your export rights.",
                "x-description-vi": "Ngày cuối cùng của quyền export phim, dạng giây Unix tại 00:00 UTC của ngày đó. Quyền của bạn gồm trọn ngày đó: ngừng dùng creative sau khi ngày UTC đó kết thúc, tức tại `ends_at + 86400`. `null` khi Wandura không có ngày kết thúc nào được ghi nhận cho quyền export của bạn."
              }
            }
          },
          "lineage": {
            "type": "object",
            "required": [
              "parent_creative_id"
            ],
            "properties": {
              "parent_creative_id": {
                "type": "string",
                "format": "uuid",
                "nullable": true,
                "description": "The creative this one was derived from.",
                "x-description-vi": "Creative gốc mà creative này được phái sinh từ đó."
              }
            }
          },
          "final": {
            "type": "object",
            "required": [
              "ready",
              "target"
            ],
            "properties": {
              "ready": {
                "type": "boolean",
                "description": "Whether the final render exists. A creative in status `pushed` has one.",
                "x-description-vi": "Bản render hoàn chỉnh đã có hay chưa. Creative ở trạng thái `pushed` luôn đã có."
              },
              "target": {
                "type": "object",
                "description": "Target render spec: `width`, `height`, `fps`, `codec`. Today it is the same for every creative (1080 × 1920, 30 fps, `h264`) and does not follow `placement`, so do not derive the frame size from it.",
                "x-description-vi": "Thông số render đích: `width`, `height`, `fps`, `codec`. Hiện giống nhau cho mọi creative (1080 × 1920, 30 fps, `h264`) và không theo `placement`, nên đừng suy khung hình từ nó.",
                "required": [
                  "width",
                  "height",
                  "fps",
                  "codec"
                ],
                "properties": {
                  "width": {
                    "type": "integer"
                  },
                  "height": {
                    "type": "integer"
                  },
                  "fps": {
                    "type": "integer"
                  },
                  "codec": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      },
      "CreativeScene": {
        "type": "object",
        "required": [
          "episode_no",
          "start_ms",
          "end_ms",
          "duration_ms",
          "action",
          "line"
        ],
        "properties": {
          "episode_no": {
            "type": "integer",
            "nullable": true,
            "description": "Episode the clip comes from.",
            "x-description-vi": "Tập chứa clip."
          },
          "start_ms": {
            "type": "integer",
            "description": "Clip start in the episode, milliseconds.",
            "x-description-vi": "Điểm bắt đầu clip trong tập, mili giây."
          },
          "end_ms": {
            "type": "integer",
            "description": "Clip end in the episode, milliseconds.",
            "x-description-vi": "Điểm kết thúc clip trong tập, mili giây."
          },
          "duration_ms": {
            "type": "integer",
            "description": "Clip length, milliseconds.",
            "x-description-vi": "Độ dài clip, mili giây."
          },
          "action": {
            "type": "string",
            "nullable": true,
            "description": "Action label of the scene, for example `reveal`, `argue`, `slap`, `kiss`, `embrace`, `cry`, `kneel`, `walk_away` or `violence`. `null` when the scene has no notable action. New labels may be added.",
            "x-description-vi": "Nhãn hành động của cảnh, ví dụ `reveal`, `argue`, `slap`, `kiss`, `embrace`, `cry`, `kneel`, `walk_away` hoặc `violence`. `null` khi cảnh không có hành động đáng chú ý. Có thể có nhãn mới được thêm vào."
          },
          "line": {
            "type": "string",
            "nullable": true,
            "description": "The spoken line or standout line of the scene.",
            "x-description-vi": "Câu thoại hoặc câu nổi bật của cảnh."
          }
        }
      },
      "DeliveryConfig": {
        "type": "object",
        "description": "Configuration that applies to the calling API key and its organisation. Contains nothing secret. New fields may be added at any time.",
        "x-description-vi": "Cấu hình áp cho API key đang gọi và tổ chức của nó. Không chứa gì bí mật. Trường mới có thể được thêm bất cứ lúc nào.",
        "required": [
          "api",
          "key",
          "rate_limits",
          "playback",
          "subtitles",
          "deprecations",
          "links"
        ],
        "properties": {
          "api": {
            "type": "object",
            "required": [
              "status",
              "path_version",
              "version",
              "pinned_version",
              "latest_version",
              "version_header",
              "supported_versions"
            ],
            "properties": {
              "status": {
                "type": "string",
                "description": "`beta` while the API is in beta.",
                "x-description-vi": "`beta` khi API còn beta."
              },
              "path_version": {
                "type": "string",
                "description": "Major version in the path, `v1`.",
                "x-description-vi": "Phiên bản lớn trong path, `v1`."
              },
              "version": {
                "type": "string",
                "description": "Dated version this response was produced in.",
                "x-description-vi": "Phiên bản ngày mà response này dùng."
              },
              "pinned_version": {
                "type": "string",
                "description": "Dated version pinned to the calling key.",
                "x-description-vi": "Phiên bản ngày ghim vào key đang gọi."
              },
              "latest_version": {
                "type": "string",
                "description": "Newest dated version.",
                "x-description-vi": "Phiên bản ngày mới nhất."
              },
              "version_header": {
                "type": "string",
                "description": "Name of the version header, `Wandura-Version`.",
                "x-description-vi": "Tên header phiên bản, `Wandura-Version`."
              },
              "supported_versions": {
                "type": "array",
                "description": "Dated versions you can use today.",
                "x-description-vi": "Các phiên bản ngày dùng được hôm nay.",
                "items": {
                  "$ref": "#/components/schemas/ApiVersionInfo"
                }
              }
            }
          },
          "key": {
            "type": "object",
            "required": [
              "scopes"
            ],
            "properties": {
              "scopes": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Scopes of the calling key.",
                "x-description-vi": "Scope của key đang gọi."
              },
              "test_organisation": {
                "type": "boolean",
                "description": "`true` when the calling key belongs to a test organisation (its traffic is not counted as views or usage, and it may send `Wandura-Simulate`).",
                "x-description-vi": "`true` khi key đang gọi thuộc một tổ chức thử (lưu lượng của nó không tính là lượt xem hay usage, và nó được gửi `Wandura-Simulate`)."
              }
            }
          },
          "rate_limits": {
            "type": "object",
            "required": [
              "window_seconds",
              "doors"
            ],
            "properties": {
              "window_seconds": {
                "type": "integer",
                "description": "Length of the counting window in seconds.",
                "x-description-vi": "Độ dài cửa sổ đếm, tính bằng giây."
              },
              "doors": {
                "type": "object",
                "description": "Per endpoint group: `catalog`, `playback`, `creatives`, `videos`, `config`.",
                "x-description-vi": "Theo nhóm endpoint: `catalog`, `playback`, `creatives`, `videos`, `config`.",
                "additionalProperties": {
                  "$ref": "#/components/schemas/DoorLimit"
                }
              }
            }
          },
          "playback": {
            "type": "object",
            "required": [
              "domains",
              "protection",
              "grant_ttl_seconds",
              "manifest_url_ttl_seconds",
              "batch"
            ],
            "properties": {
              "domains": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Domains your playback URLs use.",
                "x-description-vi": "Tên miền mà URL phát của bạn dùng."
              },
              "protection": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Protection levels your organisation can receive.",
                "x-description-vi": "Mức bảo vệ tổ chức của bạn có thể nhận."
              },
              "grant_ttl_seconds": {
                "type": "integer",
                "description": "Lifetime of a play token, in seconds.",
                "x-description-vi": "Thời gian sống của play token, tính bằng giây."
              },
              "manifest_url_ttl_seconds": {
                "type": "integer",
                "description": "Lifetime of a signed manifest URL, in seconds.",
                "x-description-vi": "Thời gian sống của manifest URL đã ký, tính bằng giây."
              },
              "batch": {
                "type": "object",
                "required": [
                  "max_episodes",
                  "max_next"
                ],
                "properties": {
                  "max_episodes": {
                    "type": "integer",
                    "description": "Most episodes one playback call may request.",
                    "x-description-vi": "Số tập nhiều nhất một lần gọi playback được xin."
                  },
                  "max_next": {
                    "type": "integer",
                    "description": "Largest `next` value in one playback call.",
                    "x-description-vi": "Giá trị `next` lớn nhất trong một lần gọi playback."
                  }
                }
              }
            }
          },
          "subtitles": {
            "type": "object",
            "required": [
              "format",
              "languages"
            ],
            "properties": {
              "format": {
                "type": "string",
                "description": "`webvtt`.",
                "x-description-vi": "`webvtt`."
              },
              "languages": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Subtitle languages present on films you are licensed to (updated within 5 minutes).",
                "x-description-vi": "Ngôn ngữ phụ đề đang có trên phim bạn được cấp phép (cập nhật trong vòng 5 phút)."
              }
            }
          },
          "deprecations": {
            "type": "array",
            "description": "Every announced deprecation. Empty when there is none.",
            "x-description-vi": "Mọi thông báo ngừng hỗ trợ. Rỗng khi không có.",
            "items": {
              "$ref": "#/components/schemas/DeprecationNotice"
            }
          },
          "links": {
            "type": "object",
            "description": "Absolute URLs: `self`, every endpoint, `docs`, `evolution` and `changes` (the machine-readable changelog).",
            "x-description-vi": "URL tuyệt đối: `self`, mọi endpoint, `docs`, `evolution` và `changes` (changelog cho máy đọc).",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "DeliveryError": {
        "type": "object",
        "description": "Flat error body used by every endpoint except the creatives envelope (and also by the creatives endpoints for 429). Branch on `error_code`; `error` is for humans and must not be parsed. Unknown `error_code` values may be added within v1: always keep a default branch.",
        "x-description-vi": "Body lỗi phẳng mà mọi endpoint dùng, trừ envelope của creatives (creatives vẫn dùng body này cho 429). Rẽ nhánh theo `error_code`; `error` dành cho người đọc, đừng phân tích. Trong v1 có thể có thêm giá trị `error_code` mới: luôn giữ một nhánh mặc định.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable message. Do not parse it.",
            "x-description-vi": "Thông điệp cho người đọc. Đừng phân tích chuỗi này."
          },
          "error_code": {
            "type": "string",
            "description": "Machine-readable code. Branch on this. Absent on some errors, for example 401.",
            "x-description-vi": "Mã cho máy đọc. Rẽ nhánh theo trường này. Không có ở một số lỗi, ví dụ 401."
          },
          "reason": {
            "type": "string",
            "description": "Present with `not_entitled` (the detailed reason, for logs and support), and with `rate_limited` when only prefetch was paused (`prefetch_shed`).",
            "x-description-vi": "Có khi `error_code` là `not_entitled` (lý do chi tiết, để ghi log và hỗ trợ), và khi `rate_limited` chỉ vì prefetch bị tạm dừng (`prefetch_shed`)."
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Present with `invalid_device_info`. Names of the device values that look invalid.",
            "x-description-vi": "Có khi `error_code` là `invalid_device_info`. Tên các giá trị thiết bị trông không hợp lệ."
          },
          "detail": {
            "type": "string",
            "description": "Extra explanation on some errors, for example `app_not_registered`.",
            "x-description-vi": "Giải thích thêm ở một số lỗi, ví dụ `app_not_registered`."
          },
          "supported_versions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Present with `unsupported_version`. Dated API versions you can use today.",
            "x-description-vi": "Có khi `error_code` là `unsupported_version`. Các phiên bản API theo ngày dùng được hôm nay."
          },
          "latest_version": {
            "type": "string",
            "description": "Present with `unsupported_version`. The newest dated API version.",
            "x-description-vi": "Có khi `error_code` là `unsupported_version`. Phiên bản API theo ngày mới nhất."
          },
          "upgrade_to": {
            "type": "string",
            "description": "Present with `unsupported_version` when the version pinned to your key has been retired. The version to move to.",
            "x-description-vi": "Có khi `error_code` là `unsupported_version` và bản ghim của key đã ngừng. Phiên bản cần chuyển sang."
          },
          "request_id": {
            "type": "string",
            "description": "The same value as the `Wandura-Request-Id` response header. Quote it when you contact Wandura.",
            "x-description-vi": "Cùng giá trị với header response `Wandura-Request-Id`. Gửi kèm khi liên hệ Wandura."
          }
        }
      },
      "DeprecationNotice": {
        "type": "object",
        "required": [
          "id",
          "endpoint",
          "field",
          "deprecated_at",
          "sunset_at",
          "replacement",
          "info_url",
          "summary"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier of the notice.",
            "x-description-vi": "Mã ổn định của thông báo."
          },
          "endpoint": {
            "type": "string",
            "description": "`METHOD /path` of the affected endpoint.",
            "x-description-vi": "`METHOD /path` của endpoint bị ảnh hưởng."
          },
          "field": {
            "type": "string",
            "nullable": true,
            "description": "The deprecated response field, or `null` when the whole endpoint is deprecated.",
            "x-description-vi": "Trường response bị ngừng, hoặc `null` khi cả endpoint bị ngừng."
          },
          "deprecated_at": {
            "type": "string",
            "description": "Deprecation date, YYYY-MM-DD.",
            "x-description-vi": "Ngày ngừng hỗ trợ, YYYY-MM-DD."
          },
          "sunset_at": {
            "type": "string",
            "nullable": true,
            "description": "Date it stops working, or `null`.",
            "x-description-vi": "Ngày thôi chạy, hoặc `null`."
          },
          "replacement": {
            "type": "string",
            "nullable": true,
            "description": "What to use instead, if anything.",
            "x-description-vi": "Dùng gì thay thế, nếu có."
          },
          "info_url": {
            "type": "string",
            "description": "Documentation page explaining the change.",
            "x-description-vi": "Trang tài liệu giải thích thay đổi."
          },
          "summary": {
            "type": "string",
            "description": "One-sentence summary.",
            "x-description-vi": "Tóm tắt một câu."
          }
        }
      },
      "DoorLimit": {
        "type": "object",
        "required": [
          "limit",
          "unit"
        ],
        "properties": {
          "limit": {
            "type": "integer",
            "nullable": true,
            "description": "Ceiling per window for your organisation, or `null` when not limited.",
            "x-description-vi": "Trần mỗi cửa sổ cho tổ chức của bạn, hoặc `null` khi không giới hạn."
          },
          "unit": {
            "type": "string",
            "enum": [
              "requests",
              "tickets"
            ],
            "description": "`tickets` for playback (one episode in a batch counts one), `requests` otherwise.",
            "x-description-vi": "`tickets` cho playback (mỗi tập trong lô tính một), còn lại là `requests`."
          }
        }
      },
      "FilmImage": {
        "type": "object",
        "description": "One image of a film.",
        "x-description-vi": "Một ảnh của phim.",
        "required": [
          "kind",
          "url",
          "width",
          "height"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "description": "What the image is for: `poster_3_4` (3:4 card poster), `cover_9_16` (9:16 full-screen cover, the same image as `poster_url`), `hero_16_9` (16:9 banner) or `title_treatment` (the title as artwork). New kinds may be added: skip a kind you do not know.",
            "x-description-vi": "Ảnh dùng cho việc gì: `poster_3_4` (poster thẻ 3:4), `cover_9_16` (ảnh bìa toàn màn hình 9:16, cùng ảnh với `poster_url`), `hero_16_9` (banner 16:9) hoặc `title_treatment` (tên phim dạng ảnh). Có thể có loại mới: bỏ qua loại bạn không biết."
          },
          "lang": {
            "type": "string",
            "nullable": true,
            "description": "Language of text burned into the image (BCP-47), or `null` when the image suits every language. Prefer the entry matching your user's language, else the `null` one.",
            "x-description-vi": "Ngôn ngữ của chữ in sẵn trong ảnh (BCP-47), hoặc `null` khi ảnh dùng được cho mọi ngôn ngữ. Ưu tiên phần tử khớp ngôn ngữ người dùng, không có thì lấy phần tử `null`."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Absolute `https` URL of the image on a Wandura host. Load it directly; it needs no signing.",
            "x-description-vi": "URL `https` tuyệt đối của ảnh trên host của Wandura. Tải thẳng được, không cần ký."
          },
          "width": {
            "type": "integer",
            "nullable": true,
            "description": "Width in pixels, measured from the file. `null` when Wandura has not recorded the size.",
            "x-description-vi": "Chiều rộng tính bằng pixel, đo từ chính file. `null` khi Wandura chưa ghi kích thước."
          },
          "height": {
            "type": "integer",
            "nullable": true,
            "description": "Height in pixels, measured from the file. `null` when Wandura has not recorded the size.",
            "x-description-vi": "Chiều cao tính bằng pixel, đo từ chính file. `null` khi Wandura chưa ghi kích thước."
          }
        }
      },
      "FilmStatus": {
        "type": "string",
        "nullable": true,
        "enum": [
          "draft",
          "published",
          "archived"
        ],
        "description": "Production state of the film in Wandura's catalogue: `published` (released), `draft` (in progress, not released yet) or `archived` (taken down). Not the same as `status`, which says whether you may stream it. Whether every planned episode is out is `is_completed`. New values may be added.",
        "x-description-vi": "Trạng thái của phim trong kho Wandura: `published` (đã phát hành), `draft` (đang làm, chưa phát hành) hoặc `archived` (đã gỡ). Khác `status`, trường cho biết bạn có được stream hay không. Đã ra đủ tập dự kiến hay chưa là `is_completed`. Có thể có giá trị mới."
      },
      "FilmTier": {
        "type": "string",
        "nullable": true,
        "enum": [
          "S",
          "A",
          "B",
          "C",
          "D",
          "E",
          "F"
        ],
        "description": "Wandura's quality tier of the film, `S` highest to `F` lowest, or `null` when not rated yet. It is not an age rating (see `content_rating`).",
        "x-description-vi": "Hạng chất lượng của phim do Wandura chấm, `S` cao nhất tới `F` thấp nhất, hoặc `null` khi chưa chấm. Không phải phân loại độ tuổi (xem `content_rating`)."
      },
      "I18nText": {
        "type": "object",
        "description": "Text by language: keys are BCP-47 codes, values are the text, for example `{\"en\": \"The Heiress Returns\"}`. Today Wandura fills `en`; more languages may appear. A missing key means no text in that language: fall back to `en`. Empty object when there is no text.",
        "x-description-vi": "Chữ theo ngôn ngữ: khoá là mã BCP-47, giá trị là chữ, ví dụ `{\"en\": \"The Heiress Returns\"}`. Hiện Wandura điền `en`; có thể có thêm ngôn ngữ. Thiếu khoá nghĩa là chưa có chữ ngôn ngữ đó: lùi về `en`. Object rỗng khi không có chữ.",
        "additionalProperties": {
          "type": "string"
        }
      },
      "MetadataPerson": {
        "type": "object",
        "required": [
          "name",
          "role"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Name of the actor or character.",
            "x-description-vi": "Tên diễn viên hoặc nhân vật."
          },
          "role": {
            "type": "string",
            "enum": [
              "lead",
              "support"
            ],
            "description": "`lead` or `support`.",
            "x-description-vi": "`lead` hoặc `support`."
          },
          "description": {
            "type": "string",
            "nullable": true,
            "description": "One-line description, or `null`.",
            "x-description-vi": "Mô tả một câu, hoặc `null`."
          },
          "avatar_url": {
            "type": "string",
            "nullable": true,
            "description": "Absolute `https` image URL, or `null`.",
            "x-description-vi": "URL ảnh `https` tuyệt đối, hoặc `null`."
          }
        }
      },
      "MetadataTag": {
        "type": "object",
        "required": [
          "slug",
          "name",
          "type",
          "is_main"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "Stable code of the tag, for example `romance` or `enemies-to-lovers`. Map it to your own tag list by this value, not by `name`.",
            "x-description-vi": "Mã ổn định của thẻ, ví dụ `romance` hoặc `enemies-to-lovers`. Ánh xạ sang danh sách thẻ của bạn theo giá trị này, không theo `name`."
          },
          "name": {
            "type": "string",
            "description": "English display name.",
            "x-description-vi": "Tên hiển thị tiếng Anh."
          },
          "name_i18n": {
            "$ref": "#/components/schemas/I18nText"
          },
          "type": {
            "type": "string",
            "description": "`genre`, `theme` or `system`. New types may be added.",
            "x-description-vi": "`genre`, `theme` hoặc `system`. Có thể có loại mới."
          },
          "is_main": {
            "type": "boolean",
            "description": "`true` for the main genre of the film (at most one).",
            "x-description-vi": "`true` với thể loại chính của phim (tối đa một)."
          }
        }
      },
      "OpenVideo": {
        "type": "object",
        "required": [
          "id",
          "slug",
          "title",
          "order_code",
          "collection",
          "position",
          "duration_ms",
          "muscle_groups",
          "equipment",
          "difficulty",
          "kcal_est",
          "mp4"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Video ID. It never changes; sync on it.",
            "x-description-vi": "Video ID. Không bao giờ đổi; đồng bộ theo trường này."
          },
          "slug": {
            "type": "string",
            "description": "URL-safe video identifier.",
            "x-description-vi": "Định danh video an toàn cho URL."
          },
          "title": {
            "type": "string",
            "description": "Video title.",
            "x-description-vi": "Tên video."
          },
          "order_code": {
            "type": "string",
            "nullable": true,
            "description": "Production order code (for example `ABS_01_E01`), matching your order file character for character, or `null` for a video that is not in it. Use it to join with the order file once.",
            "x-description-vi": "Mã đơn sản xuất (ví dụ `ABS_01_E01`), khớp từng ký tự với file đặt hàng của bạn, hoặc `null` với video không có trong đó. Dùng nó để ghép một lần với file đặt hàng."
          },
          "collection": {
            "type": "string",
            "description": "Code of the collection this row comes from. A video in two granted collections appears once under each.",
            "x-description-vi": "Mã bộ sưu tập của dòng này. Video thuộc hai bộ được cấp sẽ xuất hiện một lần dưới mỗi bộ."
          },
          "position": {
            "type": "integer",
            "description": "Position of the video in its collection. Rows come back in collection order.",
            "x-description-vi": "Vị trí của video trong bộ sưu tập. Các dòng trả về theo thứ tự của bộ."
          },
          "duration_ms": {
            "type": "integer",
            "nullable": true,
            "description": "Duration in milliseconds.",
            "x-description-vi": "Thời lượng tính bằng mili giây."
          },
          "muscle_groups": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Muscle groups worked. Can be empty.",
            "x-description-vi": "Nhóm cơ được tập. Có thể rỗng."
          },
          "equipment": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Equipment needed. Can be empty.",
            "x-description-vi": "Dụng cụ cần có. Có thể rỗng."
          },
          "difficulty": {
            "type": "string",
            "nullable": true,
            "description": "Difficulty label.",
            "x-description-vi": "Nhãn độ khó."
          },
          "kcal_est": {
            "type": "integer",
            "nullable": true,
            "description": "Estimated calories burned.",
            "x-description-vi": "Lượng calo ước tính tiêu hao."
          },
          "mp4": {
            "type": "object",
            "required": [
              "p480",
              "p720"
            ],
            "description": "Signed MP4 links, valid until `expires_at`. Play them as received; never cache them in your app.",
            "x-description-vi": "Link MP4 đã ký, dùng được tới `expires_at`. Phát nguyên như nhận được; đừng lưu chúng trong app.",
            "properties": {
              "p480": {
                "type": "string",
                "description": "480p MP4 link. Play this by default.",
                "x-description-vi": "Link MP4 480p. Mặc định phát bản này."
              },
              "p720": {
                "type": "string",
                "nullable": true,
                "description": "720p MP4 link, or `null` until that rendition exists.",
                "x-description-vi": "Link MP4 720p, hoặc `null` cho tới khi có bản này."
              }
            }
          }
        }
      },
      "PageLinks": {
        "type": "object",
        "description": "Ready-made absolute URLs of a list response. Follow them instead of building URLs.",
        "x-description-vi": "URL tuyệt đối dựng sẵn của một response dạng danh sách. Đi theo chúng thay vì tự ráp URL.",
        "properties": {
          "self": {
            "type": "string",
            "description": "This request.",
            "x-description-vi": "Chính request này."
          },
          "next": {
            "type": "string",
            "nullable": true,
            "description": "The next page, or `null` on the last page.",
            "x-description-vi": "Trang sau, hoặc `null` ở trang cuối."
          }
        }
      },
      "PlaylistCategory": {
        "type": "object",
        "required": [
          "slug",
          "name"
        ],
        "description": "The Category of the playlist (for example Fitness).",
        "x-description-vi": "Category của playlist (ví dụ Fitness).",
        "properties": {
          "slug": {
            "type": "string",
            "description": "Stable code of the Category, for example `fitness`. Also the `category` filter of the list.",
            "x-description-vi": "Mã ổn định của Category, ví dụ `fitness`. Cũng là bộ lọc `category` của danh sách."
          },
          "name": {
            "type": "string",
            "description": "Display name.",
            "x-description-vi": "Tên hiển thị."
          }
        }
      },
      "PlaylistClip": {
        "type": "object",
        "required": [
          "position",
          "id",
          "title",
          "description",
          "duration_ms",
          "muscle_groups",
          "equipment",
          "difficulty",
          "kcal_est",
          "order_code",
          "poster",
          "ready",
          "vid",
          "format",
          "playlist_id",
          "playlist_name"
        ],
        "properties": {
          "position": {
            "type": "integer",
            "description": "Position in the playlist. Clips come back in this order.",
            "x-description-vi": "Vị trí trong playlist. Các clip trả về theo thứ tự này."
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Clip ID. It never changes; sync on it. The same clip can sit in several playlists.",
            "x-description-vi": "Clip ID. Không bao giờ đổi; đồng bộ theo trường này. Một clip có thể nằm trong nhiều playlist."
          },
          "title": {
            "type": "string",
            "description": "Clip title.",
            "x-description-vi": "Tên clip."
          },
          "description": {
            "type": "string",
            "nullable": true,
            "description": "Short description, or `null`.",
            "x-description-vi": "Mô tả ngắn, hoặc `null`."
          },
          "duration_ms": {
            "type": "integer",
            "nullable": true,
            "description": "Duration in milliseconds.",
            "x-description-vi": "Thời lượng tính bằng mili giây."
          },
          "muscle_groups": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Muscle groups worked. Can be empty.",
            "x-description-vi": "Nhóm cơ được tập. Có thể rỗng."
          },
          "equipment": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Equipment needed. Can be empty.",
            "x-description-vi": "Dụng cụ cần có. Có thể rỗng."
          },
          "difficulty": {
            "type": "string",
            "nullable": true,
            "description": "`beginner`, `intermediate` or `advanced`, or `null`.",
            "x-description-vi": "`beginner`, `intermediate` hoặc `advanced`, hoặc `null`."
          },
          "kcal_est": {
            "type": "integer",
            "nullable": true,
            "description": "Estimated calories burned.",
            "x-description-vi": "Lượng calo ước tính tiêu hao."
          },
          "order_code": {
            "type": "string",
            "nullable": true,
            "description": "Production order code (for example `ABS_01_E01`), matching your order file character for character, or `null`.",
            "x-description-vi": "Mã đơn sản xuất (ví dụ `ABS_01_E01`), khớp từng ký tự với file đặt hàng của bạn, hoặc `null`."
          },
          "poster": {
            "$ref": "#/components/schemas/PlaylistImage"
          },
          "ready": {
            "type": "boolean",
            "description": "`true` when the clip is copied and playable in your media space (`media.space`).",
            "x-description-vi": "`true` khi clip đã chép xong và phát được trong media space của bạn (`media.space`)."
          },
          "vid": {
            "type": "string",
            "nullable": true,
            "description": "Video ID of the clip in your media space, or `null` while it is not copied (or the playlist is not granted).",
            "x-description-vi": "Video ID của clip trong media space của bạn, hoặc `null` khi chưa chép (hoặc playlist chưa được cấp)."
          },
          "format": {
            "type": "string",
            "nullable": true,
            "enum": [
              "hls",
              "mp4"
            ],
            "description": "How the copy is packaged: `hls` (clips of 30 seconds or more) or `mp4` (shorter clips). `null` while not copied.",
            "x-description-vi": "Dạng đóng gói của bản chép: `hls` (clip từ 30 giây) hoặc `mp4` (clip ngắn hơn). `null` khi chưa chép."
          },
          "playlist_id": {
            "type": "string",
            "nullable": true,
            "description": "ID of the playlist in your media space that holds this clip, or `null`.",
            "x-description-vi": "ID playlist trong media space của bạn đang chứa clip này, hoặc `null`."
          },
          "playlist_name": {
            "type": "string",
            "nullable": true,
            "description": "Name of that playlist, or `null`.",
            "x-description-vi": "Tên playlist đó, hoặc `null`."
          }
        }
      },
      "PlaylistDetail": {
        "type": "object",
        "required": [
          "code",
          "status",
          "title",
          "description",
          "category",
          "group",
          "updated_at",
          "content_version",
          "metadata_version",
          "granted",
          "media",
          "images",
          "clips"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Playlist code.",
            "x-description-vi": "Mã playlist."
          },
          "status": {
            "type": "string",
            "enum": [
              "active"
            ],
            "description": "Always `active` (a playlist not in the library returns 404).",
            "x-description-vi": "Luôn là `active` (playlist không có trong kho trả 404)."
          },
          "title": {
            "type": "string",
            "description": "Playlist title.",
            "x-description-vi": "Tên playlist."
          },
          "description": {
            "type": "string",
            "nullable": true,
            "description": "Playlist description, or `null`.",
            "x-description-vi": "Mô tả playlist, hoặc `null`."
          },
          "category": {
            "$ref": "#/components/schemas/PlaylistCategory"
          },
          "group": {
            "$ref": "#/components/schemas/PlaylistGroup"
          },
          "updated_at": {
            "type": "integer",
            "description": "Last change in Unix epoch seconds.",
            "x-description-vi": "Lần đổi gần nhất tính bằng giây Unix epoch."
          },
          "content_version": {
            "type": "string",
            "description": "Hash of this whole response. Store it; when the list shows the same value there is nothing to fetch.",
            "x-description-vi": "Mã băm của toàn bộ response này. Lưu lại; danh sách trả cùng giá trị thì không có gì phải tải."
          },
          "metadata_version": {
            "type": "string",
            "nullable": true,
            "description": "Hash of the playlist metadata alone (the same for every organisation).",
            "x-description-vi": "Mã băm của riêng metadata playlist (giống nhau với mọi tổ chức)."
          },
          "granted": {
            "type": "boolean",
            "description": "`true` when your organisation is licensed for this playlist.",
            "x-description-vi": "`true` khi tổ chức của bạn được cấp playlist này."
          },
          "media": {
            "type": "object",
            "nullable": true,
            "required": [
              "space",
              "playlist_id",
              "playlist_name",
              "playlists"
            ],
            "description": "Where your copies live, or `null` when the playlist is not granted. Playback stays between your app and your media space; this API never returns playback links.",
            "x-description-vi": "Bản chép của bạn nằm ở đâu, hoặc `null` khi playlist chưa được cấp. Việc phát diễn ra giữa app và media space của bạn; API này không bao giờ trả link phát.",
            "properties": {
              "space": {
                "type": "string",
                "nullable": true,
                "description": "Your media space for this Category, or `null` while it is not set up.",
                "x-description-vi": "Media space của bạn cho Category này, hoặc `null` khi chưa dựng."
              },
              "playlist_id": {
                "type": "string",
                "nullable": true,
                "description": "ID of the playlist in `space` (named after `code`), or `null` until it exists.",
                "x-description-vi": "ID playlist trong `space` (đặt tên theo `code`), hoặc `null` cho tới khi nó tồn tại."
              },
              "playlist_name": {
                "type": "string",
                "nullable": true,
                "description": "Name of that playlist, or `null`.",
                "x-description-vi": "Tên playlist đó, hoặc `null`."
              },
              "playlists": {
                "type": "array",
                "description": "Every playlist holding the clips in order (at most 100 videos each, so a longer playlist continues in `<code>-2`…).",
                "x-description-vi": "Mọi playlist chứa các clip theo thứ tự (mỗi cái tối đa 100 video, nên playlist dài hơn nối tiếp ở `<code>-2`…).",
                "items": {
                  "type": "object",
                  "required": [
                    "id",
                    "name"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "images": {
            "type": "array",
            "description": "Artwork of the playlist. Today one `cover` (the poster of the first clip); more kinds may be added. Empty when there is none.",
            "x-description-vi": "Ảnh của playlist. Hiện có một `cover` (poster của clip đầu tiên); có thể có thêm loại. Rỗng khi không có.",
            "items": {
              "type": "object",
              "required": [
                "kind",
                "source",
                "url",
                "expires_at",
                "space",
                "uri"
              ],
              "description": "One image; `source`, `url`, `expires_at`, `space` and `uri` mean the same as in a clip `poster`.",
              "x-description-vi": "Một ảnh; `source`, `url`, `expires_at`, `space` và `uri` mang cùng nghĩa như `poster` của clip.",
              "properties": {
                "kind": {
                  "type": "string",
                  "enum": [
                    "cover"
                  ],
                  "description": "`cover`. New kinds may be added.",
                  "x-description-vi": "`cover`. Có thể có loại mới."
                },
                "source": {
                  "type": "string",
                  "enum": [
                    "media_space",
                    "wandura_cdn"
                  ],
                  "description": "`media_space` or `wandura_cdn`. New values may be added.",
                  "x-description-vi": "`media_space` hoặc `wandura_cdn`. Có thể có giá trị mới."
                },
                "url": {
                  "type": "string",
                  "nullable": true,
                  "description": "Image URL when `source` is `wandura_cdn`, else `null`. Download it and host your own copy.",
                  "x-description-vi": "URL ảnh khi `source` là `wandura_cdn`, ngược lại `null`. Tải về và tự lưu bản của bạn."
                },
                "expires_at": {
                  "type": "integer",
                  "nullable": true,
                  "description": "When `url` stops working, in Unix epoch seconds, or `null`.",
                  "x-description-vi": "Lúc `url` hết hiệu lực, tính bằng giây Unix epoch, hoặc `null`."
                },
                "space": {
                  "type": "string",
                  "nullable": true,
                  "description": "Your media space holding the image, or `null`.",
                  "x-description-vi": "Media space của bạn đang chứa ảnh, hoặc `null`."
                },
                "uri": {
                  "type": "string",
                  "nullable": true,
                  "description": "The image's URI inside `space`, or `null`.",
                  "x-description-vi": "URI của ảnh trong `space`, hoặc `null`."
                }
              }
            }
          },
          "clips": {
            "type": "array",
            "description": "Clips in playlist order.",
            "x-description-vi": "Các clip theo thứ tự playlist.",
            "items": {
              "$ref": "#/components/schemas/PlaylistClip"
            }
          }
        }
      },
      "PlaylistGroup": {
        "type": "object",
        "nullable": true,
        "required": [
          "code",
          "title"
        ],
        "description": "Filter label of the playlist inside its Category (for example Pilates or Gym), or `null`. It is not a unit of licence.",
        "x-description-vi": "Nhãn lọc của playlist trong Category (ví dụ Pilates hoặc Gym), hoặc `null`. Không phải đơn vị cấp quyền.",
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable code.",
            "x-description-vi": "Mã ổn định."
          },
          "title": {
            "type": "string",
            "description": "Display name.",
            "x-description-vi": "Tên hiển thị."
          }
        }
      },
      "PlaylistImage": {
        "type": "object",
        "nullable": true,
        "required": [
          "source",
          "url",
          "expires_at",
          "space",
          "uri"
        ],
        "description": "An image, or `null` when there is none. `source` says where to get it. `media_space`: the image is already in your own media space (`space`) under `uri` (for a clip it is also the poster set on its `vid`); load it through your space as you load your videos. `wandura_cdn`: a link valid until `expires_at`; download the file and host your own copy, do not store the link. New sources may be added: skip a value you do not know.",
        "x-description-vi": "Một ảnh, hoặc `null` khi không có. `source` cho biết lấy ở đâu. `media_space`: ảnh đã nằm trong media space của bạn (`space`) tại `uri` (với clip đó cũng là poster gắn trên `vid` của nó); tải qua space của bạn như cách bạn tải video. `wandura_cdn`: link dùng được tới `expires_at`; tải file về và tự lưu bản của bạn, đừng lưu link. Có thể có nguồn mới: bỏ qua giá trị bạn không biết.",
        "properties": {
          "source": {
            "type": "string",
            "enum": [
              "media_space",
              "wandura_cdn"
            ],
            "description": "`media_space` (in your media space) or `wandura_cdn` (a Wandura link to download). New values may be added.",
            "x-description-vi": "`media_space` (trong media space của bạn) hoặc `wandura_cdn` (link Wandura để tải về). Có thể có giá trị mới."
          },
          "url": {
            "type": "string",
            "nullable": true,
            "description": "Image URL when `source` is `wandura_cdn`, else `null`.",
            "x-description-vi": "URL ảnh khi `source` là `wandura_cdn`, ngược lại `null`."
          },
          "expires_at": {
            "type": "integer",
            "nullable": true,
            "description": "When `url` stops working, in Unix epoch seconds, or `null`.",
            "x-description-vi": "Lúc `url` hết hiệu lực, tính bằng giây Unix epoch, hoặc `null`."
          },
          "space": {
            "type": "string",
            "nullable": true,
            "description": "Your media space holding the image when `source` is `media_space`, else `null`.",
            "x-description-vi": "Media space của bạn đang chứa ảnh khi `source` là `media_space`, ngược lại `null`."
          },
          "uri": {
            "type": "string",
            "nullable": true,
            "description": "The image's URI inside `space` when `source` is `media_space`, else `null`.",
            "x-description-vi": "URI của ảnh trong `space` khi `source` là `media_space`, ngược lại `null`."
          }
        }
      },
      "PlaylistSummary": {
        "type": "object",
        "required": [
          "code",
          "status",
          "title",
          "category",
          "group",
          "clip_count",
          "granted",
          "updated_at",
          "content_version",
          "withdrawn_at"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Playlist code, for example `wall-pilates-01`. It never changes.",
            "x-description-vi": "Mã playlist, ví dụ `wall-pilates-01`. Không bao giờ đổi."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "withdrawn"
            ],
            "description": "`active`: in the library now. `withdrawn`: Wandura took the playlist out of the library; it stays in this list for 30 days so your sync job sees it. New values may be added.",
            "x-description-vi": "`active`: đang có trong kho. `withdrawn`: Wandura đã rút playlist khỏi kho; nó còn trong danh sách 30 ngày để job đồng bộ của bạn kịp thấy. Có thể có giá trị mới."
          },
          "title": {
            "type": "string",
            "description": "Playlist title.",
            "x-description-vi": "Tên playlist."
          },
          "category": {
            "$ref": "#/components/schemas/PlaylistCategory"
          },
          "group": {
            "$ref": "#/components/schemas/PlaylistGroup"
          },
          "clip_count": {
            "type": "integer",
            "nullable": true,
            "description": "Clips in the playlist. `null` when withdrawn.",
            "x-description-vi": "Số clip trong playlist. `null` khi withdrawn."
          },
          "granted": {
            "type": "boolean",
            "description": "`true` when your organisation is licensed for this playlist (its clips are copied into your media space). You see the whole library; `false` playlists carry no video IDs.",
            "x-description-vi": "`true` khi tổ chức của bạn được cấp playlist này (clip được chép vào media space của bạn). Bạn thấy cả kho; playlist `false` không mang video ID."
          },
          "updated_at": {
            "type": "integer",
            "description": "Last change in Unix epoch seconds (metadata, your licence, or copies in your media space).",
            "x-description-vi": "Lần đổi gần nhất tính bằng giây Unix epoch (metadata, quyền của bạn, hoặc bản chép trong media space của bạn)."
          },
          "content_version": {
            "type": "string",
            "nullable": true,
            "description": "Hash of everything GET /playlists/{code} returns for you. Unchanged = no need to fetch the detail. `null` when withdrawn.",
            "x-description-vi": "Mã băm của mọi thứ GET /playlists/{code} trả cho bạn. Không đổi = không cần gọi chi tiết. `null` khi withdrawn."
          },
          "withdrawn_at": {
            "type": "integer",
            "nullable": true,
            "description": "When it was withdrawn (epoch seconds), or `null`.",
            "x-description-vi": "Lúc bị rút (giây epoch), hoặc `null`."
          }
        }
      },
      "SelfLink": {
        "type": "object",
        "description": "Ready-made absolute URL of this resource (an API link, not a media file).",
        "x-description-vi": "URL tuyệt đối dựng sẵn của chính tài nguyên này (link API, không phải file media).",
        "properties": {
          "self": {
            "type": "string"
          }
        }
      },
      "SeriesDetail": {
        "type": "object",
        "required": [
          "code",
          "status",
          "updated_at",
          "content_version",
          "language",
          "title_i18n",
          "synopsis_short_i18n",
          "synopsis_i18n",
          "genres",
          "poster",
          "episodes"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Series code, for example `WD000016`.",
            "x-description-vi": "Mã bộ, ví dụ `WD000016`."
          },
          "status": {
            "type": "string",
            "enum": [
              "active"
            ],
            "description": "Always `active` (a series you may not stream returns 404).",
            "x-description-vi": "Luôn là `active` (bộ bạn không được stream trả 404)."
          },
          "film_status": {
            "$ref": "#/components/schemas/FilmStatus"
          },
          "tier": {
            "$ref": "#/components/schemas/FilmTier"
          },
          "updated_at": {
            "type": "integer",
            "description": "Last change in Unix epoch seconds.",
            "x-description-vi": "Lần đổi gần nhất tính bằng giây Unix epoch."
          },
          "content_version": {
            "type": "string",
            "nullable": true,
            "description": "Hash of this whole response. Store it; when the list shows the same value there is nothing to fetch.",
            "x-description-vi": "Mã băm của toàn bộ response này. Lưu lại; danh sách trả cùng giá trị thì không có gì phải tải."
          },
          "language": {
            "type": "string",
            "nullable": true,
            "description": "Audio language, BCP-47 (same as `default_lang`).",
            "x-description-vi": "Ngôn ngữ âm thanh, BCP-47 (giống `default_lang`)."
          },
          "default_lang": {
            "type": "string",
            "nullable": true,
            "description": "Audio language, BCP-47.",
            "x-description-vi": "Ngôn ngữ âm thanh, BCP-47."
          },
          "title_i18n": {
            "$ref": "#/components/schemas/I18nText"
          },
          "synopsis_short_i18n": {
            "$ref": "#/components/schemas/I18nText"
          },
          "synopsis_i18n": {
            "$ref": "#/components/schemas/I18nText"
          },
          "genres": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Free-text genre labels (legacy). Prefer `tags`.",
            "x-description-vi": "Nhãn thể loại chữ tự do (cũ). Nên dùng `tags`."
          },
          "poster": {
            "type": "object",
            "nullable": true,
            "description": "The 9:16 cover as `{url}`, or `null`. The same image appears in `images` with its size.",
            "x-description-vi": "Ảnh bìa 9:16 dạng `{url}`, hoặc `null`. Cùng ảnh có trong `images` kèm kích thước.",
            "properties": {
              "url": {
                "type": "string"
              }
            }
          },
          "content_rating": {
            "type": "string",
            "nullable": true,
            "enum": [
              "all",
              "13+",
              "16+",
              "18+"
            ],
            "description": "Age rating, or `null` when not rated.",
            "x-description-vi": "Độ tuổi, hoặc `null` khi chưa chấm."
          },
          "is_completed": {
            "type": "boolean",
            "nullable": true,
            "description": "`true` when every planned episode is released.",
            "x-description-vi": "`true` khi mọi tập dự kiến đã ra."
          },
          "release_date": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "Original release date, or `null`.",
            "x-description-vi": "Ngày phát hành gốc, hoặc `null`."
          },
          "planned_episodes": {
            "type": "integer",
            "nullable": true,
            "description": "Total episodes planned, or `null`.",
            "x-description-vi": "Tổng số tập dự kiến, hoặc `null`."
          },
          "tags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MetadataTag"
            }
          },
          "people": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MetadataPerson"
            }
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FilmImage"
            }
          },
          "trailer": {
            "type": "object",
            "nullable": true,
            "description": "The trailer video ID in Wandura's media space, or `null`. Always `null` when you have your own space.",
            "x-description-vi": "Video ID trailer trong media space của Wandura, hoặc `null`. Luôn `null` khi bạn có space riêng.",
            "properties": {
              "vid": {
                "type": "string"
              }
            }
          },
          "metadata_version": {
            "type": "string",
            "nullable": true,
            "description": "Hash of the film metadata alone (same value as in the catalog).",
            "x-description-vi": "Mã băm của riêng metadata phim (cùng giá trị như trong catalog)."
          },
          "metadata_updated_at": {
            "type": "integer",
            "nullable": true,
            "description": "When `metadata_version` last changed (epoch seconds).",
            "x-description-vi": "Lúc `metadata_version` đổi gần nhất (giây epoch)."
          },
          "media": {
            "type": "object",
            "description": "Where the videos live: `space` is the media space (yours when you have one) and `playlist_name` is the playlist holding the episodes in order (equal to `code`).",
            "x-description-vi": "Video nằm ở đâu: `space` là media space (của bạn nếu bạn có space riêng) và `playlist_name` là playlist chứa các tập theo thứ tự (bằng `code`).",
            "properties": {
              "space": {
                "type": "string"
              },
              "playlist_name": {
                "type": "string"
              },
              "playlist_id": {
                "type": "string",
                "nullable": true,
                "description": "ID of that playlist in `space`, or `null` until Wandura has created it there. Use it to read the playlist directly from your media space.",
                "x-description-vi": "ID của playlist đó trong `space`, hoặc `null` cho tới khi Wandura tạo nó ở đó. Dùng nó để đọc playlist thẳng từ media space của bạn."
              },
              "playlists": {
                "type": "array",
                "description": "Every playlist holding the episodes, in order: one playlist holds at most 100 videos, so a longer series continues in `<code>-2`, `<code>-3`… Empty until the first one exists.",
                "x-description-vi": "Mọi playlist chứa các tập, theo thứ tự: một playlist chứa tối đa 100 video, nên bộ dài hơn nối tiếp ở `<code>-2`, `<code>-3`… Rỗng cho tới khi playlist đầu tiên tồn tại.",
                "items": {
                  "type": "object",
                  "required": [
                    "id",
                    "name"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Playlist ID in `space`.",
                      "x-description-vi": "ID playlist trong `space`."
                    },
                    "name": {
                      "type": "string",
                      "nullable": true,
                      "description": "Playlist name, or `null` when unknown.",
                      "x-description-vi": "Tên playlist, hoặc `null` khi không rõ."
                    }
                  }
                }
              }
            }
          },
          "episodes": {
            "type": "array",
            "description": "Published episodes, by `ep_number`.",
            "x-description-vi": "Các tập đã phát hành, theo `ep_number`.",
            "items": {
              "$ref": "#/components/schemas/SeriesEpisode"
            }
          }
        }
      },
      "SeriesEpisode": {
        "type": "object",
        "required": [
          "ep_number",
          "vid",
          "ready",
          "title_i18n",
          "duration_sec",
          "subtitles"
        ],
        "properties": {
          "ep_number": {
            "type": "integer",
            "description": "Episode number, starting at 1. The playlist order follows it.",
            "x-description-vi": "Số tập, bắt đầu từ 1. Thứ tự playlist theo số này."
          },
          "vid": {
            "type": "string",
            "nullable": true,
            "description": "Video ID of the episode in the media space named in `media.space` (your own space when you have one). `null` while the episode is not in that space yet.",
            "x-description-vi": "Video ID của tập trong media space ghi ở `media.space` (space riêng của bạn nếu có). `null` khi tập chưa có trong space đó."
          },
          "ready": {
            "type": "boolean",
            "description": "`true` when the episode can be played from that space.",
            "x-description-vi": "`true` khi tập phát được từ space đó."
          },
          "title_i18n": {
            "$ref": "#/components/schemas/I18nText"
          },
          "duration_sec": {
            "type": "integer",
            "nullable": true,
            "description": "Duration in seconds, for cross-checking only.",
            "x-description-vi": "Thời lượng tính bằng giây, chỉ để đối chiếu."
          },
          "subtitles": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "lang",
                "url",
                "updated_at"
              ],
              "properties": {
                "lang": {
                  "type": "string",
                  "description": "BCP-47 code.",
                  "x-description-vi": "Mã BCP-47."
                },
                "url": {
                  "type": "string",
                  "description": "WebVTT file URL. Download it and host your own copy.",
                  "x-description-vi": "URL file WebVTT. Tải về và tự lưu bản của bạn."
                },
                "updated_at": {
                  "type": "integer",
                  "description": "Last change of the file (epoch seconds).",
                  "x-description-vi": "Lần đổi file gần nhất (giây epoch)."
                }
              }
            }
          }
        }
      },
      "SeriesSummary": {
        "type": "object",
        "required": [
          "code",
          "status",
          "updated_at",
          "content_version",
          "episode_count",
          "withdrawn_at"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Series code, for example `WD000016`.",
            "x-description-vi": "Mã bộ, ví dụ `WD000016`."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "withdrawn"
            ],
            "description": "`active`: you may stream it now. `withdrawn`: your licence ended or was suspended, or Wandura took the series down; it stays in this list for 30 days so your sync job sees it. New values may be added.",
            "x-description-vi": "`active`: bạn đang được stream. `withdrawn`: giấy phép của bạn hết hạn hay bị tạm ngưng, hoặc Wandura gỡ bộ; bộ còn trong danh sách 30 ngày để job đồng bộ của bạn kịp thấy. Có thể có giá trị mới."
          },
          "updated_at": {
            "type": "integer",
            "description": "Last change in Unix epoch seconds (metadata, licence or episodes in your media space).",
            "x-description-vi": "Lần đổi gần nhất tính bằng giây Unix epoch (metadata, giấy phép hoặc tập trong media space của bạn)."
          },
          "content_version": {
            "type": "string",
            "nullable": true,
            "description": "Hash of everything GET /series/{code} returns for you. Unchanged = no need to fetch the detail. `null` when withdrawn.",
            "x-description-vi": "Mã băm của mọi thứ GET /series/{code} trả cho bạn. Không đổi = không cần gọi chi tiết. `null` khi withdrawn."
          },
          "episode_count": {
            "type": "integer",
            "nullable": true,
            "description": "Published episodes. `null` when withdrawn.",
            "x-description-vi": "Số tập đã phát hành. `null` khi withdrawn."
          },
          "withdrawn_at": {
            "type": "integer",
            "nullable": true,
            "description": "When it was withdrawn (epoch seconds), or `null`.",
            "x-description-vi": "Lúc bị gỡ (giây epoch), hoặc `null`."
          },
          "film_status": {
            "$ref": "#/components/schemas/FilmStatus"
          },
          "tier": {
            "$ref": "#/components/schemas/FilmTier"
          }
        }
      },
      "Subtitle": {
        "type": "object",
        "required": [
          "lang",
          "url",
          "format"
        ],
        "properties": {
          "lang": {
            "type": "string",
            "description": "Language code, for example `en`, `vi` or `pt-BR`.",
            "x-description-vi": "Mã ngôn ngữ, ví dụ `en`, `vi` hoặc `pt-BR`."
          },
          "url": {
            "type": "string",
            "description": "URL of the subtitle file. Not tied to the ticket's expiry.",
            "x-description-vi": "URL của file phụ đề. Không gắn với thời hạn của vé."
          },
          "format": {
            "type": "string",
            "enum": [
              "webvtt"
            ],
            "description": "Always `webvtt`.",
            "x-description-vi": "Luôn là `webvtt`."
          }
        }
      }
    },
    "securitySchemes": {
      "deliveryApiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your organisation's API key, sent on every request as `Authorization: Bearer <API key>`. Create, rotate and revoke keys on the Developers page of the partner portal. Each key carries one or more scopes; a missing, unknown or revoked key gets 401, a valid key without the endpoint's scope gets 403.",
        "x-description-vi": "API key của tổ chức bạn, gửi ở mọi request dạng `Authorization: Bearer <API key>`. Tạo, xoay vòng và thu hồi key ở trang Developers của cổng đối tác. Mỗi key mang một hoặc nhiều scope; thiếu key, key không tồn tại hoặc đã thu hồi nhận 401, key hợp lệ mà thiếu scope của endpoint nhận 403."
      }
    }
  }
}
