{
  "openapi": "3.1.0",
  "info": {
    "title": "AVOBOX Broadcast API",
    "version": "1.5.0",
    "description": "One published worldwide lineup. UTC timestamps are absolute instants. Timezones change presentation only. Public endpoints require no API key. Drafts never appear in public responses."
  },
  "servers": [
    {
      "url": "https://avobox.tv",
      "description": "Production HTTPS API"
    },
    {
      "url": "http://127.0.0.1:3186",
      "description": "Origin development only"
    }
  ],
  "tags": [
    {
      "name": "Network"
    },
    {
      "name": "Broadcasts"
    }
  ],
  "paths": {
    "/api/v1/languages": {
      "get": {
        "tags": [
          "Network"
        ],
        "summary": "List published languages",
        "operationId": "listLanguages",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Published items from one atomic lineup revision.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LanguageList"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/stations": {
      "get": {
        "tags": [
          "Network"
        ],
        "summary": "List published stations",
        "operationId": "listStations",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Published items from one atomic lineup revision.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StationList"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/channels": {
      "get": {
        "tags": [
          "Network"
        ],
        "summary": "List published channels",
        "operationId": "listChannels",
        "parameters": [
          {
            "name": "language",
            "in": "query",
            "description": "Language code, such as vi or en. A multilingual channel can appear in multiple language queries; its permanent channel ID and schedule stay the same.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Published items from one atomic lineup revision.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelList"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/schedules": {
      "get": {
        "tags": [
          "Broadcasts"
        ],
        "summary": "Programs intersecting one UTC window",
        "operationId": "listSchedules",
        "description": "Half-open window [from,to). Includes programs that start before from and end after from. Maximum window: 31 days. Sorted by startsAtUtc. The default window starts at server now and lasts 24 hours.",
        "parameters": [
          {
            "name": "channelId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "ISO 8601 timestamp with Z or explicit offset."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "ISO 8601 timestamp with Z or explicit offset."
          }
        ],
        "responses": {
          "200": {
            "description": "Published broadcasts. An empty array means no programming in this window.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduleList"
                }
              }
            }
          },
          "400": {
            "description": "Invalid timestamp, missing channelId, or invalid window.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Published channel not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/channels/{channelId}/now": {
      "get": {
        "tags": [
          "Broadcasts"
        ],
        "summary": "Current program, next program and server UTC time",
        "operationId": "getChannelNow",
        "description": "current is selected with startsAtUtc <= serverTimeUtc < endsAtUtc. A gap returns current: null. next is the earliest later broadcast, or null. Playback offset = source.inPointSeconds + (serverTimeUtc - startsAtUtc). Keep advancing against a monotonic clock. Refetch at boundaries and when scheduleRevision changes. Never search or shuffle independently on a device.",
        "parameters": [
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One published channel at the current server instant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Now"
                }
              }
            }
          },
          "404": {
            "description": "Published channel not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/lineup": {
      "get": {
        "operationId": "getPublishedLineupPage",
        "tags": [
          "Broadcasts"
        ],
        "summary": "Atomic published channels and programs for a TV-guide page",
        "description": "Efficient supplement to the five core endpoints. All metadata and programs in one response share a publication revision. Default window covers the past30minutes through the following4.5hours.",
        "parameters": [
          {
            "name": "language",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 200
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          },
          {
            "name": "revision",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "hours",
            "in": "query",
            "description": "UTC window length from the default server-derived from time; avoids relying on the viewer clock. Explicit to overrides it.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 744,
              "default": 5
            }
          },
          {
            "name": "channelId",
            "in": "query",
            "description": "Focus the page containing this published channel within the selected language/category filters. Response cursor identifies that page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One atomic guide page. nextCursor null means the last page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LineupPage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid window or pagination."
          },
          "409": {
            "description": "The requested revision is stale. Restart pagination."
          }
        }
      }
    },
    "/api/v1/channels/{channelId}": {
      "get": {
        "tags": [
          "Network"
        ],
        "summary": "Published channel metadata for a deep link",
        "parameters": [
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Published channel, available languages, server UTC and schedule revision"
          },
          "404": {
            "description": "Channel is not published"
          }
        }
      }
    },
    "/api/v1/home": {
      "get": {
        "summary": "Artwork and programmes airing now in a language",
        "parameters": [
          {
            "name": "language",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Published current programmes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "serverTimeUtc": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "scheduleRevision": {
                      "type": "integer"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/Channel"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "current": {
                                "$ref": "#/components/schemas/Program"
                              }
                            }
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/app-updates": {
      "get": {
        "summary": "Latest compatible published Android APK with pinned signing identity",
        "parameters": [
          {
            "name": "packageName",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "versionCode",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "sdk",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Update manifest; installed or newer apps receive no update",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updateAvailable": {
                      "type": "boolean"
                    },
                    "release": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "packageName": {
                          "type": "string"
                        },
                        "versionCode": {
                          "type": "integer"
                        },
                        "versionName": {
                          "type": "string"
                        },
                        "sha256": {
                          "type": "string"
                        },
                        "signerSha256": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "sizeBytes": {
                          "type": "integer"
                        },
                        "downloadUrl": {
                          "type": "string",
                          "format": "uri"
                        },
                        "downloadAccess": {
                          "type": "string",
                          "enum": [
                            "public",
                            "device"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/playback/session": {
      "get": {
        "summary": "Open an anonymous unlimited free playback session",
        "responses": {
          "200": {
            "description": "HttpOnly media session cookie, no viewing deadline",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ready": {
                      "type": "boolean"
                    },
                    "unlimited": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/playback/media": {
      "get": {
        "summary": "Current scheduled YouTube media via the authenticated backend relay",
        "parameters": [
          {
            "name": "channelId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "broadcastId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "track",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "MP4 media or HLS playlist. Supports HEAD and byte ranges. Requires anonymous playback cookie."
          },
          "409": {
            "description": "Programme segment changed; fetch current UTC segment."
          }
        }
      }
    },
    "/api/v1/ad-creatives/{creativeId}/media": {
      "get": {
        "summary": "Retired hosted-ad media endpoint",
        "parameters": [
          {
            "in": "path",
            "name": "creativeId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "410": {
            "description": "Use the YouTube source in the current broadcast."
          },
          "404": {
            "description": "Creative is unavailable."
          }
        },
        "deprecated": true,
        "description": "YouTube ads use source.type=youtube and source.videoId in the shared broadcast API. Hosted ad media is no longer stored."
      }
    },
    "/api/admin/channels/{channelId}/playout": {
      "parameters": [
        {
          "name": "channelId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "summary": "Actual saved programme and advertisement chronology",
        "security": [
          {
            "AdminSession": []
          }
        ],
        "parameters": [
          {
            "name": "hours",
            "in": "query",
            "schema": {
              "type": "integer",
              "enum": [
                6,
                12,
                24
              ],
              "default": 6
            }
          },
          {
            "name": "view",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "published",
                "draft"
              ],
              "default": "published"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chronological segments, exact resume offsets, contiguous coverage and gaps.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "channel": {
                      "type": "object"
                    },
                    "serverTimeUtc": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "scheduleRevision": {
                      "type": "integer"
                    },
                    "targetHours": {
                      "const": 6
                    },
                    "publishedCoverage": {
                      "type": "object"
                    },
                    "draftCoverage": {
                      "type": "object"
                    },
                    "rows": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "gaps": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Owner authentication required."
          }
        }
      },
      "post": {
        "summary": "Extend this channel from qualified saved library content to the six-hour target",
        "security": [
          {
            "AdminSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Preparation result; shortages remain explicit.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Owner authentication required."
          }
        }
      }
    },
    "/api/admin/state": {
      "get": {
        "summary": "Protected administrative state; catalogue scopes are paginated and language-filtered",
        "security": [
          {
            "AdminSession": []
          }
        ],
        "parameters": [
          {
            "name": "scope",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "channels",
                "stations",
                "videos"
              ]
            }
          },
          {
            "name": "language",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "en"
            }
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Catalogue metadata and at most100 matching records per page when scope is supplied.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Owner authentication required."
          }
        }
      }
    },
    "/api/admin/ad-creatives/import": {
      "post": {
        "summary": "Verify a YouTube ad link and save metadata for review",
        "security": [
          {
            "AdminSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "languageId": {
                    "type": "string",
                    "default": "all",
                    "description": "all for universal, or a permanent language ID."
                  },
                  "advertiserId": {
                    "type": "string",
                    "description": "Owner only; ignored for advertiser sessions."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued metadata verification. New ads start as drafts; an existing active ad is reused without changing its audience."
          },
          "400": {
            "description": "Invalid YouTube video URL or language."
          },
          "401": {
            "description": "Sign in required."
          }
        },
        "description": "Media stays on YouTube. Save the permanent video ID, original title, exact duration and audience. No video download or MP4 storage occurs."
      }
    },
    "/api/advertiser/import": {
      "post": {
        "summary": "Verify a YouTube ad link and save metadata for review",
        "security": [
          {
            "AdvertiserSession": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "languageId": {
                    "type": "string",
                    "default": "all",
                    "description": "all for universal, or a permanent language ID."
                  },
                  "advertiserId": {
                    "type": "string",
                    "description": "Owner only; ignored for advertiser sessions."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued metadata verification. New ads start as drafts; an existing active ad is reused without changing its audience."
          },
          "400": {
            "description": "Invalid YouTube video URL or language."
          },
          "401": {
            "description": "Sign in required."
          }
        },
        "description": "Media stays on YouTube. Save the permanent video ID, original title, exact duration and audience. No video download or MP4 storage occurs."
      }
    },
    "/api/admin/ad-imports": {
      "get": {
        "summary": "Recent import progress; advertiser sessions only see their own jobs",
        "security": [
          {
            "AdminSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Queued, verifying (legacy status value: downloading), ready or failed metadata jobs. No video download occurs."
          }
        }
      }
    },
    "/api/advertiser/imports": {
      "get": {
        "summary": "Recent import progress; advertiser sessions only see their own jobs",
        "security": [
          {
            "AdvertiserSession": []
          }
        ],
        "responses": {
          "200": {
            "description": "Queued, verifying (legacy status value: downloading), ready or failed metadata jobs. No video download occurs."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "Language": {
        "type": "object",
        "required": [
          "id",
          "code",
          "name",
          "nativeName"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "lang_vi"
            ]
          },
          "code": {
            "type": "string",
            "examples": [
              "vi"
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "Vietnamese"
            ]
          },
          "nativeName": {
            "type": "string",
            "examples": [
              "Xin chào"
            ]
          },
          "published": {
            "type": "boolean"
          }
        }
      },
      "Station": {
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "st_lumiere"
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "Lumière"
            ]
          },
          "description": {
            "type": "string"
          },
          "logo": {
            "type": "string"
          },
          "published": {
            "type": "boolean"
          }
        }
      },
      "Channel": {
        "type": "object",
        "required": [
          "id",
          "number",
          "name",
          "languageIds",
          "stationId",
          "category"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "ch_001"
            ]
          },
          "number": {
            "type": "integer",
            "minimum": 1,
            "description": "Ascending number within a language lineup. The same number may exist in disjoint languages; use permanent id for references."
          },
          "name": {
            "type": "string"
          },
          "languageIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "uniqueItems": true
          },
          "stationId": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "logo": {
            "type": "string"
          },
          "published": {
            "type": "boolean"
          },
          "isDemo": {
            "type": "boolean"
          },
          "displayName": {
            "type": "string",
            "description": "Short viewer-facing name without a language prefix. Use name when absent.",
            "examples": [
              "Silver Screen Premiere"
            ]
          },
          "diagnostic": {
            "type": "boolean",
            "description": "Integration test feed; omit from normal TV channel surfing.",
            "default": false
          },
          "localizedNames": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Native-language display names keyed by language code. Use the selected language, then displayName, then name."
          }
        }
      },
      "Source": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "type",
              "videoId",
              "inPointSeconds"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "youtube"
                ]
              },
              "videoId": {
                "type": "string",
                "pattern": "^[A-Za-z0-9_-]{11}$"
              },
              "inPointSeconds": {
                "type": "integer",
                "minimum": 0
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type",
              "url",
              "inPointSeconds"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "mp4"
                ]
              },
              "url": {
                "type": "string",
                "format": "uri",
                "description": "HTTPS same-origin verified advertiser media."
              },
              "inPointSeconds": {
                "type": "integer",
                "enum": [
                  0
                ]
              }
            }
          }
        ]
      },
      "Program": {
        "type": "object",
        "required": [
          "broadcastId",
          "channelId",
          "title",
          "startsAtUtc",
          "endsAtUtc",
          "source",
          "durationSeconds"
        ],
        "properties": {
          "broadcastId": {
            "type": "string"
          },
          "channelId": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "startsAtUtc": {
            "type": "string",
            "format": "date-time"
          },
          "endsAtUtc": {
            "type": "string",
            "format": "date-time"
          },
          "source": {
            "$ref": "#/components/schemas/Source"
          },
          "durationSeconds": {
            "type": "integer",
            "minimum": 1
          },
          "thumbnail": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "isDemo": {
            "type": "boolean"
          },
          "isAdvertisement": {
            "type": "boolean"
          },
          "programme": {
            "type": "object",
            "description": "Logical programme metadata. Its UTC span includes shared ad breaks; durationSeconds is content duration. Present on segmented programmes.",
            "required": [
              "broadcastId",
              "title",
              "startsAtUtc",
              "endsAtUtc",
              "durationSeconds"
            ],
            "properties": {
              "broadcastId": {
                "type": "string"
              },
              "videoId": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "thumbnail": {
                "type": "string"
              },
              "startsAtUtc": {
                "type": "string",
                "format": "date-time"
              },
              "endsAtUtc": {
                "type": "string",
                "format": "date-time"
              },
              "durationSeconds": {
                "type": "integer",
                "minimum": 1
              }
            }
          }
        }
      },
      "LanguageList": {
        "type": "object",
        "required": [
          "serverTimeUtc",
          "scheduleRevision",
          "items"
        ],
        "properties": {
          "serverTimeUtc": {
            "type": "string",
            "format": "date-time"
          },
          "scheduleRevision": {
            "type": "integer",
            "minimum": 0
          },
          "publishedAtUtc": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Language"
            }
          }
        }
      },
      "StationList": {
        "type": "object",
        "required": [
          "serverTimeUtc",
          "scheduleRevision",
          "items"
        ],
        "properties": {
          "serverTimeUtc": {
            "type": "string",
            "format": "date-time"
          },
          "scheduleRevision": {
            "type": "integer",
            "minimum": 0
          },
          "publishedAtUtc": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Station"
            }
          }
        }
      },
      "ChannelList": {
        "type": "object",
        "required": [
          "serverTimeUtc",
          "scheduleRevision",
          "items"
        ],
        "properties": {
          "serverTimeUtc": {
            "type": "string",
            "format": "date-time"
          },
          "scheduleRevision": {
            "type": "integer",
            "minimum": 0
          },
          "publishedAtUtc": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Channel"
            }
          }
        }
      },
      "ScheduleList": {
        "type": "object",
        "required": [
          "serverTimeUtc",
          "scheduleRevision",
          "channelId",
          "from",
          "to",
          "items"
        ],
        "properties": {
          "serverTimeUtc": {
            "type": "string",
            "format": "date-time"
          },
          "scheduleRevision": {
            "type": "integer",
            "minimum": 0
          },
          "publishedAtUtc": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "channelId": {
            "type": "string"
          },
          "from": {
            "type": "string",
            "format": "date-time"
          },
          "to": {
            "type": "string",
            "format": "date-time"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Program"
            }
          }
        }
      },
      "Now": {
        "type": "object",
        "required": [
          "serverTimeUtc",
          "scheduleRevision",
          "channelId",
          "current",
          "next"
        ],
        "properties": {
          "serverTimeUtc": {
            "type": "string",
            "format": "date-time"
          },
          "scheduleRevision": {
            "type": "integer",
            "minimum": 0
          },
          "publishedAtUtc": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "channelId": {
            "type": "string"
          },
          "current": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Program"
              },
              {
                "type": "null"
              }
            ]
          },
          "next": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Program"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "LineupPage": {
        "type": "object",
        "required": [
          "serverTimeUtc",
          "scheduleRevision",
          "languages",
          "stations",
          "channels",
          "programs",
          "totalChannels",
          "nextCursor"
        ],
        "properties": {
          "serverTimeUtc": {
            "type": "string",
            "format": "date-time"
          },
          "scheduleRevision": {
            "type": "integer",
            "minimum": 0
          },
          "publishedAtUtc": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "languages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Language"
            }
          },
          "stations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Station"
            }
          },
          "channels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Channel"
            }
          },
          "programs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Program"
            }
          },
          "totalChannels": {
            "type": "integer",
            "minimum": 0
          },
          "nextCursor": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "from": {
            "type": "string",
            "format": "date-time"
          },
          "to": {
            "type": "string",
            "format": "date-time"
          },
          "categories": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "AdminSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "avobox_session",
        "description": "Protected owner session obtained through admin login."
      },
      "AdvertiserSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "avobox_advertiser",
        "description": "Private account-scoped advertiser session."
      }
    }
  }
}
