{
  "openapi": "3.1.0",
  "info": {
    "title": "AVOBOX Broadcast API",
    "version": "1.0.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.cc",
      "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"
            }
          }
        ],
        "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."
          }
        }
      }
    }
  },
  "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\u00e0o"
            ]
          },
          "published": {
            "type": "boolean"
          }
        }
      },
      "Station": {
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "st_lumiere"
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "Lumi\u00e8re"
            ]
          },
          "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
          }
        }
      },
      "Source": {
        "type": "object",
        "required": [
          "type",
          "videoId",
          "inPointSeconds"
        ],
        "properties": {
          "type": {
            "const": "youtube"
          },
          "videoId": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{11}$"
          },
          "inPointSeconds": {
            "type": "integer",
            "minimum": 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"
          }
        }
      },
      "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"
            }
          }
        }
      }
    }
  }
}
