{
  "openapi": "3.0.3",
  "info": {
    "title": "SceneSat Public API",
    "version": "1.0.0",
    "description": "Read-only, anonymous access to SceneSat and guest-station archive data, including published recordings, cue sheets and replay chat. No account or key is required. Anonymous reads allow 30 requests per client IP per UTC minute; optional operator-issued application keys allow 60 per key. Supplied keys must be valid and have the endpoint scope. Keys are separate from SceneSatID sessions and internal service credentials. Compatible additions stay in v1; breaking changes get a new major URL with a documented migration period. Access to metadata does not grant redistribution rights.",
    "contact": {
      "name": "SceneSat",
      "url": "https://scenesat.com"
    }
  },
  "servers": [
    {
      "url": "https://api.scenesat.com/v1",
      "description": "Public API v1"
    }
  ],
  "security": [
    {},
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Channels"
    },
    {
      "name": "Recordings"
    },
    {
      "name": "Tracks"
    }
  ],
  "paths": {
    "/channels": {
      "get": {
        "operationId": "listChannels",
        "summary": "List channels",
        "tags": [
          "Channels"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Maximum items per page."
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Opaque nextCursor from the previous page. Keep the same filters."
          }
        ],
        "x-schema": "Channel",
        "x-list": true,
        "description": "An archive channel (SceneSat or a guest station). These numeric IDs identify archive channels; audio playout channels have a separate namespace of names. Anonymous access is allowed. Scope when using a key: `catalog:read`. Ordered by ID ascending.",
        "responses": {
          "200": {
            "description": "Success. Example IDs are illustrative.",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID."
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Requests per UTC minute: 30 per client IP anonymously, or 60 per valid key."
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Quota reset as Unix seconds."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Channel"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1,
                      "slug": "scenesat",
                      "title": "SceneSat",
                      "description": "SceneSat archive",
                      "kind": "scenesat",
                      "websiteUrl": "https://scenesat.com",
                      "livestreamUrl": null
                    }
                  ],
                  "pagination": {
                    "limit": 50,
                    "nextCursor": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query or cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Supplied API key is invalid, revoked or expired (omitting Authorization is allowed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Supplied key lacks the endpoint scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not public",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Read-only endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Quota exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          }
        },
        "x-key-scope": "catalog:read"
      }
    },
    "/channels/{id}": {
      "get": {
        "operationId": "getChannel",
        "summary": "Get one channel",
        "tags": [
          "Channels"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 1
            },
            "description": "Manager archive channel ID. Not a reLive ID."
          }
        ],
        "x-schema": "Channel",
        "x-list": false,
        "description": "An archive channel (SceneSat or a guest station). These numeric IDs identify archive channels; audio playout channels have a separate namespace of names. Anonymous access is allowed. Scope when using a key: `catalog:read`.",
        "responses": {
          "200": {
            "description": "Success. Example IDs are illustrative.",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID."
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Requests per UTC minute: 30 per client IP anonymously, or 60 per valid key."
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Quota reset as Unix seconds."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Channel"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 1,
                    "slug": "scenesat",
                    "title": "SceneSat",
                    "description": "SceneSat archive",
                    "kind": "scenesat",
                    "websiteUrl": "https://scenesat.com",
                    "livestreamUrl": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query or cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Supplied API key is invalid, revoked or expired (omitting Authorization is allowed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Supplied key lacks the endpoint scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not public",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Read-only endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Quota exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          }
        },
        "x-key-scope": "catalog:read"
      }
    },
    "/recordings": {
      "get": {
        "operationId": "listRecordings",
        "summary": "List recordings",
        "tags": [
          "Recordings"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Maximum items per page."
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Opaque nextCursor from the previous page. Keep the same filters."
          },
          {
            "name": "channelId",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 1
            },
            "description": "Filter by an archive channel ID."
          }
        ],
        "x-schema": "Recording",
        "x-list": true,
        "description": "A published primary audio recording, identified by its Manager file ID. showId identifies the broadcast; neither ID is a legacy reLive stream ID. Replacing the recording file changes the recording ID. Video and secondary recordings are not included in this initial collection. Anonymous access is allowed. Scope when using a key: `catalog:read`. Ordered by ID ascending.",
        "responses": {
          "200": {
            "description": "Success. Example IDs are illustrative.",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID."
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Requests per UTC minute: 30 per client IP anonymously, or 60 per valid key."
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Quota reset as Unix seconds."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Recording"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 700,
                      "showId": 42,
                      "channelId": 1,
                      "medium": "audio",
                      "title": "Example broadcast",
                      "host": "Example host",
                      "description": "An illustrative recording",
                      "startedAt": "2026-09-20T16:00:00Z",
                      "durationSeconds": 3600,
                      "mediaUrl": "https://media.scenesat.com/asset/00000000-0000-4000-8000-000000000700"
                    }
                  ],
                  "pagination": {
                    "limit": 50,
                    "nextCursor": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query or cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Supplied API key is invalid, revoked or expired (omitting Authorization is allowed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Supplied key lacks the endpoint scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not public",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Read-only endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Quota exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          }
        },
        "x-key-scope": "catalog:read"
      }
    },
    "/recordings/{id}": {
      "get": {
        "operationId": "getRecording",
        "summary": "Get one recording",
        "tags": [
          "Recordings"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 1
            },
            "description": "Manager file ID. Not a reLive ID."
          }
        ],
        "x-schema": "Recording",
        "x-list": false,
        "description": "A published primary audio recording, identified by its Manager file ID. showId identifies the broadcast; neither ID is a legacy reLive stream ID. Replacing the recording file changes the recording ID. Video and secondary recordings are not included in this initial collection. Anonymous access is allowed. Scope when using a key: `catalog:read`.",
        "responses": {
          "200": {
            "description": "Success. Example IDs are illustrative.",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID."
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Requests per UTC minute: 30 per client IP anonymously, or 60 per valid key."
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Quota reset as Unix seconds."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Recording"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 700,
                    "showId": 42,
                    "channelId": 1,
                    "medium": "audio",
                    "title": "Example broadcast",
                    "host": "Example host",
                    "description": "An illustrative recording",
                    "startedAt": "2026-09-20T16:00:00Z",
                    "durationSeconds": 3600,
                    "mediaUrl": "https://media.scenesat.com/asset/00000000-0000-4000-8000-000000000700"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query or cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Supplied API key is invalid, revoked or expired (omitting Authorization is allowed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Supplied key lacks the endpoint scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not public",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Read-only endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Quota exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          }
        },
        "x-key-scope": "catalog:read"
      }
    },
    "/tracks": {
      "get": {
        "operationId": "listTracks",
        "summary": "List tracks",
        "tags": [
          "Tracks"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Maximum items per page."
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Opaque nextCursor from the previous page. Keep the same filters."
          }
        ],
        "x-schema": "Track",
        "x-list": true,
        "description": "Public catalog metadata. This response grants no track audio streaming or download rights. Anonymous access is allowed. Scope when using a key: `catalog:read`. Ordered by ID ascending.",
        "responses": {
          "200": {
            "description": "Success. Example IDs are illustrative.",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID."
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Requests per UTC minute: 30 per client IP anonymously, or 60 per valid key."
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Quota reset as Unix seconds."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Track"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 17,
                      "artist": "Example artist",
                      "title": "Example track",
                      "durationSeconds": 240,
                      "explicit": false
                    }
                  ],
                  "pagination": {
                    "limit": 50,
                    "nextCursor": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query or cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Supplied API key is invalid, revoked or expired (omitting Authorization is allowed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Supplied key lacks the endpoint scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not public",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Read-only endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Quota exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          }
        },
        "x-key-scope": "catalog:read"
      }
    },
    "/tracks/{id}": {
      "get": {
        "operationId": "getTrack",
        "summary": "Get one track",
        "tags": [
          "Tracks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 1
            },
            "description": "Manager track ID. Not a reLive ID."
          }
        ],
        "x-schema": "Track",
        "x-list": false,
        "description": "Public catalog metadata. This response grants no track audio streaming or download rights. Anonymous access is allowed. Scope when using a key: `catalog:read`.",
        "responses": {
          "200": {
            "description": "Success. Example IDs are illustrative.",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID."
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Requests per UTC minute: 30 per client IP anonymously, or 60 per valid key."
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Quota reset as Unix seconds."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Track"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 17,
                    "artist": "Example artist",
                    "title": "Example track",
                    "durationSeconds": 240,
                    "explicit": false
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query or cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Supplied API key is invalid, revoked or expired (omitting Authorization is allowed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Supplied key lacks the endpoint scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not public",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Read-only endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Quota exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          }
        },
        "x-key-scope": "catalog:read"
      }
    },
    "/recordings/{id}/cuesheet": {
      "get": {
        "operationId": "getRecordingCuesheet",
        "summary": "Read a recording’s cue sheet",
        "tags": [
          "Recordings"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 1
            },
            "description": "Manager file ID of a published recording."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Maximum items per page."
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Opaque nextCursor from the previous page. Keep the same filters."
          }
        ],
        "x-schema": "Cue",
        "x-list": true,
        "description": "An as-aired tracklist entry. timingSource is an extensible label such as exact, provided, detected or interpolated; null means unknown. trackId is null for unmatched or non-public tracks. Offsets are not necessarily sample-accurate. Anonymous access is allowed. Scope when using a key: `catalog:read`. Ordered by offsetSeconds, then ID.",
        "responses": {
          "200": {
            "description": "Success. Example IDs are illustrative.",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID."
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Requests per UTC minute: 30 per client IP anonymously, or 60 per valid key."
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Quota reset as Unix seconds."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Cue"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 25,
                      "offsetSeconds": 125.5,
                      "endSeconds": 365.5,
                      "timingSource": "provided",
                      "artist": "Example artist",
                      "title": "Example track",
                      "trackId": 17
                    }
                  ],
                  "pagination": {
                    "limit": 50,
                    "nextCursor": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query or cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Supplied API key is invalid, revoked or expired (omitting Authorization is allowed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Supplied key lacks the endpoint scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not public",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Read-only endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Quota exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          }
        },
        "x-key-scope": "catalog:read"
      }
    },
    "/recordings/{id}/chat": {
      "get": {
        "operationId": "getRecordingChat",
        "summary": "Read a recording’s replay chat",
        "tags": [
          "Recordings"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 1
            },
            "description": "Manager file ID of a published recording."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Maximum items per page."
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Opaque nextCursor from the previous page. Keep the same filters."
          }
        ],
        "x-schema": "ChatMessage",
        "x-list": true,
        "description": "An edited archived replay message or host commentary line. type is an extensible label (message, action, join, part, topic, and others). No raw payload or platform account IDs are exposed. Text is untrusted plain text; escape it when displaying HTML. Replay audience messages at offsetSeconds rather than showing future conversation. Anonymous access is allowed. Scope when using a key: `chat:read`. Ordered by offsetSeconds, then ID.",
        "responses": {
          "200": {
            "description": "Success. Example IDs are illustrative.",
            "headers": {
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                },
                "description": "Support correlation ID."
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Requests per UTC minute: 30 per client IP anonymously, or 60 per valid key."
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Quota reset as Unix seconds."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ChatMessage"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 91,
                      "offsetSeconds": 130,
                      "channel": {
                        "id": 8,
                        "label": "SceneSat chat",
                        "kind": "audience"
                      },
                      "type": "message",
                      "author": "Listener",
                      "text": "Hello!"
                    }
                  ],
                  "pagination": {
                    "limit": 50,
                    "nextCursor": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query or cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Supplied API key is invalid, revoked or expired (omitting Authorization is allowed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Supplied key lacks the endpoint scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not public",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Read-only endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Quota exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            }
          }
        },
        "x-key-scope": "chat:read"
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "ssapi_<64 hex characters>",
        "description": "Optional operator-issued application key for a higher quota (60 requests per minute instead of 30 anonymously). Send Authorization: Bearer <key>. Supplied keys must be valid and have the endpoint scope; invalid keys never fall back to anonymous access. Do not embed secrets in browser or mobile bundles."
      }
    },
    "schemas": {
      "Channel": {
        "type": "object",
        "required": [
          "id",
          "slug",
          "title",
          "description",
          "kind",
          "websiteUrl",
          "livestreamUrl"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "websiteUrl": {
            "type": "string",
            "format": "uri",
            "nullable": true
          },
          "livestreamUrl": {
            "type": "string",
            "format": "uri",
            "nullable": true
          }
        },
        "description": "An archive channel (SceneSat or a guest station). These numeric IDs identify archive channels; audio playout channels have a separate namespace of names."
      },
      "Recording": {
        "type": "object",
        "required": [
          "id",
          "showId",
          "channelId",
          "medium",
          "title",
          "host",
          "description",
          "startedAt",
          "durationSeconds",
          "mediaUrl"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "showId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "channelId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "nullable": true
          },
          "medium": {
            "type": "string",
            "enum": [
              "audio"
            ]
          },
          "title": {
            "type": "string"
          },
          "host": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "startedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "durationSeconds": {
            "type": "number"
          },
          "mediaUrl": {
            "type": "string",
            "format": "uri",
            "nullable": true
          }
        },
        "description": "A published primary audio recording, identified by its Manager file ID. showId identifies the broadcast; neither ID is a legacy reLive stream ID. Replacing the recording file changes the recording ID. Video and secondary recordings are not included in this initial collection."
      },
      "Track": {
        "type": "object",
        "required": [
          "id",
          "artist",
          "title",
          "durationSeconds",
          "explicit"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "artist": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "durationSeconds": {
            "type": "number"
          },
          "explicit": {
            "type": "boolean"
          }
        },
        "description": "Public catalog metadata. This response grants no track audio streaming or download rights."
      },
      "Cue": {
        "type": "object",
        "required": [
          "id",
          "offsetSeconds",
          "endSeconds",
          "timingSource",
          "artist",
          "title",
          "trackId"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "offsetSeconds": {
            "type": "number"
          },
          "endSeconds": {
            "type": "number",
            "nullable": true
          },
          "timingSource": {
            "type": "string",
            "nullable": true
          },
          "artist": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "trackId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "nullable": true
          }
        },
        "description": "An as-aired tracklist entry. timingSource is an extensible label such as exact, provided, detected or interpolated; null means unknown. trackId is null for unmatched or non-public tracks. Offsets are not necessarily sample-accurate."
      },
      "ChatMessage": {
        "type": "object",
        "required": [
          "id",
          "offsetSeconds",
          "channel",
          "type",
          "author",
          "text"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "offsetSeconds": {
            "type": "number"
          },
          "channel": {
            "type": "object",
            "required": [
              "id",
              "label",
              "kind"
            ],
            "properties": {
              "id": {
                "type": "integer",
                "format": "int64",
                "minimum": 1
              },
              "label": {
                "type": "string"
              },
              "kind": {
                "type": "string",
                "enum": [
                  "audience",
                  "commentary"
                ]
              }
            }
          },
          "type": {
            "type": "string"
          },
          "author": {
            "type": "string"
          },
          "text": {
            "type": "string"
          }
        },
        "description": "An edited archived replay message or host commentary line. type is an extensible label (message, action, join, part, topic, and others). No raw payload or platform account IDs are exposed. Text is untrusted plain text; escape it when displaying HTML. Replay audience messages at offsetSeconds rather than showing future conversation."
      },
      "Pagination": {
        "type": "object",
        "required": [
          "limit",
          "nextCursor"
        ],
        "properties": {
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100
          },
          "nextCursor": {
            "type": "string",
            "nullable": true
          }
        },
        "description": "Pass nextCursor unchanged as cursor with the same endpoint and filters. Null means the final page. Collections are live views, not transactional snapshots; an export may need a fresh scan after editorial changes."
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "requestId"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "requestId": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}
