{
  "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": "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`.",
        "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`.",
        "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"
                    }
                  ],
                  "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
                    },
                    {
                      "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/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`.",
        "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`.",
        "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.",
        "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.",
        "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."
          }
        }
      },
      "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"
        ],
        "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."
          }
        }
      },
      "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`."
          }
        }
      },
      "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."
          }
        }
      },
      "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"
          }
        }
      },
      "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."
      }
    }
  }
}
