{
  "openapi": "3.1.0",
  "info": {
    "title": "ScriptHaul API",
    "version": "1.0.0",
    "description": "Cache-first transcript delivery for a video, playlist, or channel. Cache hits and failures cost zero credits. Search, video download, and audio download are not offered.",
    "termsOfService": "https://api.scripthaul.com/terms",
    "contact": {
      "email": "support@scripthaul.com"
    }
  },
  "servers": [
    {
      "url": "https://api.scripthaul.com"
    }
  ],
  "tags": [
    {
      "name": "Authentication"
    },
    {
      "name": "Keys"
    },
    {
      "name": "Transcripts"
    },
    {
      "name": "Videos"
    },
    {
      "name": "Account"
    },
    {
      "name": "Billing"
    },
    {
      "name": "Bulk jobs"
    },
    {
      "name": "Webhooks"
    },
    {
      "name": "Status"
    }
  ],
  "paths": {
    "/v1/auth/code": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "operationId": "requestAuthCode",
        "summary": "Send a six-digit verification code",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AuthCodeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Code accepted for delivery",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/auth/logout": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "operationId": "logoutSession",
        "summary": "End the dashboard session",
        "description": "Deletes the session behind the sh_dashboard cookie and clears the cookie. Like every cookie-authenticated mutation it requires the X-ScriptHaul-Dashboard: 1 header. API keys have no session to end.",
        "responses": {
          "204": {
            "description": "Signed out",
            "headers": {
              "Set-Cookie": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/auth/verify": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "operationId": "verifyAuthCode",
        "summary": "Verify a code and return a session and first key",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AuthVerifyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verified",
            "headers": {
              "Set-Cookie": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthVerifyResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/keys": {
      "get": {
        "tags": [
          "Keys"
        ],
        "operationId": "listKeys",
        "summary": "List account keys without secret values",
        "responses": {
          "200": {
            "description": "Key list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "tags": [
          "Keys"
        ],
        "operationId": "createKey",
        "summary": "Create and show a key once",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KeyCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyCreated"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/keys/{keyId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/KeyId"
        }
      ],
      "post": {
        "tags": [
          "Keys"
        ],
        "operationId": "updateKey",
        "summary": "Rename a key or set its daily credit cap",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KeyCreate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "tags": [
          "Keys"
        ],
        "operationId": "revokeKey",
        "summary": "Revoke immediately or with a 24-hour grace period",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "grace_period_hours": {
                    "type": "integer",
                    "enum": [
                      0,
                      24
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Revoked or scheduled"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/keys/{keyId}/rotate": {
      "parameters": [
        {
          "$ref": "#/components/parameters/KeyId"
        }
      ],
      "post": {
        "tags": [
          "Keys"
        ],
        "operationId": "rotateKey",
        "summary": "Create a replacement and give the old key 24 hours of grace",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KeyCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Replacement key shown once",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyCreated"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/transcript": {
      "get": {
        "tags": [
          "Transcripts"
        ],
        "operationId": "getTranscript",
        "summary": "Get one cache-first transcript",
        "description": "Use exactly one query mode: video_id starts or reads a transcript; request_id polls an earlier 202 response. The two parameters are mutually exclusive.",
        "x-scripthaul-query-modes": {
          "oneOf": [
            {
              "required": [
                "video_id"
              ],
              "forbidden": [
                "request_id"
              ]
            },
            {
              "required": [
                "request_id"
              ],
              "forbidden": [
                "video_id"
              ]
            }
          ]
        },
        "parameters": [
          {
            "name": "video_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 11,
              "maxLength": 11
            },
            "description": "An 11-character YouTube video ID. Required only when request_id is absent."
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "clean",
                "timestamped",
                "both",
                "srt",
                "vtt",
                "md"
              ],
              "default": "both"
            }
          },
          {
            "name": "language",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "en"
            }
          },
          {
            "name": "fallback",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "raw",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "request_id",
            "in": "query",
            "required": false,
            "description": "Opaque polling ID returned by a prior 202. Required only when video_id is absent; never an API credential.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Transcript",
            "headers": {
              "X-Cache": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "HIT",
                    "MISS"
                  ]
                }
              },
              "X-Credits-Charged": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-Credits-Balance": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-Request-Id": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transcript"
                }
              },
              "text/plain": {},
              "application/x-subrip": {},
              "text/vtt": {},
              "text/markdown": {}
            }
          },
          "202": {
            "description": "Still fetching; poll the returned URL",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Pending"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "402": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/video": {
      "get": {
        "tags": [
          "Videos"
        ],
        "operationId": "getVideoFacts",
        "summary": "Get already-known video and cache facts without an upstream fetch",
        "parameters": [
          {
            "name": "video_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 11,
              "maxLength": 11
            },
            "description": "An 11-character YouTube video ID. Required when neither input nor url is sent."
          },
          {
            "name": "input",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "A public YouTube video URL or bare ID; alternative to video_id."
          },
          {
            "name": "url",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "A public YouTube video URL; alternative to video_id."
          }
        ],
        "responses": {
          "200": {
            "description": "Known facts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoFacts"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        },
        "description": "Returns only facts ScriptHaul already holds; it never triggers a transcript fetch or a YouTube Data API call. Identify the video with exactly one of video_id, input, or url.",
        "x-scripthaul-query-modes": {
          "oneOf": [
            {
              "required": [
                "video_id"
              ],
              "forbidden": [
                "input",
                "url"
              ]
            },
            {
              "required": [
                "input"
              ],
              "forbidden": [
                "video_id",
                "url"
              ]
            },
            {
              "required": [
                "url"
              ],
              "forbidden": [
                "video_id",
                "input"
              ]
            }
          ]
        }
      }
    },
    "/v1/videos": {
      "get": {
        "tags": [
          "Videos"
        ],
        "operationId": "listVideos",
        "summary": "List up to 500 channel, playlist, or resolved-list videos",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Channel URL, playlist URL, or list: key. Required when input is absent."
          },
          {
            "name": "input",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Alias for url; send one of the two."
          },
          {
            "name": "contents",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "videos",
                "shorts"
              ],
              "default": "all"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "0 or a multiple of 500, at most 20,000; pass the next_offset from the previous page. Other values return 400 InvalidInput with code invalid_offset.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 20000,
              "multipleOf": 500,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Inventory snapshot"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        },
        "description": "Lists a channel or playlist snapshot, or a resolved list: key. Identify the source with exactly one of url or input.",
        "x-scripthaul-query-modes": {
          "oneOf": [
            {
              "required": [
                "url"
              ],
              "forbidden": [
                "input"
              ]
            },
            {
              "required": [
                "input"
              ],
              "forbidden": [
                "url"
              ]
            }
          ]
        }
      }
    },
    "/v1/videos/resolve": {
      "post": {
        "tags": [
          "Videos"
        ],
        "operationId": "resolveVideos",
        "summary": "Extract and validate up to 500 video links from text",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stable list key with found and notFound rows"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/account": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getAccount",
        "summary": "Get buckets, tier, limits, today's usage, and key facts",
        "responses": {
          "200": {
            "description": "Account summary"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "put": {
        "tags": [
          "Account"
        ],
        "operationId": "putAccountResource",
        "summary": "Dashboard alias: create or replace the account webhook with the session cookie",
        "description": "Same behaviour as PUT /v1/webhooks, reachable with the dashboard session cookie plus the X-ScriptHaul-Dashboard: 1 header. The resource parameter selects the operation; the only value today is webhook.",
        "parameters": [
          {
            "$ref": "#/components/parameters/AccountResource"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookConfig"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook configured; webhook_secret is present only when a new secret was issued"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "tags": [
          "Account"
        ],
        "operationId": "deleteAccountResource",
        "summary": "Dashboard alias: delete the account webhook, or close the account, with the session cookie",
        "description": "Same behaviour as DELETE /v1/webhooks, reachable with the dashboard session cookie plus the X-ScriptHaul-Dashboard: 1 header. The resource parameter selects the operation; the only value today is webhook.",
        "parameters": [
          {
            "$ref": "#/components/parameters/AccountDeleteResource"
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook deleted; durable delivery snapshots remain"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/usage": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getUsage",
        "summary": "Get account or owned-key daily rollups",
        "parameters": [
          {
            "name": "resource",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ledger"
              ]
            },
            "description": "Select the credit ledger instead of usage rollups."
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "key_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "csv"
              ],
              "default": "json"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 100
            },
            "description": "Ledger rows per page (resource=ledger only)."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            },
            "description": "Ledger paging offset (resource=ledger only)."
          }
        ],
        "responses": {
          "200": {
            "description": "JSON rollups or CSV attachment"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        },
        "description": "Without resource, daily usage rollups for the account or one owned key. With resource=ledger, the append-only credit ledger (newest first) paged with limit and offset; the CSV format applies to rollups only."
      }
    },
    "/v1/checkout": {
      "post": {
        "tags": [
          "Billing"
        ],
        "operationId": "createCheckoutOrRefund",
        "summary": "Create a one-time Stripe Checkout Session or request an untouched-block refund",
        "security": [
          {
            "bearerAuth": []
          },
          {}
        ],
        "parameters": [
          {
            "name": "action",
            "in": "query",
            "required": false,
            "description": "Set to refund for an authenticated untouched-block refund request; omit for Checkout.",
            "schema": {
              "type": "string",
              "enum": [
                "refund"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "required": [
                      "block"
                    ],
                    "properties": {
                      "block": {
                        "type": "integer",
                        "enum": [
                          5,
                          20,
                          50,
                          200,
                          1000
                        ]
                      },
                      "email": {
                        "type": "string",
                        "format": "email"
                      }
                    },
                    "additionalProperties": false
                  },
                  {
                    "type": "object",
                    "required": [
                      "purchase_id"
                    ],
                    "properties": {
                      "purchase_id": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "reason": {
                        "type": "string"
                      }
                    },
                    "additionalProperties": false
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Checkout URL"
          },
          "202": {
            "description": "Refund request accepted and submitted with an idempotent purchase-scoped identity"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "description": "purchase_in_progress: an open Checkout Session for this account has not expired yet (retry_after gives the remaining seconds); or stripe_refund_conflict on a refund request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "new_account_purchase_limit: one purchase until 100 credits have been used; or the per-address checkout throttle",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/billing/stripe": {
      "post": {
        "tags": [
          "Billing"
        ],
        "operationId": "stripeWebhook",
        "summary": "Receive signed Stripe events",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Processed or already processed"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/billing/portal": {
      "get": {
        "tags": [
          "Billing"
        ],
        "operationId": "getBillingPortal",
        "summary": "Get a Stripe customer portal URL",
        "responses": {
          "200": {
            "description": "Portal URL"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "getWebhook",
        "summary": "Get the account webhook configuration without its signing secret",
        "responses": {
          "200": {
            "description": "Webhook configuration"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "put": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "putWebhook",
        "summary": "Create or replace the account webhook, optionally rotating its signing secret",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookConfig"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook configured"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        },
        "description": "The signing secret is returned once: when the webhook is first configured and whenever rotate_secret is true. Rotation does not re-sign deliveries already enqueued; they keep the secret snapshot they were created with."
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "deleteWebhook",
        "summary": "Delete the account webhook configuration",
        "responses": {
          "200": {
            "description": "Webhook deleted; durable delivery snapshots remain"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/jobs": {
      "get": {
        "tags": [
          "Bulk jobs"
        ],
        "operationId": "listJobs",
        "summary": "List account-owned API jobs",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "running",
                "completed",
                "failed",
                "cancelled",
                "creating",
                "enumerating",
                "paused"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paged jobs"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "tags": [
          "Bulk jobs"
        ],
        "operationId": "createJob",
        "summary": "Create an idempotent bulk transcript job of up to 500 videos",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JobCreate"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job accepted, with cold credits reserved when known"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "402": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/jobs/{jobId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobId"
        }
      ],
      "get": {
        "tags": [
          "Bulk jobs"
        ],
        "operationId": "getJob",
        "summary": "Get job progress, exact credits, webhook delivery state, and video rows",
        "responses": {
          "200": {
            "description": "Owned job"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/jobs/{jobId}/videos": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobId"
        }
      ],
      "get": {
        "tags": [
          "Bulk jobs"
        ],
        "operationId": "listJobVideos",
        "summary": "Page and filter per-video outcomes",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "processing",
                "cached",
                "fetched",
                "cancelled",
                "no_captions",
                "unavailable",
                "error",
                "paused_budget",
                "paused_outage"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paged video outcomes"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/jobs/{jobId}/manifest": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobId"
        }
      ],
      "get": {
        "tags": [
          "Bulk jobs"
        ],
        "operationId": "getJobManifest",
        "summary": "Download a complete JSON or CSV job manifest",
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "csv"
              ],
              "default": "json"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Manifest",
            "content": {
              "application/json": {},
              "text/csv": {}
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/jobs/{jobId}/archive": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobId"
        }
      ],
      "get": {
        "tags": [
          "Bulk jobs"
        ],
        "operationId": "getJobArchive",
        "summary": "Queue or download a zero-credit server-side ZIP archive",
        "description": "Available only to a Bearer API key after the owned job is terminal. With no format parameter, the job's single format is used. Multi-format requests are split into ZIP parts before any part can exceed 80 MiB; the index response identifies numbered parts. Archives expire after seven days.",
        "x-scripthaul-query-modes": {
          "mutuallyExclusive": [
            "format",
            "formats"
          ]
        },
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "description": "One format, a comma-separated list, or a repeated query parameter.",
            "schema": {
              "type": "array",
              "uniqueItems": true,
              "items": {
                "type": "string",
                "enum": [
                  "json",
                  "clean",
                  "timestamped",
                  "both",
                  "srt",
                  "vtt",
                  "md"
                ]
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "name": "formats",
            "in": "query",
            "description": "Alias for format; do not send both names.",
            "schema": {
              "type": "array",
              "uniqueItems": true,
              "items": {
                "type": "string",
                "enum": [
                  "json",
                  "clean",
                  "timestamped",
                  "both",
                  "srt",
                  "vtt",
                  "md"
                ]
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "name": "part",
            "in": "query",
            "description": "Download one numbered ZIP part after a split build.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10000
            }
          },
          {
            "name": "rebuild",
            "in": "query",
            "description": "Send 1 to start a fresh generation of a failed archive at once instead of waiting for its cool-down.",
            "schema": {
              "type": "integer",
              "enum": [
                1
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A single ZIP, one requested ZIP part, or a JSON index for a split archive",
            "content": {
              "application/zip": {},
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArchiveState"
                }
              }
            }
          },
          "202": {
            "description": "Archive generation is queued or building",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArchiveState"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "description": "archive_job_not_ready (retryable: the job is not terminal yet) or archive_failed (the build exhausted its bounded attempts; Retry-After is the cool-down and rebuild_url starts a new generation now)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/jobs/{jobId}/files/{videoId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobId"
        },
        {
          "$ref": "#/components/parameters/JobVideoId"
        }
      ],
      "get": {
        "tags": [
          "Bulk jobs"
        ],
        "operationId": "getJobFile",
        "summary": "Download one completed transcript in a supported format",
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "clean",
                "timestamped",
                "both",
                "srt",
                "vtt",
                "md"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Transcript attachment"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/jobs/{jobId}/events": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobId"
        }
      ],
      "get": {
        "tags": [
          "Bulk jobs"
        ],
        "operationId": "getJobEvents",
        "summary": "Read a reconnectable SSE progress snapshot",
        "responses": {
          "200": {
            "description": "text/event-stream progress event",
            "content": {
              "text/event-stream": {}
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/jobs/{jobId}/retry": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobId"
        }
      ],
      "post": {
        "tags": [
          "Bulk jobs"
        ],
        "operationId": "retryJob",
        "summary": "Reserve and retry eligible zero-cost transient failures",
        "responses": {
          "200": {
            "description": "Retry sweep result"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "402": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/jobs/{jobId}/cancel": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobId"
        }
      ],
      "post": {
        "tags": [
          "Bulk jobs"
        ],
        "operationId": "cancelJob",
        "summary": "Cancel queued work and release every unsettled reservation once",
        "responses": {
          "200": {
            "description": "Cancelled job"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/status": {
      "get": {
        "tags": [
          "Status"
        ],
        "operationId": "getStatus",
        "summary": "Get public service and per-strategy health",
        "security": [],
        "responses": {
          "200": {
            "description": "Current health and privacy-safe rolling latency without credentials or internal addresses",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Status"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sh_live_…",
        "description": "Send only in the Authorization header."
      }
    },
    "parameters": {
      "KeyId": {
        "name": "keyId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "JobId": {
        "name": "jobId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[a-f0-9]{32}$"
        }
      },
      "JobVideoId": {
        "name": "videoId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "minLength": 11,
          "maxLength": 11
        }
      },
      "VideoId": {
        "name": "video_id",
        "in": "query",
        "required": true,
        "schema": {
          "type": "string",
          "minLength": 11,
          "maxLength": 11
        },
        "description": "An 11-character YouTube video ID."
      },
      "AccountResource": {
        "name": "resource",
        "in": "query",
        "required": true,
        "schema": {
          "type": "string",
          "enum": [
            "webhook"
          ]
        },
        "description": "Selects the account sub-resource the dashboard is editing."
      },
      "AccountDeleteResource": {
        "name": "resource",
        "in": "query",
        "required": true,
        "schema": {
          "type": "string",
          "enum": [
            "webhook",
            "account"
          ]
        },
        "description": "`webhook` deletes the default endpoint; `account` closes the account: keys revoked and sessions ended immediately, personal data anonymized 30 days later; refused with 409 while jobs are active, a refund is pending, or refund debt is outstanding."
      }
    },
    "responses": {
      "Error": {
        "description": "Typed API error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Ok": {
        "type": "object",
        "required": [
          "ok"
        ],
        "properties": {
          "ok": {
            "const": true
          }
        }
      },
      "AuthCodeRequest": {
        "type": "object",
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          }
        }
      },
      "AuthVerifyRequest": {
        "type": "object",
        "required": [
          "email",
          "code",
          "accept_terms"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "code": {
            "type": "string",
            "pattern": "^[0-9]{6}$"
          },
          "accept_terms": {
            "const": true
          }
        }
      },
      "AuthVerifyResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Ok"
          },
          {
            "type": "object",
            "properties": {
              "api_key": {
                "type": "string",
                "writeOnly": true
              },
              "session_expires_in": {
                "type": "integer"
              }
            }
          }
        ]
      },
      "KeyCreate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 60
          },
          "daily_credit_cap": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          }
        }
      },
      "KeyCreated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Ok"
          },
          {
            "type": "object",
            "properties": {
              "api_key": {
                "type": "string",
                "writeOnly": true
              }
            }
          }
        ]
      },
      "KeyList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Ok"
          },
          {
            "type": "object",
            "properties": {
              "keys": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "prefix": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "grace",
                        "revoked"
                      ]
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "JobCreate": {
        "type": "object",
        "required": [
          "input"
        ],
        "properties": {
          "input": {
            "type": "string",
            "description": "YouTube channel, playlist, video URL, or list: key"
          },
          "contents": {
            "type": "string",
            "enum": [
              "all",
              "videos",
              "shorts"
            ]
          },
          "offset": {
            "type": "integer",
            "minimum": 0
          },
          "video_ids": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "uniqueItems": true,
            "items": {
              "type": "string",
              "minLength": 11,
              "maxLength": 11
            }
          },
          "format": {
            "type": "string",
            "enum": [
              "json",
              "clean",
              "timestamped",
              "both",
              "srt",
              "vtt",
              "md"
            ]
          },
          "view": {
            "type": "string",
            "enum": [
              "clean",
              "timestamped",
              "both"
            ]
          },
          "language": {
            "type": "string",
            "default": "en"
          },
          "fallback": {
            "type": "boolean",
            "default": true
          },
          "webhook_url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://"
          }
        },
        "additionalProperties": false
      },
      "ArchiveState": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Ok"
          },
          {
            "type": "object",
            "required": [
              "archive"
            ],
            "properties": {
              "archive": {
                "type": "object",
                "required": [
                  "id",
                  "job_id",
                  "status",
                  "formats",
                  "poll_url",
                  "parts"
                ],
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "job_id": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "queued",
                      "building",
                      "ready",
                      "failed"
                    ]
                  },
                  "formats": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "json",
                        "clean",
                        "timestamped",
                        "both",
                        "srt",
                        "vtt",
                        "md"
                      ]
                    }
                  },
                  "part_count": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "total_bytes": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "expires_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time"
                  },
                  "poll_url": {
                    "type": "string"
                  },
                  "parts": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "part",
                        "filename",
                        "bytes",
                        "url"
                      ],
                      "properties": {
                        "part": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "filename": {
                          "type": "string"
                        },
                        "bytes": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "url": {
                          "type": "string"
                        }
                      }
                    }
                  },
                  "error": {
                    "type": "object",
                    "required": [
                      "errorClass",
                      "message",
                      "retryable"
                    ],
                    "properties": {
                      "errorClass": {
                        "type": "string"
                      },
                      "message": {
                        "type": "string"
                      },
                      "retryable": {
                        "type": "boolean"
                      }
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "LatencyMeasurement": {
        "type": "object",
        "required": [
          "sample_count",
          "measured",
          "p50_ms",
          "p95_ms",
          "average_ms"
        ],
        "properties": {
          "sample_count": {
            "type": "integer",
            "minimum": 0
          },
          "measured": {
            "type": "boolean"
          },
          "p50_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "p95_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "average_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          }
        }
      },
      "Status": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Ok"
          },
          {
            "type": "object",
            "required": [
              "status",
              "degraded",
              "updated_at",
              "latency"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "healthy",
                  "degraded"
                ]
              },
              "degraded": {
                "type": "boolean"
              },
              "updated_at": {
                "type": "string",
                "format": "date-time"
              },
              "latency": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/LatencyMeasurement"
                  },
                  {
                    "type": "object",
                    "required": [
                      "source",
                      "window_hours",
                      "bucket_seconds",
                      "percentile_method",
                      "percentile_values_are_upper_bounds",
                      "window_started_at",
                      "measured_through",
                      "minimum_samples",
                      "transcript_cache_hits",
                      "transcript_cold"
                    ],
                    "properties": {
                      "source": {
                        "const": "authenticated_api_requests"
                      },
                      "window_hours": {
                        "type": "integer"
                      },
                      "bucket_seconds": {
                        "type": "integer"
                      },
                      "percentile_method": {
                        "const": "nearest_rank_histogram_upper_bound"
                      },
                      "percentile_values_are_upper_bounds": {
                        "const": true
                      },
                      "window_started_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "measured_through": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "minimum_samples": {
                        "type": "integer"
                      },
                      "transcript_cache_hits": {
                        "$ref": "#/components/schemas/LatencyMeasurement"
                      },
                      "transcript_cold": {
                        "$ref": "#/components/schemas/LatencyMeasurement"
                      }
                    }
                  }
                ]
              }
            }
          }
        ]
      },
      "Language": {
        "type": "object",
        "required": [
          "requested",
          "delivered",
          "kind",
          "fallback",
          "translation"
        ],
        "properties": {
          "requested": {
            "type": "string"
          },
          "delivered": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "manual",
              "asr"
            ]
          },
          "fallback": {
            "type": "boolean"
          },
          "translation": {
            "type": "string",
            "enum": [
              "not_requested",
              "served",
              "refused",
              "unavailable"
            ],
            "description": "refused: the translation throttle declined this attempt and the source track was delivered (retry later). unavailable: the video offers no translation into the requested language. Job rows are null before delivery."
          }
        }
      },
      "Transcript": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Ok"
          },
          {
            "type": "object",
            "required": [
              "video",
              "language",
              "format"
            ],
            "properties": {
              "video": {
                "type": "object",
                "required": [
                  "id"
                ],
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "title": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "channel": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "title": {
                        "type": [
                          "string",
                          "null"
                        ]
                      }
                    },
                    "additionalProperties": false
                  },
                  "published_at": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "duration": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  }
                }
              },
              "language": {
                "$ref": "#/components/schemas/Language"
              },
              "format": {
                "type": "string"
              },
              "text": {
                "type": "string"
              },
              "cues": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              }
            }
          }
        ]
      },
      "Pending": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Ok"
          },
          {
            "type": "object",
            "required": [
              "status",
              "request_id",
              "poll_url"
            ],
            "properties": {
              "status": {
                "const": "pending"
              },
              "request_id": {
                "type": "string",
                "format": "uuid"
              },
              "poll_url": {
                "type": "string"
              }
            }
          }
        ]
      },
      "Error": {
        "type": "object",
        "required": [
          "ok",
          "errorClass",
          "code",
          "message",
          "retryable",
          "docs_url",
          "request_id"
        ],
        "properties": {
          "ok": {
            "const": false
          },
          "errorClass": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "retryable": {
            "type": "boolean"
          },
          "docs_url": {
            "type": "string",
            "format": "uri"
          },
          "request_id": {
            "type": "string"
          },
          "buy_url": {
            "type": "string",
            "format": "uri"
          },
          "allow": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Present on 405 responses: the methods the path supports."
          }
        },
        "additionalProperties": true,
        "description": "Every error carries the errorClass taxonomy value, a detail code listed under that class on /docs/errors (docs_url links to the class), and the request id echoed in X-Request-Id."
      },
      "WebhookConfig": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://"
          },
          "low_balance_threshold": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "rotate_secret": {
            "type": "boolean",
            "default": false,
            "description": "Issue a new signing secret and return it once in this response."
          }
        },
        "additionalProperties": false
      },
      "CaptionTrack": {
        "type": "object",
        "required": [
          "language_code",
          "kind"
        ],
        "properties": {
          "language_code": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "manual",
              "asr"
            ]
          },
          "name": {
            "type": "string"
          }
        }
      },
      "VideoFacts": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Ok"
          },
          {
            "type": "object",
            "required": [
              "video",
              "cached_languages",
              "caption_tracks",
              "selected"
            ],
            "properties": {
              "video": {
                "type": "object",
                "required": [
                  "id",
                  "status"
                ],
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "title": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "channel_id": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "channel_title": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "published_at": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "duration": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": "Seconds"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "unknown",
                      "fetched",
                      "no_captions",
                      "unavailable",
                      "error"
                    ]
                  },
                  "error_class": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "fetched_at": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "cached_languages": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "language_code",
                    "kind"
                  ],
                  "properties": {
                    "language_code": {
                      "type": "string"
                    },
                    "kind": {
                      "type": "string",
                      "enum": [
                        "manual",
                        "asr"
                      ]
                    },
                    "cached_at": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              },
              "caption_tracks": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CaptionTrack"
                }
              },
              "selected": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/CaptionTrack"
                  }
                ]
              }
            }
          }
        ]
      }
    }
  },
  "security": [
    {
      "bearerAuth": []
    }
  ]
}
