{
  "openapi": "3.1.0",
  "info": {
    "title": "Maktaba API",
    "version": "0.1.0",
    "summary": "Self-hosted video library with automatic transcription, semantic search, and adaptive streaming.",
    "description": "The Maktaba API is the control plane for a self-hosted video library. It exposes\nlibraries, videos, transcripts, search, processing jobs, streaming sessions, and\nuser state over a uniform JSON surface.\n\n## Conventions\n\n- **Transport.** HTTP/1.1 and HTTP/2 over TLS. JSON request and response bodies\n  use `Content-Type: application/json; charset=utf-8`. Error responses use\n  [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) `application/problem+json`.\n- **Identifiers.** Resource ids are UUIDv4 unless documented otherwise.\n  `Job.id` and `AudioTrack.id` are 64-bit integers.\n- **Times.** All timestamps are RFC 3339 / ISO 8601 in UTC (`Z` suffix).\n  Durations and offsets are seconds as floats.\n- **Pagination.** Cursor-based. Pass `limit` (default 50, max 200) and the\n  opaque `cursor` returned from the previous page. The page envelope is\n  `{ items: [...], next: \"<cursor>\" | null }`. Cursors encode\n  `(updated_at DESC, id DESC)` and rotate when the schema version changes.\n- **Idempotency.** All `POST /api/jobs/{id}/{action}` endpoints are idempotent.\n  Clients may set `Idempotency-Key: <uuid>` on `POST` and `PATCH` requests; the\n  server replays the original response for 24 hours.\n- **Request correlation.** Every response carries `X-Request-Id` (UUIDv7).\n  Clients may pass their own `X-Request-Id` to thread it through.\n- **Rate limits.** Default: 600 requests / minute / authenticated user, 6 000 /\n  minute / IP. Exhausted requests get `429 Too Many Requests` with `Retry-After`\n  and headers `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`.\n\n## Authentication\n\nTwo equivalent paths backed by the same identity:\n\n- **Web** uses `httpOnly` cookies set by `POST /api/auth/login`. State-changing\n  requests must also include the `X-CSRF-Token` header (value mirrored from the\n  `csrf_token` cookie).\n- **Mobile / desktop / TV** uses bearer JWT (RS256, 15 min) with a long-lived\n  opaque refresh token. Public keys are exposed at `/api/.well-known/jwks.json`.\n\nSingle-user installs may pass a static admin token via `Authorization: Bearer`.\n\n## Streaming\n\nThe `/api/sessions` endpoints mint short-lived signed URLs that the client\nsends to the Streaming Service (separate origin) directly. Bytes never round\ntrip the API.\n\n## WebSocket\n\n`GET /api/ws` upgrades to a multiplexed event stream. Messages are JSON\n`{ type, data, at }` envelopes. Clients subscribe by sending\n`{ type: \"subscribe\", channels: [\"jobs\", \"playback\", \"library\"] }` on connect.\n",
    "contact": {
      "name": "Maktaba",
      "url": "https://github.com/maktaba/maktaba"
    },
    "license": {
      "name": "AGPL-3.0",
      "identifier": "AGPL-3.0"
    }
  },
  "servers": [
    {
      "url": "https://{host}/api",
      "description": "Production",
      "variables": {
        "host": {
          "default": "maktaba.local",
          "description": "The host where the API service is reachable."
        }
      }
    },
    {
      "url": "http://localhost:8080/api",
      "description": "Local development"
    }
  ],
  "externalDocs": {
    "description": "Architecture spec",
    "url": "https://github.com/maktaba/maktaba/blob/main/specs/architecture.md"
  },
  "tags": [
    {
      "name": "Auth",
      "description": "Login, token refresh, session lifecycle."
    },
    {
      "name": "Libraries",
      "description": "Folder collections shared by configuration and language profile."
    },
    {
      "name": "Videos",
      "description": "Video metadata, processing controls, transcript window."
    },
    {
      "name": "Search",
      "description": "Hybrid (FTS + semantic) search with filters and saved queries."
    },
    {
      "name": "Jobs",
      "description": "Pipeline job state and pause / resume / cancel / retry control."
    },
    {
      "name": "Queue",
      "description": "Aggregate queue statistics."
    },
    {
      "name": "Collections",
      "description": "User-curated and smart collections of videos."
    },
    {
      "name": "Tags",
      "description": "Tag taxonomy."
    },
    {
      "name": "Speakers",
      "description": "Speaker labels derived from diarization."
    },
    {
      "name": "Watch progress",
      "description": "Per-user playback position and completion state."
    },
    {
      "name": "Sessions",
      "description": "Streaming session lifecycle (mint signed URLs, close, capabilities)."
    },
    {
      "name": "Settings",
      "description": "App-wide and per-user settings."
    },
    {
      "name": "Devices",
      "description": "Push-notification device registration."
    },
    {
      "name": "Recommendations",
      "description": "Personalized video suggestions."
    },
    {
      "name": "System",
      "description": "Health, version, metrics. Open to monitoring agents."
    },
    {
      "name": "WebSocket",
      "description": "Real-time event channel (documented; protocol is WS)."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "cookieAuth": []
    }
  ],
  "paths": {
    "/auth/login": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Authenticate with username and password",
        "description": "On success, sets `auth` and `csrf_token` cookies (web) **and** returns a\nbearer access token + refresh token in the body (mobile/TV). Clients use\nwhichever they need; the server issues both unconditionally.\n",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              },
              "example": {
                "username": "admin",
                "password": "correct horse battery staple"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Authenticated.",
            "headers": {
              "Set-Cookie": {
                "schema": {
                  "type": "string"
                },
                "description": "Sets `auth` (httpOnly secure) and `csrf_token` (readable) cookies."
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthTokenResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "operationId": "login"
      }
    },
    "/auth/register": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Create a new user account",
        "description": "Disabled in single-user mode. When enabled, requires either an admin token\n(multi-user with admin-only invites) or open registration if configured.\n",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Account created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthTokenResponse"
                }
              }
            }
          },
          "403": {
            "description": "Registration disabled.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Username already taken.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "operationId": "register"
      }
    },
    "/auth/refresh": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Exchange a refresh token for a fresh access token",
        "description": "Rotating refresh tokens; the previous refresh token is invalidated on use.\nWeb clients send the refresh token via the `refresh_token` cookie; native\nclients send `{refresh_token}` in the body.\n",
        "security": [],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "refresh_token": {
                    "type": "string",
                    "description": "Required for native clients."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "New token pair.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthTokenResponse"
                }
              }
            }
          },
          "401": {
            "description": "Refresh token invalid, revoked, or expired.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "operationId": "refreshToken"
      }
    },
    "/auth/logout": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Revoke current credentials",
        "description": "Clears auth cookies and revokes the bearer refresh token.",
        "responses": {
          "204": {
            "description": "Logged out.",
            "headers": {
              "Set-Cookie": {
                "schema": {
                  "type": "string"
                },
                "description": "Clears `auth`, `csrf_token`, `refresh_token` cookies."
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "logout"
      }
    },
    "/auth/me": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Return the authenticated user",
        "responses": {
          "200": {
            "description": "Authenticated user profile.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "operationId": "getCurrentUser"
      }
    },
    "/libraries": {
      "get": {
        "tags": [
          "Libraries"
        ],
        "summary": "List libraries",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of libraries.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Page"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Library"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "operationId": "listLibraries"
      },
      "post": {
        "tags": [
          "Libraries"
        ],
        "summary": "Create a library",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LibraryCreate"
              },
              "example": {
                "name": "Lectures",
                "roots": [
                  "/mnt/media/lectures"
                ],
                "settings": {
                  "language": "ar",
                  "stt_profile": "default"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Library created.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri-reference"
                },
                "description": "Canonical URL of the new library."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Library"
                }
              }
            }
          },
          "409": {
            "description": "Name already in use.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "createLibrary"
      }
    },
    "/libraries/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/LibraryId"
        }
      ],
      "get": {
        "tags": [
          "Libraries"
        ],
        "summary": "Fetch a library",
        "responses": {
          "200": {
            "description": "Library.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Library"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getLibrary"
      },
      "patch": {
        "tags": [
          "Libraries"
        ],
        "summary": "Update a library",
        "description": "Settings are deep-merged; pass `null` to clear a key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LibraryUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated library.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Library"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateLibrary"
      },
      "delete": {
        "tags": [
          "Libraries"
        ],
        "summary": "Delete a library",
        "parameters": [
          {
            "in": "query",
            "name": "purge",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "When `true`, also unlinks media files from disk."
          },
          {
            "in": "query",
            "name": "confirm",
            "schema": {
              "type": "string"
            },
            "description": "Required when `purge=true`. Must equal the library id."
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "207": {
            "description": "Multi-status — soft delete done, some files failed to unlink.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "failed_paths": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "412": {
            "description": "Confirmation token missing or wrong.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteLibrary"
      }
    },
    "/libraries/{id}/scan": {
      "parameters": [
        {
          "$ref": "#/components/parameters/LibraryId"
        }
      ],
      "post": {
        "tags": [
          "Libraries"
        ],
        "summary": "Enqueue a scan job for a library",
        "description": "Returns immediately with the scan job id; progress is published over WebSocket.",
        "responses": {
          "202": {
            "description": "Scan enqueued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "job_id"
                  ],
                  "properties": {
                    "job_id": {
                      "type": "integer",
                      "format": "int64"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "scanLibrary"
      }
    },
    "/libraries/{id}/stats": {
      "parameters": [
        {
          "$ref": "#/components/parameters/LibraryId"
        }
      ],
      "get": {
        "tags": [
          "Libraries"
        ],
        "summary": "Aggregate stats for a library",
        "responses": {
          "200": {
            "description": "Library stats.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LibraryStats"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getLibraryStats"
      }
    },
    "/videos": {
      "get": {
        "tags": [
          "Videos"
        ],
        "summary": "List videos",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "in": "query",
            "name": "library",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "in": "query",
            "name": "language",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 2,
                "maxLength": 5
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "in": "query",
            "name": "type",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true,
            "description": "Filter by `content_type` (e.g. `lecture`, `film`)."
          },
          {
            "in": "query",
            "name": "tag",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "in": "query",
            "name": "q",
            "schema": {
              "type": "string",
              "maxLength": 256
            },
            "description": "Lightweight FTS over title + description. Use `POST /search` for full search."
          },
          {
            "in": "query",
            "name": "sort",
            "schema": {
              "type": "string",
              "enum": [
                "updated_at",
                "created_at",
                "title",
                "duration_sec",
                "relevance"
              ],
              "default": "created_at"
            },
            "description": "Without one, the most recently added first (`created_at`); with `q`, `relevance` (titles that start with `q`, then titles that hold it, then descriptions only, each newest-added first). A sort the caller names always wins; `relevance` needs `q`."
          }
        ],
        "responses": {
          "200": {
            "description": "Page of videos.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Page"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/VideoSummary"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid sort or filter.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listVideos"
      }
    },
    "/videos/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/VideoId"
        }
      ],
      "get": {
        "tags": [
          "Videos"
        ],
        "summary": "Fetch a video with full metadata",
        "responses": {
          "200": {
            "description": "Video.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoDetail"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVideo"
      },
      "patch": {
        "tags": [
          "Videos"
        ],
        "summary": "Update mutable video fields",
        "description": "Body is capped at 8 KiB.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VideoUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated video.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoDetail"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "description": "Body too large.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateVideo"
      },
      "delete": {
        "tags": [
          "Videos"
        ],
        "summary": "Delete a video",
        "parameters": [
          {
            "in": "query",
            "name": "purge",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "in": "query",
            "name": "confirm",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Required when `purge=true`; must equal the video id."
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "412": {
            "description": "Confirmation required for purge.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteVideo"
      }
    },
    "/videos/{id}/process": {
      "parameters": [
        {
          "$ref": "#/components/parameters/VideoId"
        }
      ],
      "post": {
        "tags": [
          "Videos"
        ],
        "summary": "Enqueue a single processing stage for a video",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "stage"
                ],
                "properties": {
                  "stage": {
                    "$ref": "#/components/schemas/JobStage"
                  },
                  "priority": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 999,
                    "default": 100
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job enqueued (or returned if it already existed).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "job_id",
                    "stage",
                    "state"
                  ],
                  "properties": {
                    "job_id": {
                      "type": "integer",
                      "format": "int64"
                    },
                    "stage": {
                      "$ref": "#/components/schemas/JobStage"
                    },
                    "state": {
                      "$ref": "#/components/schemas/JobState"
                    },
                    "note": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`stage` cannot be enqueued at video scope (e.g. `scan`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "processVideo"
      }
    },
    "/videos/{id}/reprocess": {
      "parameters": [
        {
          "$ref": "#/components/parameters/VideoId"
        }
      ],
      "post": {
        "tags": [
          "Videos"
        ],
        "summary": "Reset state and reprocess from a given stage",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "from_stage"
                ],
                "properties": {
                  "from_stage": {
                    "$ref": "#/components/schemas/JobStage"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Jobs enqueued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "job_ids",
                    "from_stage",
                    "predecessor_state"
                  ],
                  "properties": {
                    "job_ids": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "format": "int64"
                      }
                    },
                    "from_stage": {
                      "$ref": "#/components/schemas/JobStage"
                    },
                    "predecessor_state": {
                      "$ref": "#/components/schemas/VideoState"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "reprocessVideo"
      }
    },
    "/videos/{id}/segments": {
      "parameters": [
        {
          "$ref": "#/components/parameters/VideoId"
        }
      ],
      "get": {
        "tags": [
          "Videos"
        ],
        "summary": "Read a transcript window for a video",
        "parameters": [
          {
            "in": "query",
            "name": "from",
            "schema": {
              "type": "number",
              "format": "float",
              "default": 0
            },
            "description": "Inclusive start (seconds)."
          },
          {
            "in": "query",
            "name": "to",
            "schema": {
              "type": "number",
              "format": "float"
            },
            "description": "Exclusive end (seconds). Defaults to end of transcript."
          },
          {
            "in": "query",
            "name": "words",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "When `true`, populates `Segment.words` with word-level timing."
          },
          {
            "in": "query",
            "name": "include_superseded",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Include segments from superseded transcripts."
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Segments in `[from, to)`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SegmentWindow"
                }
              }
            }
          },
          "400": {
            "description": "Invalid time window or query.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listVideoSegments"
      }
    },
    "/videos/{id}/subtitles": {
      "parameters": [
        {
          "$ref": "#/components/parameters/VideoId"
        }
      ],
      "get": {
        "tags": [
          "Videos"
        ],
        "summary": "List subtitle tracks (signed URLs)",
        "parameters": [
          {
            "in": "header",
            "name": "Accept-Language",
            "schema": {
              "type": "string"
            },
            "description": "Preferred language ranking; orders the result."
          }
        ],
        "responses": {
          "200": {
            "description": "Subtitle tracks. URLs are signed and short-lived.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Subtitle"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listVideoSubtitles"
      }
    },
    "/videos/{id}/chapters": {
      "parameters": [
        {
          "$ref": "#/components/parameters/VideoId"
        }
      ],
      "get": {
        "tags": [
          "Videos"
        ],
        "summary": "List chapters for a video (ordered by `seq`)",
        "responses": {
          "200": {
            "description": "Chapters.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Chapter"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listVideoChapters"
      }
    },
    "/videos/{id}/tags": {
      "parameters": [
        {
          "$ref": "#/components/parameters/VideoId"
        }
      ],
      "patch": {
        "tags": [
          "Tags"
        ],
        "summary": "Add or remove tags on a video",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "add": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 64
                    }
                  },
                  "remove": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated video tag list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Tag"
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "patchVideoTags"
      }
    },
    "/videos/{id}/pause": {
      "parameters": [
        {
          "$ref": "#/components/parameters/VideoId"
        }
      ],
      "post": {
        "tags": [
          "Jobs"
        ],
        "summary": "Pause every active job for this video",
        "responses": {
          "200": {
            "description": "Number of jobs flagged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AffectedCount"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "pauseVideoJobs"
      }
    },
    "/videos/{id}/resume": {
      "parameters": [
        {
          "$ref": "#/components/parameters/VideoId"
        }
      ],
      "post": {
        "tags": [
          "Jobs"
        ],
        "summary": "Resume every paused job for this video",
        "responses": {
          "200": {
            "description": "Number of jobs resumed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AffectedCount"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "resumeVideoJobs"
      }
    },
    "/videos/{id}/cancel": {
      "parameters": [
        {
          "$ref": "#/components/parameters/VideoId"
        }
      ],
      "post": {
        "tags": [
          "Jobs"
        ],
        "summary": "Cancel every active job for this video",
        "responses": {
          "200": {
            "description": "Number of jobs cancelled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AffectedCount"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "cancelVideoJobs"
      }
    },
    "/videos/{id}/progress": {
      "parameters": [
        {
          "$ref": "#/components/parameters/VideoId"
        }
      ],
      "get": {
        "tags": [
          "Watch progress"
        ],
        "summary": "Fetch playback state for the current user and a video",
        "responses": {
          "200": {
            "description": "Playback state. Returns zeros if the user has not played the video yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlaybackState"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getWatchProgress"
      },
      "put": {
        "tags": [
          "Watch progress"
        ],
        "summary": "Upsert playback state for the current user and a video",
        "description": "Server-side debounced (1 Hz per user/video). Fires `playback.completed`\non `false → true` transition. Excluded from per-user rate limits.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlaybackStateInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged.",
            "headers": {
              "Maktaba-Warning": {
                "schema": {
                  "type": "string"
                },
                "description": "Set if `position_sec` was clamped to duration."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlaybackState"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateWatchProgress"
      }
    },
    "/search": {
      "get": {
        "tags": [
          "Search"
        ],
        "summary": "Hybrid search across the library",
        "description": "Combines BM25 full-text and vector similarity using reciprocal rank fusion.\nResults carry per-stage timing under `took_ms`. When semantic backend is\noffline, the response sets `degraded=true` and falls back to FTS only.\n",
        "parameters": [
          {
            "in": "query",
            "name": "q",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 1024
            }
          },
          {
            "in": "query",
            "name": "mode",
            "schema": {
              "type": "string",
              "enum": [
                "hybrid",
                "fts",
                "semantic"
              ],
              "default": "hybrid"
            }
          },
          {
            "in": "query",
            "name": "language",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 2,
                "maxLength": 5
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "in": "query",
            "name": "library",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "in": "query",
            "name": "tag",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "in": "query",
            "name": "speaker",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Search hits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "503": {
            "description": "Semantic backend unavailable and `mode=semantic` was requested.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "searchByQuery"
      },
      "post": {
        "tags": [
          "Search"
        ],
        "summary": "Hybrid search with a structured request body",
        "description": "Same semantics as `GET /search`; the body form is preferred when filters get long.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SearchRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Search hits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "search"
      }
    },
    "/search/suggest": {
      "get": {
        "tags": [
          "Search"
        ],
        "summary": "Autocomplete suggestions",
        "parameters": [
          {
            "in": "query",
            "name": "q",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 64
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 10 suggestions ranked by frequency.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "suggestions"
                  ],
                  "properties": {
                    "suggestions": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "searchSuggest"
      }
    },
    "/search/save": {
      "post": {
        "tags": [
          "Search"
        ],
        "summary": "Save a search query for the current user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SavedSearchInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Saved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SavedSearch"
                }
              }
            }
          },
          "409": {
            "description": "A saved search with that name already exists for this user.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "saveSearch"
      }
    },
    "/search/saved": {
      "get": {
        "tags": [
          "Search"
        ],
        "summary": "List the current user's saved searches",
        "responses": {
          "200": {
            "description": "Saved searches.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SavedSearch"
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listSavedSearches"
      }
    },
    "/search/saved/{id}": {
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "delete": {
        "tags": [
          "Search"
        ],
        "summary": "Delete a saved search",
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteSavedSearch"
      }
    },
    "/jobs": {
      "get": {
        "tags": [
          "Jobs"
        ],
        "summary": "List processing jobs",
        "parameters": [
          {
            "in": "query",
            "name": "state",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/JobState"
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "in": "query",
            "name": "stage",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/JobStage"
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "in": "query",
            "name": "video",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of jobs.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Page"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Job"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listJobs"
      }
    },
    "/jobs/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobId"
        }
      ],
      "get": {
        "tags": [
          "Jobs"
        ],
        "summary": "Fetch a job",
        "responses": {
          "200": {
            "description": "Job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getJob"
      }
    },
    "/jobs/{id}/pause": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobId"
        }
      ],
      "post": {
        "tags": [
          "Jobs"
        ],
        "summary": "Request a graceful pause",
        "description": "Idempotent. With `?force=true`, flips state immediately and drops the in-flight segment.",
        "parameters": [
          {
            "in": "query",
            "name": "force",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job (with `pause_requested=true` or `state=paused`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Job is in a terminal state.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "pauseJob"
      }
    },
    "/jobs/{id}/resume": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobId"
        }
      ],
      "post": {
        "tags": [
          "Jobs"
        ],
        "summary": "Make a paused job re-claimable",
        "responses": {
          "200": {
            "description": "Job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Job is in a terminal state.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "resumeJob"
      }
    },
    "/jobs/{id}/cancel": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobId"
        }
      ],
      "post": {
        "tags": [
          "Jobs"
        ],
        "summary": "Cancel a job",
        "description": "Idempotent. Sets `cancel_requested`, or transitions `pending` → `cancelled` immediately.",
        "responses": {
          "200": {
            "description": "Job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Job is in a terminal state.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "cancelJob"
      }
    },
    "/jobs/{id}/retry": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobId"
        }
      ],
      "post": {
        "tags": [
          "Jobs"
        ],
        "summary": "Reset a failed job to `pending`",
        "responses": {
          "200": {
            "description": "Job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Job is not in `failed` state.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "retryJob"
      }
    },
    "/queue/stats": {
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "Aggregate queue stats per stage",
        "responses": {
          "200": {
            "description": "Queue stats.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueueStats"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQueueStats"
      }
    },
    "/collections": {
      "get": {
        "tags": [
          "Collections"
        ],
        "summary": "List collections",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of collections.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Page"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Collection"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listCollections"
      },
      "post": {
        "tags": [
          "Collections"
        ],
        "summary": "Create a collection (manual or smart)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CollectionCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Collection created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Collection"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "createCollection"
      }
    },
    "/collections/{id}": {
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Collections"
        ],
        "summary": "Fetch a collection",
        "responses": {
          "200": {
            "description": "Collection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Collection"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getCollection"
      },
      "patch": {
        "tags": [
          "Collections"
        ],
        "summary": "Update a collection",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CollectionUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated collection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Collection"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateCollection"
      },
      "delete": {
        "tags": [
          "Collections"
        ],
        "summary": "Delete a collection",
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteCollection"
      }
    },
    "/collections/{id}/videos": {
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Collections"
        ],
        "summary": "List videos in a collection",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of videos.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Page"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/VideoSummary"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listCollectionVideos"
      },
      "post": {
        "tags": [
          "Collections"
        ],
        "summary": "Add a video to a collection",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "video_id"
                ],
                "properties": {
                  "video_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "position": {
                    "type": "integer",
                    "minimum": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Video added.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CollectionItem"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "addCollectionVideo"
      }
    },
    "/collections/{id}/videos/{videoId}": {
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "in": "path",
          "name": "videoId",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "delete": {
        "tags": [
          "Collections"
        ],
        "summary": "Remove a video from a collection",
        "responses": {
          "204": {
            "description": "Removed."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "removeCollectionVideo"
      }
    },
    "/tags": {
      "get": {
        "tags": [
          "Tags"
        ],
        "summary": "List tags",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "in": "query",
            "name": "q",
            "schema": {
              "type": "string",
              "maxLength": 64
            },
            "description": "Substring filter."
          }
        ],
        "responses": {
          "200": {
            "description": "Page of tags.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Page"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Tag"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listTags"
      },
      "post": {
        "tags": [
          "Tags"
        ],
        "summary": "Create a tag",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Tag.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tag"
                }
              }
            }
          },
          "409": {
            "description": "Tag already exists.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "createTag"
      }
    },
    "/sessions": {
      "post": {
        "tags": [
          "Sessions"
        ],
        "summary": "Mint a streaming session",
        "description": "Returns a signed manifest URL (HLS / DASH) or direct URL. The client passes\nit to its player; bytes are served by the Streaming Service, not the API.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SessionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Session minted.",
            "headers": {
              "Maktaba-Warning": {
                "schema": {
                  "type": "string"
                },
                "description": "e.g. `start-sec-clamped`."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "403": {
            "description": "User cannot access this video.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "507": {
            "description": "Too little free on the streaming service's disk to prepare (transcode) the video (code `insufficient_storage`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "createSession"
      }
    },
    "/sessions/{id}": {
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Sessions"
        ],
        "summary": "Fetch a session",
        "responses": {
          "200": {
            "description": "Session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getSession"
      },
      "delete": {
        "tags": [
          "Sessions"
        ],
        "summary": "Close a streaming session",
        "description": "Frees the transcoder slot and revokes the signed URL.",
        "responses": {
          "204": {
            "description": "Closed."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "closeSession"
      }
    },
    "/sessions/{id}/progress": {
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "tags": [
          "Sessions"
        ],
        "summary": "Heartbeat session progress",
        "description": "Server-side debounced (1 Hz). Mirrors the position into `playback_state`.\nExcluded from rate limits.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "position_sec"
                ],
                "properties": {
                  "position_sec": {
                    "type": "number",
                    "minimum": 0
                  },
                  "completed": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accepted": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "sessionProgressHeartbeat"
      }
    },
    "/sessions/capabilities": {
      "get": {
        "tags": [
          "Sessions"
        ],
        "summary": "Streaming server capabilities",
        "description": "Cached 60 s; invalidated by the streaming service when the encoder profile changes.",
        "responses": {
          "200": {
            "description": "Capabilities.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StreamCapabilities"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getStreamCapabilities"
      }
    },
    "/settings": {
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "Read effective settings",
        "description": "Merges defaults, file, env, and DB. Secrets are never returned.",
        "responses": {
          "200": {
            "description": "Settings.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Settings"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getSettings"
      },
      "put": {
        "tags": [
          "Settings"
        ],
        "summary": "Replace the DB-stored settings layer",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SettingsInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated settings (re-read after write).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Settings"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateSettings"
      }
    },
    "/settings/stt-backends": {
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "Enumerate available STT backends",
        "responses": {
          "200": {
            "description": "List of backends.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SttBackend"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listSttBackends"
      }
    },
    "/settings/stt-test": {
      "post": {
        "tags": [
          "Settings"
        ],
        "summary": "Dry-run a transcribe request against a backend",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "backend"
                ],
                "properties": {
                  "backend": {
                    "type": "string"
                  },
                  "config": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Probe result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "detail": {
                      "type": "string"
                    },
                    "latency_ms": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "testSttBackend"
      }
    },
    "/devices/register": {
      "post": {
        "tags": [
          "Devices"
        ],
        "summary": "Register or rotate a push-notification device",
        "description": "Idempotent on `(user_id, platform, bundle_id)`. Returns 201 on first\nregistration, 200 on token rotation.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceRegisterInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Device updated (token rotated).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Device"
                }
              }
            }
          },
          "201": {
            "description": "Device registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Device"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "registerDevice"
      }
    },
    "/devices": {
      "get": {
        "tags": [
          "Devices"
        ],
        "summary": "List the current user's registered devices",
        "description": "Push tokens are redacted in this view.",
        "responses": {
          "200": {
            "description": "Devices.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Device"
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listDevices"
      }
    },
    "/devices/{id}": {
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "delete": {
        "tags": [
          "Devices"
        ],
        "summary": "Revoke a registered device",
        "responses": {
          "204": {
            "description": "Revoked."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "revokeDevice"
      }
    },
    "/recommendations": {
      "get": {
        "tags": [
          "Recommendations"
        ],
        "summary": "Personalized video recommendations",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "in": "query",
            "name": "shelf",
            "schema": {
              "type": "string",
              "enum": [
                "continue_watching",
                "more_like_this",
                "recently_added",
                "trending"
              ]
            },
            "description": "When set, returns just one shelf."
          }
        ],
        "responses": {
          "200": {
            "description": "Recommendations grouped by shelf.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecommendationsResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getRecommendations"
      }
    },
    "/ws": {
      "get": {
        "tags": [
          "WebSocket"
        ],
        "summary": "Upgrade to the realtime event stream",
        "description": "HTTP/1.1 upgrade to a WebSocket. Documented here for client-generation\ntools that want to know the protocol exists; the message envelope is in\nthe schemas section as `WsMessage`.\n\n### Channels\n- `jobs` — job state and progress.\n- `playback` — `playback.progress`, `playback.completed` events.\n- `library` — `library.updated`, `videos.state_changed` events.\n- `system` — `profiles_changed`, `health_changed`.\n\n### Subscribe\n```json\n{ \"type\": \"subscribe\", \"channels\": [\"jobs\", \"playback\"] }\n```\n\n### Sample event\n```json\n{\n  \"type\": \"jobs.progress\",\n  \"data\": { \"job_id\": 42, \"stage\": \"transcribe\", \"pct\": 0.18, \"eta_sec\": 612 },\n  \"at\": \"2026-05-05T13:21:04Z\"\n}\n```\n",
        "responses": {
          "101": {
            "description": "Switching protocols (WebSocket)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "operationId": "connectWebSocket",
        "x-ws-message-schema": {
          "$ref": "#/components/schemas/WsMessage"
        },
        "requestBody": {
          "$ref": "#/components/requestBodies/WsClientMessage"
        }
      }
    },
    "/system/health": {
      "servers": [
        {
          "url": "https://{host}",
          "variables": {
            "host": {
              "default": "maktaba.local"
            }
          }
        }
      ],
      "get": {
        "tags": [
          "System"
        ],
        "summary": "Liveness / readiness check",
        "description": "Mounted at both `/health` (off the API root, for monitoring agents) and\n`/api/system/health`. 503 if any required component is `down`.\n",
        "security": [],
        "responses": {
          "200": {
            "description": "All dependencies healthy or degraded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "503": {
            "description": "At least one required component is down.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "operationId": "getHealth"
      }
    },
    "/system/version": {
      "get": {
        "tags": [
          "System"
        ],
        "summary": "Build identification",
        "description": "Mounted at both `/version` and `/api/system/version`.",
        "security": [],
        "responses": {
          "200": {
            "description": "Version metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Version"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "operationId": "getVersion"
      }
    },
    "/system/metrics": {
      "get": {
        "tags": [
          "System"
        ],
        "summary": "Prometheus exposition (text format)",
        "description": "Mounted at `/metrics` on a separate listener (default `127.0.0.1:9090`).\nWhen `metrics_admin_token` is configured, requires `Authorization: Bearer`.\n",
        "security": [],
        "responses": {
          "200": {
            "description": "Prometheus text format.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid metrics token.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "operationId": "getMetrics"
      }
    },
    "/speakers": {
      "get": {
        "tags": [
          "Speakers"
        ],
        "summary": "List speakers",
        "operationId": "listSpeakers",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "in": "query",
            "name": "library",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of speakers.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Page"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Speaker"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/speakers/{id}": {
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "integer",
            "format": "int64"
          }
        }
      ],
      "patch": {
        "tags": [
          "Speakers"
        ],
        "summary": "Rename a speaker",
        "operationId": "renameSpeaker",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 128
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated speaker.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Speaker"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/speakers/merge": {
      "post": {
        "tags": [
          "Speakers"
        ],
        "summary": "Merge two speaker labels into one",
        "operationId": "mergeSpeakers",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "keep",
                  "drop"
                ],
                "properties": {
                  "keep": {
                    "type": "integer",
                    "format": "int64"
                  },
                  "drop": {
                    "type": "integer",
                    "format": "int64"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Merge result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "kept": {
                      "$ref": "#/components/schemas/Speaker"
                    },
                    "reassigned_segments": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/queues": {
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "Queue overview snapshot",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueuesSnapshot"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQueuesSnapshot"
      }
    },
    "/queues/graph": {
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "Stage graph and worker pools",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "stages": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/QueueStageInfo"
                      }
                    },
                    "pools": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WorkerPool"
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQueueGraph"
      }
    },
    "/queues/libraries": {
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "Per-library queue progress (cached 5 s)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LibraryQueueRow"
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQueueLibraries"
      }
    },
    "/queues/health": {
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "Health metrics over time",
        "parameters": [
          {
            "name": "hours",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 168,
              "default": 6
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "series": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/HealthSeries"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQueueHealth"
      }
    },
    "/queues/pause-all": {
      "post": {
        "tags": [
          "Queue"
        ],
        "summary": "Pause every queue",
        "responses": {
          "204": {
            "description": "Done."
          },
          "403": {
            "description": "Admin only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "pauseAllQueues"
      }
    },
    "/queues/resume-all": {
      "post": {
        "tags": [
          "Queue"
        ],
        "summary": "Resume every queue",
        "responses": {
          "204": {
            "description": "Done."
          },
          "403": {
            "description": "Admin only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "resumeAllQueues"
      }
    },
    "/queues/{stage}": {
      "parameters": [
        {
          "name": "stage",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "One queue: KPIs, running, next up, failures, log",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueueDetail"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQueueDetail"
      },
      "patch": {
        "tags": [
          "Queue"
        ],
        "summary": "Enable/disable a queue or set its write mode",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "write_mode": {
                    "type": "string",
                    "enum": [
                      "auto",
                      "apply",
                      "review"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueueStageInfo"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Admin only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Stage is not configured.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "patchQueue"
      }
    },
    "/queues/{stage}/rate": {
      "parameters": [
        {
          "name": "stage",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "Done/failed/skipped per 10 minutes",
        "parameters": [
          {
            "name": "hours",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 168,
              "default": 24
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bucket_sec": {
                      "type": "integer"
                    },
                    "points": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/QueueRatePoint"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQueueRate"
      }
    },
    "/queues/{stage}/jobs": {
      "parameters": [
        {
          "name": "stage",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "Jobs of a queue (keyset paged, newest first)",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma separated job states."
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200,
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/QueueJobRef"
                      }
                    },
                    "next": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listQueueJobs"
      }
    },
    "/queues/{stage}/failures": {
      "parameters": [
        {
          "name": "stage",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "Open failures grouped by cause",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "groups": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FailureGroup"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQueueFailures"
      }
    },
    "/queues/{stage}/pause": {
      "parameters": [
        {
          "name": "stage",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Queue"
        ],
        "summary": "Pause a queue (running jobs finish)",
        "parameters": [
          {
            "name": "interrupt",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Also ask cooperative stages (transcribe) to pause mid-run."
          }
        ],
        "responses": {
          "204": {
            "description": "Done."
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Admin only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "pauseQueue"
      }
    },
    "/queues/{stage}/resume": {
      "parameters": [
        {
          "name": "stage",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Queue"
        ],
        "summary": "Resume a queue",
        "responses": {
          "204": {
            "description": "Done."
          },
          "403": {
            "description": "Admin only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "resumeQueue"
      }
    },
    "/queues/{stage}/retry": {
      "parameters": [
        {
          "name": "stage",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Queue"
        ],
        "summary": "Retry failures of a cause or specific jobs",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "One of cause or job_ids is required.",
                "properties": {
                  "cause": {
                    "type": "string"
                  },
                  "job_ids": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enqueued": {
                      "type": "integer"
                    },
                    "coalesced": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Admin only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "retryQueueFailures"
      }
    },
    "/queues/{stage}/apply": {
      "parameters": [
        {
          "name": "stage",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Queue"
        ],
        "summary": "Apply reviewed external writes",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "job_ids": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    }
                  },
                  "all": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enqueued": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Admin only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "applyQueueReviews"
      }
    },
    "/pools": {
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "Worker pools",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pools": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WorkerPool"
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listPools"
      }
    },
    "/pools/{pool}": {
      "parameters": [
        {
          "name": "pool",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "put": {
        "tags": [
          "Queue"
        ],
        "summary": "Resize a pool",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "size"
                ],
                "properties": {
                  "size": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 64
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkerPool"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Admin only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "putPool"
      }
    },
    "/queue/retrigger": {
      "post": {
        "tags": [
          "Queue"
        ],
        "summary": "Re-add subjects to their entry queues",
        "description": "Every item is enqueued through the processing_jobs_enqueue SQL function in one transaction.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RetriggerRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RetriggerResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Admin only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "retriggerQueue"
      }
    },
    "/issues": {
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "All open issues (failures, awaiting review, stale graph)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "groups": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FailureGroup"
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listIssues"
      }
    },
    "/activity": {
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "Activity log (job_events)",
        "parameters": [
          {
            "name": "stage",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "library_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "level",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "info",
              "enum": [
                "debug",
                "info",
                "warn",
                "error"
              ]
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 500,
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/QueueLogLine"
                      }
                    },
                    "next": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listActivity"
      }
    },
    "/jobs/{id}/events": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobId"
        }
      ],
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "Log of one job",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/QueueLogLine"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listJobEvents"
      }
    },
    "/items/{kind}/{id}/history": {
      "parameters": [
        {
          "name": "kind",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "enum": [
              "video",
              "library",
              "series",
              "season",
              "catalog"
            ]
          }
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "History of one subject across queues",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200,
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueueItemHistory"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getItemHistory"
      }
    },
    "/workers": {
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "Registered workers and whether they are online",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "workers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/QueueWorker"
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listWorkers"
      }
    },
    "/extensions": {
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "Optional extensions (Plex, dizqueTV)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "extensions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ExtensionInfo"
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "listExtensions"
      }
    },
    "/extensions/{name}": {
      "parameters": [
        {
          "name": "name",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "put": {
        "tags": [
          "Queue"
        ],
        "summary": "Enable/disable an extension or update its settings (secrets are write-only)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "settings": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExtensionInfo"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Admin only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "putExtension"
      }
    },
    "/extensions/{name}/test": {
      "parameters": [
        {
          "name": "name",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Queue"
        ],
        "summary": "Test an extension connection (a list of checks)",
        "description": "Admin-only; runs on the home. Each check: key, ok, optional (a warning when not ok), skipped, detail, code, latency_ms. Plex: reachable, token, server, livetv, maktaba_tuner, dizquetv_tuner, path_map. dizqueTV: reachable, channels, plex_servers. Cloudflare Stream: see /tv/remote/test. Every run is audited (audit_log config/extension_test).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    },
                    "checked_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "lines": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "ok": {
                            "type": "boolean"
                          },
                          "optional": {
                            "type": "boolean"
                          },
                          "skipped": {
                            "type": "boolean"
                          },
                          "detail": {
                            "type": "string"
                          },
                          "code": {
                            "type": "string"
                          },
                          "latency_ms": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Admin only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "testExtension"
      }
    },
    "/extensions/plex/signin": {
      "post": {
        "tags": [
          "Queue"
        ],
        "summary": "Start \"Sign in with Plex\" (creates a plex.tv PIN)",
        "description": "Admin-only. Open auth_url in a browser window, then poll GET /extensions/plex/signin/{id}.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "auth_url": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Admin only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "502": {
            "description": "plex.tv could not be reached"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "plexSignin"
      }
    },
    "/extensions/plex/signin/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Queue"
        ],
        "summary": "Poll a Plex sign-in; once done, lists the account's Plex servers (no tokens)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "state": {
                      "type": "string",
                      "enum": [
                        "waiting",
                        "done"
                      ]
                    },
                    "servers": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "uri": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Admin only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "description": "plex.tv could not be reached"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "plexSigninStatus"
      }
    },
    "/extensions/plex/signin/{id}/use": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Queue"
        ],
        "summary": "Save the chosen Plex server (URL and its token) into the plex extension settings",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExtensionInfo"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Admin only.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The sign-in is not finished yet"
          },
          "default": {
            "$ref": "#/components/responses/InternalError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "plexSigninUse"
      }
    },
    "/ws/queues": {
      "get": {
        "tags": [
          "WebSocket"
        ],
        "summary": "Queue snapshot stream (Server-Sent Events)",
        "description": "SSE. Sends one `queues.snapshot` immediately, then every 2 s while subscribed. Ephemeral: no `id:` lines, no replay.",
        "responses": {
          "200": {
            "description": "text/event-stream",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "streamQueues"
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "RS256 access token (15 min). Mobile / desktop / TV clients pass it as\n`Authorization: Bearer <token>`. The Streaming Service validates the same\nJWT offline against the JWKS at `/.well-known/jwks.json`.\n"
      },
      "cookieAuth": {
        "type": "apiKey",
        "in": "cookie",
        "name": "auth",
        "description": "`httpOnly`, `secure`, `sameSite=lax` cookie set by `POST /auth/login`.\nState-changing requests must also send the `X-CSRF-Token` header.\n"
      }
    },
    "parameters": {
      "Limit": {
        "in": "query",
        "name": "limit",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        },
        "description": "Page size (1–200, default 50)."
      },
      "Cursor": {
        "in": "query",
        "name": "cursor",
        "schema": {
          "type": "string"
        },
        "description": "Opaque pagination cursor from the previous page's `next` field."
      },
      "LibraryId": {
        "in": "path",
        "name": "id",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "Library id."
      },
      "VideoId": {
        "in": "path",
        "name": "id",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "Video id."
      },
      "JobId": {
        "in": "path",
        "name": "id",
        "required": true,
        "schema": {
          "type": "integer",
          "format": "int64"
        },
        "description": "Job id (64-bit integer)."
      }
    },
    "headers": {
      "X-Request-Id": {
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "UUIDv7 correlation id. Echoed in every response."
      },
      "X-RateLimit-Limit": {
        "schema": {
          "type": "integer"
        },
        "description": "Requests allowed in the current window."
      },
      "X-RateLimit-Remaining": {
        "schema": {
          "type": "integer"
        },
        "description": "Requests remaining in the current window."
      },
      "X-RateLimit-Reset": {
        "schema": {
          "type": "integer"
        },
        "description": "Unix-epoch seconds when the window resets."
      },
      "Retry-After": {
        "schema": {
          "type": "integer"
        },
        "description": "Seconds to wait before retrying."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid credentials.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ProblemDetails"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ProblemDetails"
            }
          }
        }
      },
      "ValidationError": {
        "description": "Request body or parameters failed validation.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                {
                  "type": "object",
                  "properties": {
                    "errors": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FieldError"
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ProblemDetails"
            }
          }
        }
      },
      "Unavailable": {
        "description": "A downstream dependency (Pipeline or Streaming) is unavailable.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ProblemDetails"
            }
          }
        }
      },
      "InternalError": {
        "description": "Unhandled server error.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ProblemDetails"
            }
          }
        }
      }
    },
    "schemas": {
      "ProblemDetails": {
        "type": "object",
        "description": "RFC 9457 problem document.",
        "required": [
          "type",
          "title",
          "status",
          "request_id"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "Stable problem identifier (`https://maktaba.tv/problems/<slug>`).",
            "examples": [
              "https://maktaba.tv/problems/validation"
            ]
          },
          "title": {
            "type": "string",
            "description": "Short human-readable summary."
          },
          "status": {
            "type": "integer",
            "minimum": 100,
            "maximum": 599
          },
          "detail": {
            "type": "string",
            "description": "Human-readable explanation specific to this occurrence."
          },
          "instance": {
            "type": "string",
            "format": "uri-reference",
            "description": "URI of the resource that produced the problem."
          },
          "request_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUIDv7 correlation id (matches `X-Request-Id`)."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FieldError"
            },
            "description": "Present on validation failures."
          }
        }
      },
      "FieldError": {
        "type": "object",
        "required": [
          "field",
          "message"
        ],
        "properties": {
          "field": {
            "type": "string",
            "description": "Dotted-path to the offending field, e.g. `roots[0]`."
          },
          "message": {
            "type": "string"
          }
        }
      },
      "Page": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array"
          },
          "next": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cursor for the next page; `null` on the last page."
          }
        }
      },
      "AffectedCount": {
        "type": "object",
        "required": [
          "affected"
        ],
        "properties": {
          "affected": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "User": {
        "type": "object",
        "required": [
          "id",
          "username",
          "is_admin",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "username": {
            "type": "string"
          },
          "is_admin": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LoginRequest": {
        "type": "object",
        "required": [
          "username",
          "password"
        ],
        "properties": {
          "username": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "password": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256
          }
        }
      },
      "RegisterRequest": {
        "type": "object",
        "required": [
          "username",
          "password"
        ],
        "properties": {
          "username": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "password": {
            "type": "string",
            "minLength": 8,
            "maxLength": 256
          },
          "invite_token": {
            "type": "string"
          }
        }
      },
      "AuthTokenResponse": {
        "type": "object",
        "required": [
          "user",
          "access_token",
          "token_type",
          "expires_in"
        ],
        "properties": {
          "user": {
            "$ref": "#/components/schemas/User"
          },
          "access_token": {
            "type": "string",
            "description": "Short-lived (15 min) JWT."
          },
          "refresh_token": {
            "type": "string",
            "description": "Long-lived opaque token. Omitted for web clients (set via cookie)."
          },
          "token_type": {
            "type": "string",
            "enum": [
              "Bearer"
            ]
          },
          "expires_in": {
            "type": "integer",
            "description": "Lifetime of `access_token` in seconds.",
            "examples": [
              900
            ]
          }
        }
      },
      "Library": {
        "type": "object",
        "required": [
          "id",
          "name",
          "roots",
          "settings",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "roots": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Absolute filesystem paths."
          },
          "settings": {
            "type": "object",
            "additionalProperties": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LibraryCreate": {
        "type": "object",
        "required": [
          "name",
          "roots"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "roots": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string"
            }
          },
          "settings": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "LibraryUpdate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "roots": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "settings": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "LibraryStats": {
        "type": "object",
        "required": [
          "total_videos",
          "total_duration_sec",
          "by_state",
          "processed_pct",
          "by_language"
        ],
        "properties": {
          "total_videos": {
            "type": "integer"
          },
          "total_duration_sec": {
            "type": "number"
          },
          "processed_pct": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "by_state": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "by_language": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          }
        }
      },
      "VideoState": {
        "type": "string",
        "enum": [
          "discovered",
          "probed",
          "audio_extracted",
          "transcribed",
          "indexed",
          "ready",
          "failed"
        ]
      },
      "VideoSummary": {
        "type": "object",
        "required": [
          "id",
          "library_id",
          "title",
          "state",
          "playable",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "library_id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "detected_language": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 639-1 code."
          },
          "content_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "$ref": "#/components/schemas/VideoState"
          },
          "path": {
            "type": [
              "string",
              "null"
            ]
          },
          "playable": {
            "type": "boolean"
          },
          "duration_sec": {
            "type": [
              "number",
              "null"
            ]
          },
          "poster_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "VideoDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/VideoSummary"
          },
          {
            "type": "object",
            "properties": {
              "media_info": {
                "$ref": "#/components/schemas/MediaInfo"
              },
              "audio_tracks": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/AudioTrack"
                }
              },
              "chapters": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Chapter"
                }
              },
              "tags": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Tag"
                }
              },
              "transcript_id": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uuid"
              },
              "playback_state": {
                "$ref": "#/components/schemas/PlaybackState"
              }
            }
          }
        ]
      },
      "VideoUpdate": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 512
          },
          "description": {
            "type": "string",
            "maxLength": 8192
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 64
            }
          },
          "content_type": {
            "type": "string"
          }
        }
      },
      "MediaInfo": {
        "type": "object",
        "properties": {
          "container": {
            "type": "string"
          },
          "video_codec": {
            "type": "string"
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "fps": {
            "type": "number"
          },
          "bitrate_kbps": {
            "type": "integer"
          },
          "has_subtitles": {
            "type": "boolean"
          },
          "duration_sec": {
            "type": "number"
          }
        }
      },
      "AudioTrack": {
        "type": "object",
        "required": [
          "id",
          "index"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "index": {
            "type": "integer"
          },
          "codec": {
            "type": "string"
          },
          "channels": {
            "type": "integer"
          },
          "sample_rate": {
            "type": "integer"
          },
          "language": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "is_default": {
            "type": "boolean"
          }
        }
      },
      "Segment": {
        "type": "object",
        "required": [
          "id",
          "seq",
          "start_sec",
          "end_sec",
          "text"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "seq": {
            "type": "integer"
          },
          "start_sec": {
            "type": "number"
          },
          "end_sec": {
            "type": "number"
          },
          "text": {
            "type": "string",
            "description": "Bidi-isolated with U+2068 (FSI) and U+2069 (PDI)."
          },
          "speaker": {
            "type": [
              "string",
              "null"
            ]
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1
          },
          "words": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Word"
            }
          }
        }
      },
      "Word": {
        "type": "object",
        "required": [
          "seq",
          "start_sec",
          "end_sec",
          "text"
        ],
        "properties": {
          "seq": {
            "type": "integer"
          },
          "start_sec": {
            "type": "number"
          },
          "end_sec": {
            "type": "number"
          },
          "text": {
            "type": "string"
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          }
        }
      },
      "SegmentWindow": {
        "type": "object",
        "required": [
          "items",
          "partial"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Segment"
            }
          },
          "next": {
            "type": [
              "string",
              "null"
            ]
          },
          "partial": {
            "type": "boolean",
            "description": "`true` when the window starts past the last segment\\'s end."
          },
          "transcript_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        }
      },
      "Subtitle": {
        "type": "object",
        "required": [
          "id",
          "language",
          "format",
          "source",
          "url",
          "expires_at"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "language": {
            "type": "string",
            "description": "ISO 639-1."
          },
          "format": {
            "type": "string",
            "enum": [
              "vtt",
              "srt",
              "ass",
              "embedded"
            ]
          },
          "source": {
            "type": "string",
            "enum": [
              "auto",
              "external",
              "embedded"
            ]
          },
          "is_default": {
            "type": "boolean"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Signed URL on the Streaming origin."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Chapter": {
        "type": "object",
        "required": [
          "seq",
          "start_sec",
          "end_sec",
          "source"
        ],
        "properties": {
          "seq": {
            "type": "integer"
          },
          "start_sec": {
            "type": "number"
          },
          "end_sec": {
            "type": "number"
          },
          "title": {
            "type": "string"
          },
          "source": {
            "type": "string",
            "enum": [
              "embedded",
              "manual",
              "inferred"
            ]
          }
        }
      },
      "Tag": {
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "Speaker": {
        "type": "object",
        "required": [
          "id",
          "library_id"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "library_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "JobStage": {
        "type": "string",
        "enum": [
          "scan",
          "probe",
          "extract",
          "transcribe",
          "subtitle_gen",
          "index",
          "thumbnail"
        ]
      },
      "JobState": {
        "type": "string",
        "enum": [
          "pending",
          "claimed",
          "running",
          "resuming",
          "paused",
          "done",
          "failed",
          "cancelled"
        ]
      },
      "Job": {
        "type": "object",
        "required": [
          "id",
          "video_id",
          "stage",
          "state",
          "priority",
          "attempts",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "video_id": {
            "type": "string",
            "format": "uuid"
          },
          "stage": {
            "$ref": "#/components/schemas/JobStage"
          },
          "state": {
            "$ref": "#/components/schemas/JobState"
          },
          "priority": {
            "type": "integer"
          },
          "attempts": {
            "type": "integer"
          },
          "pause_requested": {
            "type": "boolean"
          },
          "cancel_requested": {
            "type": "boolean"
          },
          "last_segment_end_sec": {
            "type": "number"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          },
          "progress": {
            "$ref": "#/components/schemas/JobProgress"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "JobProgress": {
        "type": "object",
        "properties": {
          "pct": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "eta_sec": {
            "type": [
              "number",
              "null"
            ]
          },
          "rate": {
            "type": "number",
            "description": "Stage-specific units / sec."
          },
          "message": {
            "type": "string"
          }
        }
      },
      "QueueStats": {
        "type": "object",
        "required": [
          "by_stage",
          "total_pending",
          "oldest_job_age_sec"
        ],
        "properties": {
          "by_stage": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "total_pending": {
            "type": "integer"
          },
          "oldest_job_age_sec": {
            "type": "number"
          },
          "eta": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "description": "Per-stage ETA estimate in seconds."
          }
        }
      },
      "SearchRequest": {
        "type": "object",
        "required": [
          "q"
        ],
        "properties": {
          "q": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1024
          },
          "mode": {
            "type": "string",
            "enum": [
              "hybrid",
              "fts",
              "semantic"
            ],
            "default": "hybrid"
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50,
            "default": 20
          },
          "offset": {
            "type": "integer",
            "minimum": 0
          },
          "cursor": {
            "type": "string"
          },
          "k": {
            "type": "integer",
            "minimum": 1,
            "maximum": 200,
            "default": 50,
            "description": "Fusion candidate-pool size for hybrid mode."
          },
          "filters": {
            "$ref": "#/components/schemas/SearchFilters"
          }
        }
      },
      "SearchFilters": {
        "type": "object",
        "properties": {
          "language": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "library_id": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "speaker": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "tag": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "duration_sec": {
            "type": "object",
            "properties": {
              "gte": {
                "type": "number"
              },
              "lte": {
                "type": "number"
              }
            }
          },
          "date": {
            "type": "object",
            "properties": {
              "gte": {
                "type": "string",
                "format": "date-time"
              },
              "lte": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "SearchHit": {
        "type": "object",
        "required": [
          "video_id",
          "score",
          "matches",
          "snippet"
        ],
        "properties": {
          "video_id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "score": {
            "type": "number"
          },
          "snippet": {
            "type": "string"
          },
          "matches": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchMatch"
            }
          }
        }
      },
      "SearchMatch": {
        "type": "object",
        "required": [
          "segment_id",
          "start_sec",
          "end_sec",
          "text"
        ],
        "properties": {
          "segment_id": {
            "type": "integer",
            "format": "int64"
          },
          "start_sec": {
            "type": "number"
          },
          "end_sec": {
            "type": "number"
          },
          "text": {
            "type": "string"
          },
          "speaker": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "SearchResponse": {
        "type": "object",
        "required": [
          "hits",
          "total",
          "took_ms"
        ],
        "properties": {
          "hits": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchHit"
            }
          },
          "total": {
            "type": "integer"
          },
          "took_ms": {
            "type": "object",
            "required": [
              "total"
            ],
            "properties": {
              "fts": {
                "type": "integer"
              },
              "semantic": {
                "type": "integer"
              },
              "fusion": {
                "type": "integer"
              },
              "total": {
                "type": "integer"
              }
            }
          },
          "facets": {
            "$ref": "#/components/schemas/SearchFacets"
          },
          "degraded": {
            "type": "boolean",
            "description": "`true` when hybrid mode fell back to FTS only."
          },
          "reason": {
            "type": "string"
          },
          "next": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "SearchFacets": {
        "type": "object",
        "properties": {
          "language": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FacetBucket"
            }
          },
          "library": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FacetBucket"
            }
          },
          "tag": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FacetBucket"
            }
          },
          "speaker": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FacetBucket"
            }
          }
        }
      },
      "FacetBucket": {
        "type": "object",
        "required": [
          "value",
          "count"
        ],
        "properties": {
          "value": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          }
        }
      },
      "SavedSearch": {
        "type": "object",
        "required": [
          "id",
          "name",
          "query",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "query": {
            "$ref": "#/components/schemas/SearchRequest"
          },
          "kind": {
            "type": "string",
            "enum": [
              "user",
              "smart_collection"
            ],
            "default": "user"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SavedSearchInput": {
        "type": "object",
        "required": [
          "name",
          "query"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "query": {
            "$ref": "#/components/schemas/SearchRequest"
          }
        }
      },
      "Collection": {
        "type": "object",
        "required": [
          "id",
          "name",
          "is_smart",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_smart": {
            "type": "boolean"
          },
          "smart_query": {
            "$ref": "#/components/schemas/SearchRequest"
          },
          "item_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CollectionCreate": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "description": {
            "type": "string",
            "maxLength": 1024
          },
          "is_smart": {
            "type": "boolean",
            "default": false
          },
          "smart_query": {
            "$ref": "#/components/schemas/SearchRequest"
          }
        }
      },
      "CollectionUpdate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "description": {
            "type": "string",
            "maxLength": 1024
          },
          "smart_query": {
            "$ref": "#/components/schemas/SearchRequest"
          }
        }
      },
      "CollectionItem": {
        "type": "object",
        "required": [
          "collection_id",
          "video_id",
          "position"
        ],
        "properties": {
          "collection_id": {
            "type": "string",
            "format": "uuid"
          },
          "video_id": {
            "type": "string",
            "format": "uuid"
          },
          "position": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "PlaybackState": {
        "type": "object",
        "required": [
          "video_id",
          "position_sec",
          "completed",
          "updated_at"
        ],
        "properties": {
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "video_id": {
            "type": "string",
            "format": "uuid"
          },
          "position_sec": {
            "type": "number",
            "minimum": 0
          },
          "completed": {
            "type": "boolean"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "source_session_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        }
      },
      "PlaybackStateInput": {
        "type": "object",
        "required": [
          "position_sec"
        ],
        "properties": {
          "position_sec": {
            "type": "number",
            "minimum": 0
          },
          "completed": {
            "type": "boolean"
          }
        }
      },
      "SessionRequest": {
        "type": "object",
        "required": [
          "video_id"
        ],
        "properties": {
          "video_id": {
            "type": "string",
            "format": "uuid"
          },
          "client_profile": {
            "type": "string",
            "description": "Hint for the encoder ladder (e.g. `web`, `ios`, `tv-1080p`)."
          },
          "audio_track": {
            "type": "string",
            "description": "Language tag or stream id."
          },
          "subtitle_track": {
            "type": "string"
          },
          "start_sec": {
            "type": "number",
            "minimum": 0
          },
          "max_bitrate_kbps": {
            "type": "integer",
            "minimum": 100
          },
          "format": {
            "type": "string",
            "enum": [
              "hls",
              "dash",
              "direct"
            ]
          },
          "force_software": {
            "type": "boolean"
          },
          "force_transcode": {
            "type": "boolean"
          },
          "burn_subs": {
            "type": "boolean"
          },
          "accept_queue": {
            "type": "boolean"
          }
        }
      },
      "Session": {
        "type": "object",
        "required": [
          "session_id",
          "mode",
          "expires_at",
          "ladder",
          "current_rendition"
        ],
        "properties": {
          "session_id": {
            "type": "string",
            "format": "uuid"
          },
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "video_id": {
            "type": "string",
            "format": "uuid"
          },
          "library_id": {
            "type": "string",
            "format": "uuid"
          },
          "mode": {
            "type": "string",
            "enum": [
              "direct",
              "remux",
              "transcode"
            ]
          },
          "manifest_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "direct_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "ladder": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Rung"
            }
          },
          "current_rendition": {
            "type": "string"
          },
          "duration_sec": {
            "type": "number",
            "description": "The video's length in the catalogue (absent when unknown)."
          },
          "start_sec": {
            "type": "number",
            "description": "Where in the video the stream starts, in seconds (a transcode opened at `start_sec` starts there; its times are the video's). Absent at 0."
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Rung": {
        "type": "object",
        "required": [
          "name",
          "kbps_cap",
          "width",
          "height",
          "codec"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "kbps_cap": {
            "type": "integer"
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "codec": {
            "type": "string"
          }
        }
      },
      "StreamCapabilities": {
        "type": "object",
        "required": [
          "codecs",
          "hwaccel",
          "max_bitrate_kbps",
          "supported_containers"
        ],
        "properties": {
          "codecs": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "hwaccel": {
            "type": "string",
            "enum": [
              "videotoolbox",
              "vaapi",
              "nvenc",
              "qsv",
              "none"
            ]
          },
          "max_bitrate_kbps": {
            "type": "integer"
          },
          "supported_containers": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "transcode_slots": {
            "type": "object",
            "properties": {
              "used": {
                "type": "integer"
              },
              "capacity": {
                "type": "integer"
              }
            }
          }
        }
      },
      "Settings": {
        "type": "object",
        "description": "Effective settings (defaults ⊕ file ⊕ env ⊕ DB). Secrets redacted.",
        "additionalProperties": true,
        "properties": {
          "search": {
            "type": "object",
            "properties": {
              "fts_weight": {
                "type": "number"
              },
              "semantic_weight": {
                "type": "number"
              }
            }
          },
          "playback": {
            "type": "object",
            "properties": {
              "default_quality": {
                "type": "string"
              },
              "autoplay_next": {
                "type": "boolean"
              }
            }
          },
          "ui": {
            "type": "object",
            "properties": {
              "theme": {
                "type": "string",
                "enum": [
                  "light",
                  "dark",
                  "system"
                ]
              },
              "language": {
                "type": "string"
              }
            }
          }
        }
      },
      "SettingsInput": {
        "type": "object",
        "additionalProperties": true,
        "description": "Settings to persist to the DB layer."
      },
      "SttBackend": {
        "type": "object",
        "required": [
          "name",
          "available"
        ],
        "properties": {
          "name": {
            "type": "string",
            "examples": [
              "whisper-mlx"
            ]
          },
          "available": {
            "type": "boolean"
          },
          "models": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "cost_per_minute_usd": {
            "type": [
              "number",
              "null"
            ]
          },
          "notes": {
            "type": "string"
          }
        }
      },
      "Device": {
        "type": "object",
        "required": [
          "id",
          "platform",
          "bundle_id",
          "registered_at",
          "last_seen_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "platform": {
            "type": "string",
            "enum": [
              "ios",
              "android",
              "web"
            ]
          },
          "bundle_id": {
            "type": "string"
          },
          "push_token": {
            "type": [
              "string",
              "null"
            ],
            "description": "Redacted in list / get responses; only echoed on register."
          },
          "app_version": {
            "type": "string"
          },
          "locale": {
            "type": "string"
          },
          "registered_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_seen_at": {
            "type": "string",
            "format": "date-time"
          },
          "revoked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "DeviceRegisterInput": {
        "type": "object",
        "required": [
          "platform",
          "push_token",
          "bundle_id"
        ],
        "properties": {
          "platform": {
            "type": "string",
            "enum": [
              "ios",
              "android",
              "web"
            ]
          },
          "push_token": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4096
          },
          "bundle_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256
          },
          "app_version": {
            "type": "string",
            "maxLength": 64
          },
          "locale": {
            "type": "string",
            "maxLength": 16
          }
        }
      },
      "Recommendation": {
        "type": "object",
        "required": [
          "video_id",
          "score",
          "reason"
        ],
        "properties": {
          "video_id": {
            "type": "string",
            "format": "uuid"
          },
          "score": {
            "type": "number"
          },
          "reason": {
            "type": "string",
            "examples": [
              "Because you watched X",
              "Recently added to Lectures"
            ]
          },
          "video": {
            "$ref": "#/components/schemas/VideoSummary"
          }
        }
      },
      "RecommendationsResponse": {
        "type": "object",
        "required": [
          "shelves"
        ],
        "properties": {
          "shelves": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "title",
                "items"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "examples": [
                    "continue_watching"
                  ]
                },
                "title": {
                  "type": "string"
                },
                "items": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Recommendation"
                  }
                }
              }
            }
          }
        }
      },
      "Health": {
        "type": "object",
        "required": [
          "status",
          "components",
          "checked_at"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "degraded",
              "down"
            ]
          },
          "components": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "required": [
                "status"
              ],
              "properties": {
                "status": {
                  "type": "string",
                  "enum": [
                    "ok",
                    "degraded",
                    "down"
                  ]
                },
                "detail": {
                  "type": "string"
                },
                "latency_ms": {
                  "type": "number"
                }
              }
            }
          },
          "checked_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Version": {
        "type": "object",
        "required": [
          "version",
          "build_sha",
          "build_time",
          "go_version",
          "schema_revision"
        ],
        "properties": {
          "version": {
            "type": "string",
            "examples": [
              "0.1.0-rc.3"
            ]
          },
          "build_sha": {
            "type": "string"
          },
          "build_time": {
            "type": "string",
            "format": "date-time"
          },
          "go_version": {
            "type": "string"
          },
          "schema_revision": {
            "type": "integer"
          }
        }
      },
      "WsMessage": {
        "type": "object",
        "description": "Envelope for every server-to-client WebSocket message.\n`type` follows the form `<channel>.<event>` (e.g. `jobs.progress`,\n`playback.completed`, `library.updated`).\n",
        "required": [
          "type",
          "data",
          "at"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "subscribe",
              "unsubscribe",
              "jobs.new",
              "jobs.flag_set",
              "jobs.progress",
              "jobs.state_changed",
              "jobs.done",
              "playback.progress",
              "playback.completed",
              "library.updated",
              "videos.state_changed",
              "videos.new",
              "profiles_changed",
              "health_changed",
              "error"
            ]
          },
          "data": {
            "description": "Event-specific payload. See per-event schemas below.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/WsSubscribe"
              },
              {
                "$ref": "#/components/schemas/WsJobEvent"
              },
              {
                "$ref": "#/components/schemas/WsPlaybackEvent"
              },
              {
                "$ref": "#/components/schemas/WsLibraryEvent"
              },
              {
                "$ref": "#/components/schemas/WsVideoEvent"
              },
              {
                "$ref": "#/components/schemas/WsErrorEvent"
              }
            ]
          },
          "at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WsSubscribe": {
        "type": "object",
        "properties": {
          "channels": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "jobs",
                "playback",
                "library",
                "system"
              ]
            }
          }
        }
      },
      "WsJobEvent": {
        "type": "object",
        "required": [
          "job_id"
        ],
        "properties": {
          "job_id": {
            "type": "integer",
            "format": "int64"
          },
          "video_id": {
            "type": "string",
            "format": "uuid"
          },
          "stage": {
            "$ref": "#/components/schemas/JobStage"
          },
          "state": {
            "$ref": "#/components/schemas/JobState"
          },
          "progress": {
            "$ref": "#/components/schemas/JobProgress"
          },
          "error": {
            "type": "string"
          }
        }
      },
      "WsPlaybackEvent": {
        "type": "object",
        "required": [
          "video_id",
          "position_sec"
        ],
        "properties": {
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "video_id": {
            "type": "string",
            "format": "uuid"
          },
          "session_id": {
            "type": "string",
            "format": "uuid"
          },
          "position_sec": {
            "type": "number"
          },
          "completed": {
            "type": "boolean"
          }
        }
      },
      "WsLibraryEvent": {
        "type": "object",
        "required": [
          "library_id"
        ],
        "properties": {
          "library_id": {
            "type": "string",
            "format": "uuid"
          },
          "change": {
            "type": "string",
            "enum": [
              "created",
              "updated",
              "deleted",
              "scan_started",
              "scan_finished"
            ]
          },
          "stats": {
            "$ref": "#/components/schemas/LibraryStats"
          }
        }
      },
      "WsVideoEvent": {
        "type": "object",
        "required": [
          "video_id",
          "state"
        ],
        "properties": {
          "video_id": {
            "type": "string",
            "format": "uuid"
          },
          "library_id": {
            "type": "string",
            "format": "uuid"
          },
          "state": {
            "$ref": "#/components/schemas/VideoState"
          }
        }
      },
      "WsErrorEvent": {
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "QueueJobRef": {
        "type": "object",
        "description": "A job as shown in queue views (REST and SSE).",
        "required": [
          "id",
          "stage",
          "state",
          "subject_kind",
          "subject_id",
          "subject_label",
          "library_id",
          "priority",
          "attempts",
          "max_attempts",
          "outcome",
          "error_cause",
          "created_at",
          "started_at",
          "finished_at",
          "progress"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "stage": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "subject_kind": {
            "type": "string"
          },
          "subject_id": {
            "type": "string"
          },
          "subject_label": {
            "type": "string"
          },
          "library_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "priority": {
            "type": "integer"
          },
          "attempts": {
            "type": "integer"
          },
          "max_attempts": {
            "type": "integer"
          },
          "outcome": {
            "type": [
              "string",
              "null"
            ]
          },
          "error_cause": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "oneOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          },
          "finished_at": {
            "oneOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          },
          "progress": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          }
        }
      },
      "QueueEdge": {
        "type": "object",
        "description": "One edge of the stage graph.",
        "required": [
          "to",
          "map",
          "when",
          "legacy"
        ],
        "properties": {
          "to": {
            "type": "string"
          },
          "map": {
            "type": "string"
          },
          "when": {
            "type": "string"
          },
          "legacy": {
            "type": "boolean"
          }
        }
      },
      "QueueStageInfo": {
        "type": "object",
        "description": "A row of job_stages plus computed fed_by.",
        "required": [
          "stage",
          "label",
          "lane",
          "lane_order",
          "subject_kinds",
          "pool",
          "version",
          "legacy",
          "hidden",
          "external_write",
          "write_mode",
          "enabled",
          "disabled_reason",
          "paused",
          "paused_at",
          "paused_by",
          "debounce_sec",
          "edges",
          "may_request",
          "entry_for",
          "fed_by",
          "extension",
          "graph_synced_at"
        ],
        "properties": {
          "stage": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "lane": {
            "type": "string"
          },
          "lane_order": {
            "type": "integer"
          },
          "subject_kinds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "pool": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "legacy": {
            "type": "boolean"
          },
          "hidden": {
            "type": "boolean"
          },
          "external_write": {
            "type": "boolean"
          },
          "write_mode": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "disabled_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "paused": {
            "type": "boolean"
          },
          "paused_at": {
            "oneOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          },
          "paused_by": {
            "type": [
              "string",
              "null"
            ]
          },
          "debounce_sec": {
            "type": "integer"
          },
          "edges": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QueueEdge"
            }
          },
          "may_request": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "entry_for": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "fed_by": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "extension": {
            "type": [
              "string",
              "null"
            ]
          },
          "graph_synced_at": {
            "oneOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "WorkerPool": {
        "type": "object",
        "description": "A worker pool with live usage.",
        "required": [
          "pool",
          "label",
          "scope",
          "size",
          "effective_size",
          "busy",
          "stages",
          "updated_at",
          "updated_by"
        ],
        "properties": {
          "pool": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "scope": {
            "type": "string"
          },
          "size": {
            "type": [
              "integer",
              "null"
            ]
          },
          "effective_size": {
            "type": "integer"
          },
          "busy": {
            "type": "integer"
          },
          "stages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_by": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "QueueCounts": {
        "type": "object",
        "description": "Per-stage counters.",
        "required": [
          "pending",
          "ready",
          "running",
          "paused",
          "failed_open",
          "done_24h",
          "skipped_24h",
          "awaiting_review"
        ],
        "properties": {
          "pending": {
            "type": "integer"
          },
          "ready": {
            "type": "integer"
          },
          "running": {
            "type": "integer"
          },
          "paused": {
            "type": "integer"
          },
          "failed_open": {
            "type": "integer"
          },
          "done_24h": {
            "type": "integer"
          },
          "skipped_24h": {
            "type": "integer"
          },
          "awaiting_review": {
            "type": "integer"
          }
        }
      },
      "QueueSummary": {
        "type": "object",
        "description": "Per-stage card data (gauge, rate, ETA, waiting reason, worker coverage).",
        "required": [
          "stage",
          "counts",
          "wave",
          "rate_per_min",
          "eta_sec",
          "oldest_waiting_sec",
          "waiting_reason",
          "running_jobs",
          "served_by",
          "served_workers",
          "no_worker",
          "extension"
        ],
        "properties": {
          "stage": {
            "type": "string"
          },
          "counts": {
            "$ref": "#/components/schemas/QueueCounts"
          },
          "wave": {
            "type": "object",
            "additionalProperties": true
          },
          "rate_per_min": {
            "type": "number"
          },
          "eta_sec": {
            "type": [
              "number",
              "null"
            ]
          },
          "oldest_waiting_sec": {
            "type": [
              "number",
              "null"
            ]
          },
          "waiting_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "running_jobs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QueueJobRef"
            }
          },
          "served_by": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "served_workers": {
            "type": "integer"
          },
          "no_worker": {
            "type": "boolean"
          },
          "extension": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "QueueFlow": {
        "type": "object",
        "description": "Trigger traffic between two stages.",
        "required": [
          "from",
          "to",
          "per_min"
        ],
        "properties": {
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          },
          "per_min": {
            "type": "number"
          }
        }
      },
      "QueueHealth": {
        "type": "object",
        "description": "Health strip.",
        "required": [
          "nas",
          "cpu",
          "network",
          "backlog",
          "spark"
        ],
        "properties": {
          "nas": {
            "type": "object",
            "additionalProperties": true
          },
          "cpu": {
            "type": "object",
            "additionalProperties": true
          },
          "network": {
            "type": "object",
            "additionalProperties": true
          },
          "backlog": {
            "type": "object",
            "additionalProperties": true
          },
          "spark": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "QueuesSnapshot": {
        "type": "object",
        "description": "Everything the overview needs; also the queues.snapshot SSE payload.",
        "required": [
          "at",
          "paused_all",
          "stages",
          "queues",
          "pools",
          "flows",
          "health",
          "issues_count"
        ],
        "properties": {
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "paused_all": {
            "type": "boolean"
          },
          "stages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QueueStageInfo"
            }
          },
          "queues": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QueueSummary"
            }
          },
          "pools": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkerPool"
            }
          },
          "flows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QueueFlow"
            }
          },
          "health": {
            "$ref": "#/components/schemas/QueueHealth"
          },
          "issues_count": {
            "type": "integer"
          }
        }
      },
      "QueueRatePoint": {
        "type": "object",
        "description": "One 10-minute bucket.",
        "required": [
          "t",
          "done",
          "failed",
          "skipped",
          "nas_timeouts"
        ],
        "properties": {
          "t": {
            "type": "string",
            "format": "date-time"
          },
          "done": {
            "type": "integer"
          },
          "failed": {
            "type": "integer"
          },
          "skipped": {
            "type": "integer"
          },
          "nas_timeouts": {
            "type": "integer"
          }
        }
      },
      "QueueDetail": {
        "type": "object",
        "description": "One queue page.",
        "required": [
          "info",
          "summary",
          "kpis",
          "running",
          "next_up",
          "failures",
          "log"
        ],
        "properties": {
          "info": {
            "$ref": "#/components/schemas/QueueStageInfo"
          },
          "summary": {
            "$ref": "#/components/schemas/QueueSummary"
          },
          "kpis": {
            "type": "object",
            "additionalProperties": true
          },
          "running": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QueueJobRef"
            }
          },
          "next_up": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QueueJobRef"
            }
          },
          "failures": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FailureGroup"
            }
          },
          "log": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QueueLogLine"
            }
          }
        }
      },
      "FailureGroup": {
        "type": "object",
        "description": "Open failures of a stage grouped by normalised cause (plus awaiting_review / stale_graph).",
        "required": [
          "stage",
          "cause",
          "label",
          "count",
          "first_at",
          "last_at",
          "sample_message",
          "sample_subjects",
          "action"
        ],
        "properties": {
          "stage": {
            "type": "string"
          },
          "cause": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "first_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_at": {
            "type": "string",
            "format": "date-time"
          },
          "sample_message": {
            "type": "string"
          },
          "sample_subjects": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "action": {
            "type": "string"
          }
        }
      },
      "QueueLogLine": {
        "type": "object",
        "description": "One job_events row.",
        "required": [
          "id",
          "at",
          "job_id",
          "stage",
          "subject_kind",
          "subject_id",
          "subject_label",
          "level",
          "kind",
          "message",
          "data"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "job_id": {
            "type": "integer"
          },
          "stage": {
            "type": "string"
          },
          "subject_kind": {
            "type": "string"
          },
          "subject_id": {
            "type": "string"
          },
          "subject_label": {
            "type": "string"
          },
          "level": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "data": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          }
        }
      },
      "RetriggerRequest": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "maxItems": 5000,
            "items": {
              "type": "object",
              "required": [
                "subject_kind",
                "subject_id"
              ],
              "properties": {
                "subject_kind": {
                  "type": "string",
                  "enum": [
                    "video",
                    "library",
                    "series",
                    "season",
                    "catalog"
                  ]
                },
                "subject_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "stage": {
                  "type": "string"
                }
              }
            }
          },
          "mode": {
            "type": "string",
            "enum": [
              "queue",
              "force",
              "force_cascade"
            ],
            "default": "queue"
          },
          "priority": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1000,
            "default": 50
          }
        }
      },
      "RetriggerResponse": {
        "type": "object",
        "required": [
          "results"
        ],
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "subject_kind": {
                  "type": "string"
                },
                "subject_id": {
                  "type": "string"
                },
                "stage": {
                  "type": "string"
                },
                "job_id": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "outcome": {
                  "type": "string",
                  "enum": [
                    "inserted",
                    "coalesced",
                    "rerun_requested",
                    "disabled",
                    "unknown_stage",
                    "invalid_subject"
                  ]
                }
              }
            }
          }
        }
      },
      "QueueItemHistory": {
        "type": "object",
        "description": "History and re-queue options of one subject.",
        "required": [
          "subject",
          "kinds",
          "queues",
          "appears_in",
          "timeline",
          "next"
        ],
        "properties": {
          "subject": {
            "type": "object",
            "additionalProperties": true
          },
          "kinds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "queues": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "appears_in": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "timeline": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "next": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "LibraryQueueRow": {
        "type": "object",
        "description": "One row of the libraries table.",
        "required": [
          "library_id",
          "name",
          "videos",
          "seasons",
          "strip",
          "last_activity_at"
        ],
        "properties": {
          "library_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "videos": {
            "type": "integer"
          },
          "seasons": {
            "type": "integer"
          },
          "strip": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "last_activity_at": {
            "oneOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "HealthSeries": {
        "type": "object",
        "description": "One health metric over time (nas.open_ms_p95, cpu.pct, net.plex_ms, backlog.ready, ...).",
        "required": [
          "metric",
          "points"
        ],
        "properties": {
          "metric": {
            "type": "string"
          },
          "points": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "QueueWorker": {
        "type": "object",
        "description": "A registered worker.",
        "required": [
          "worker_id",
          "host",
          "online",
          "last_seen_at",
          "serves",
          "serves_label",
          "pools",
          "started_at",
          "version"
        ],
        "properties": {
          "worker_id": {
            "type": "string"
          },
          "host": {
            "type": "string"
          },
          "online": {
            "type": "boolean"
          },
          "last_seen_at": {
            "type": "string",
            "format": "date-time"
          },
          "serves": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "serves_label": {
            "type": "string"
          },
          "pools": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "version": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ExtensionInfo": {
        "type": "object",
        "description": "An optional extension (Plex, dizqueTV). Secret settings are never returned.",
        "required": [
          "name",
          "label",
          "description",
          "version",
          "enabled",
          "configured",
          "settings_schema",
          "settings",
          "secrets_set"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "configured": {
            "type": "boolean"
          },
          "settings_schema": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "settings": {
            "type": "object",
            "additionalProperties": true
          },
          "secrets_set": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      }
    },
    "requestBodies": {
      "WsClientMessage": {
        "description": "JSON envelope sent from client to server over the WebSocket. Use `subscribe` / `unsubscribe`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/WsMessage"
            }
          }
        }
      }
    }
  }
}