openapi: 3.1.0
info:
  title: Jellypod API
  version: 1.0.0
  description: Programmatic access to Jellypod podcasts, episodes, hosts, sources,
    and voices.
  contact:
    name: Jellypod Support
    email: support@jellypod.ai
    url: https://www.jellypod.com/docs/api
  license:
    name: Proprietary
servers:
  - url: https://api.jellypod.com
    description: Production
  - url: http://localhost:3003
    description: Local development
tags:
  - name: Health
    description: Service liveness checks.
  - name: Account
    description: Authenticated organization details.
  - name: Podcasts
    description: Podcast resources.
  - name: Episodes
    description: Episode resources.
  - name: Analytics
    description: Streaming and playback metrics for podcasts and episodes.
  - name: Hosts
    description: Host (speaker) resources.
  - name: Sources
    description: Source documents fed to generation.
  - name: Voices
    description: TTS voices.
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: "API key issued from /settings/api-keys in the Jellypod studio.
        Send as `Authorization: Bearer <key>`."
  schemas:
    Account:
      type: object
      properties:
        data:
          type: object
          properties:
            organization_id:
              type: string
              readOnly: true
            plan:
              type: string
              readOnly: true
            credits:
              type: object
              properties:
                balance:
                  type: number
                  readOnly: true
              required:
                - balance
          required:
            - organization_id
            - plan
            - credits
      required:
        - data
      description: Caller organization, current plan, and credit balance.
    ApiError:
      type: object
      properties:
        type:
          type: string
          enum:
            - bad_request
            - validation_error
            - unauthorized
            - forbidden
            - not_found
            - conflict
            - method_not_allowed
            - unsupported_media_type
            - unprocessable_entity
            - rate_limited
            - bad_gateway
            - internal_error
          description: Machine-readable error class. Suitable for switch/match in client
            code.
        title:
          type: string
          description: Short human-readable summary.
        status:
          type: integer
          description: HTTP status code mirrored from the response.
        detail:
          type: string
          description: Longer human-readable explanation when available.
        request_id:
          type: string
          description: Correlation id, also returned in the `X-Request-Id` response
            header. Include in support requests.
        errors:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
              message:
                type: string
            required:
              - field
              - message
          description: Per-field validation failures. Present when `type` is
            `validation_error`.
      required:
        - type
        - title
        - status
      description: RFC 9457 error envelope returned for all non-2xx responses from the
        Jellypod API.
    EpisodeAnalytics:
      type: object
      properties:
        episode_id:
          type: string
          readOnly: true
        downloads:
          $ref: "#/components/schemas/EpisodeDownloads"
        video_views:
          $ref: "#/components/schemas/EpisodeVideoViews"
        as_of:
          type: string
          readOnly: true
          description: ISO-8601 time these metrics were computed.
      required:
        - episode_id
        - downloads
        - video_views
        - as_of
      description: Streaming metrics for a single episode. Audio download windows are
        measured since publish; video view windows are rolling.
    EpisodeDownloads:
      type: object
      properties:
        total:
          type: number
          description: All-time audio downloads (OP3).
        first_7_days:
          type: number
          description: Audio downloads in the first 7 days after publish.
        first_30_days:
          type: number
          description: Audio downloads in the first 30 days after publish.
      required:
        - total
        - first_7_days
        - first_30_days
    EpisodeVideoViews:
      type: object
      properties:
        total:
          type: number
          description: Web video views over the last 90 days (Mux retention window).
        last_7_days:
          type: number
          description: Web video views in the last 7 days.
        last_30_days:
          type: number
          description: Web video views in the last 30 days.
      required:
        - total
        - last_7_days
        - last_30_days
    PodcastAnalytics:
      type: object
      properties:
        podcast_id:
          type: string
          readOnly: true
        analytics_enabled:
          type: boolean
          description: Whether download tracking is enabled. When false, audio metrics are
            0.
        totals:
          type: object
          properties:
            plays:
              type: number
              description: All-time audio downloads + web video views.
            downloads:
              type: number
              description: All-time audio downloads (OP3).
            video_views:
              type: number
              description: Web video views (Mux, last 90 days).
            last_7_days:
              type: number
              description: Combined plays in the last 7 days.
            last_30_days:
              type: number
              description: Combined plays in the last 30 days.
          required:
            - plays
            - downloads
            - video_views
            - last_7_days
            - last_30_days
        plays_by_app:
          type: object
          additionalProperties:
            type: number
          description: Audio downloads grouped by client app (Spotify, Apple Podcasts,
            Overcast, ...). Show-level.
        episodes:
          type: array
          items:
            type: object
            properties:
              episode_id:
                type: string
                readOnly: true
              downloads:
                $ref: "#/components/schemas/EpisodeDownloads"
              video_views:
                $ref: "#/components/schemas/EpisodeVideoViews"
            required:
              - episode_id
              - downloads
              - video_views
        as_of:
          type: string
          readOnly: true
          description: ISO-8601 time these metrics were computed.
      required:
        - podcast_id
        - analytics_enabled
        - totals
        - plays_by_app
        - episodes
        - as_of
      description: "Streaming metrics for a podcast: combined totals, per-app download
        breakdown, and per-episode metrics."
    ListEpisodesResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Episode"
      required:
        - data
      description: Page of episodes. Use the `Link` response header (`rel="next"`) to
        paginate.
    Episode:
      type: object
      properties:
        id:
          type: string
          readOnly: true
        podcast_id:
          type: string
        status:
          $ref: "#/components/schemas/EpisodeStatus"
        title:
          type:
            - string
            - "null"
        description:
          type:
            - string
            - "null"
        image_url:
          type:
            - string
            - "null"
        url:
          type:
            - string
            - "null"
          readOnly: true
        video_url:
          type:
            - string
            - "null"
          readOnly: true
        audio_url:
          type:
            - string
            - "null"
          readOnly: true
        host_ids:
          type:
            - array
            - "null"
          items:
            type: string
        source_ids:
          type: array
          items:
            type: string
          readOnly: true
        published_at:
          type:
            - string
            - "null"
          readOnly: true
        scheduled_at:
          type:
            - string
            - "null"
          readOnly: true
        generation:
          type:
            - object
            - "null"
          properties:
            state:
              type: string
              enum:
                - generating
                - completed
                - failed
            started_at:
              type: string
            completed_at:
              type:
                - string
                - "null"
            error_message:
              type: string
          required:
            - state
            - started_at
            - completed_at
          readOnly: true
        created_at:
          type: string
          readOnly: true
        updated_at:
          type: string
          readOnly: true
      required:
        - id
        - podcast_id
        - status
        - title
        - description
        - image_url
        - url
        - video_url
        - audio_url
        - host_ids
        - source_ids
        - published_at
        - scheduled_at
        - generation
        - created_at
        - updated_at
      description: A podcast episode with publishing metadata and (when available)
        audio/video URLs.
    EpisodeStatus:
      type: string
      enum:
        - draft
        - published
        - scheduled
      description: Lifecycle state stored on the episode row. Generation progress is
        reported separately on the `generation` field —
        `generating`/`completed`/`failed` are not `status` values.
      readOnly: true
    CreateEpisodeInput:
      type: object
      properties:
        podcast_id:
          type: string
          format: uuid
          description: Podcast that will own the new episode.
        host_ids:
          type: array
          items:
            type: string
        title:
          type: string
          minLength: 1
          maxLength: 200
        description:
          type: string
          maxLength: 2000
        image:
          type: string
          format: uri
      required:
        - podcast_id
      additionalProperties: false
      description: Fields for creating an empty episode row. No content is generated.
    AcceptedEpisodeResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            id:
              type: string
            podcast_id:
              type: string
            host_ids:
              type: array
              items:
                type: string
            magic_created_host_ids:
              type: array
              items:
                type: string
              description: Subset of host_ids that were magic-created by this import (newly
                added to the org). Empty when all speakers matched existing
                hosts.
            status:
              type: string
              enum:
                - generating
            workflow_run_id:
              type: string
            created_at:
              type: string
            updated_at:
              type: string
          required:
            - id
            - podcast_id
            - host_ids
            - magic_created_host_ids
            - status
            - workflow_run_id
            - created_at
            - updated_at
      required:
        - data
      description: Acknowledgement for an asynchronously-generated episode.
    GenerateEpisodeInput:
      type: object
      properties:
        prompt:
          type: string
          minLength: 10
          maxLength: 10000
          description: Prompt used to write the episode script. Provide either `prompt` or
            `script`, not both.
          example: Create a friendly two-host episode about simple habits for a calmer
            workweek.
        script:
          $ref: "#/components/schemas/ImportedEpisodeScript"
        host_ids:
          type: array
          items:
            type: string
          description: Hosts to use for generation. In script mode, every segment host_id
            must be included here, or omit host_ids to infer them from the
            script.
          example:
            - 2e6939fe
            - a91c4d22
        source_ids:
          type: array
          items:
            type: string
            format: uuid
          description: Source documents for prompt-based generation. Not supported when
            `script` is provided.
        options:
          type: object
          properties:
            episode_length:
              type: string
            web_search:
              type: boolean
              default: true
          default:
            web_search: true
          description: Prompt-mode generation options. These are not used when `script` is
            provided.
        podcast_id:
          type: string
          format: uuid
          description: Podcast that will own the generated episode.
      required:
        - podcast_id
      additionalProperties: false
      description: Fields for generating a new episode asynchronously. Use prompt mode
        to have Jellypod write the script, or script mode to import a validated
        structured script.
    ImportedEpisodeScript:
      type: object
      properties:
        chapters:
          type: array
          items:
            $ref: "#/components/schemas/ImportedScriptChapter"
          minItems: 1
          maxItems: 50
          description: Ordered script chapters. Maximum 50 chapters.
      required:
        - chapters
      additionalProperties: false
      description: "Structured script input for episode generation. Script mode skips
        prompt-based script writing. Limits: 75000 total text characters, 50
        chapters, 200 segments per chapter, 600 total segments, and 5000
        characters per segment."
      example:
        chapters:
          - title: Opening
            segments:
              - host_id: 2e6939fe
                text: Welcome to the show. Today we are talking about one small habit that can
                  make the week easier.
              - host_id: a91c4d22
                text: I like that. Let us start with something simple someone can try tomorrow
                  morning.
                speed: 1
    ImportedScriptChapter:
      type: object
      properties:
        title:
          type: string
          minLength: 1
          maxLength: 200
          description: Optional chapter title.
          example: Opening
        segments:
          type: array
          items:
            $ref: "#/components/schemas/ImportedScriptSegment"
          minItems: 1
          maxItems: 200
          description: Spoken turns in this chapter. Maximum 200 segments.
      required:
        - segments
      additionalProperties: false
      description: A group of imported script segments. Each chapter can contain up to
        200 segments.
      example:
        title: Opening
        segments:
          - host_id: 2e6939fe
            text: Welcome to the show. Today we are talking about one small habit that can
              make the week easier.
          - host_id: a91c4d22
            text: I like that. Let us start with something simple someone can try tomorrow
              morning.
            speed: 1
    ImportedScriptSegment:
      type: object
      properties:
        host_id:
          type: string
          minLength: 1
          description: Host ID for the speaker of this segment.
          example: 2e6939fe
        text:
          type: string
          minLength: 1
          maxLength: 5000
          description: Text to synthesize for this segment. Maximum 5000 characters.
          example: Welcome to the show. Today we are talking about one small habit that
            can make the week easier.
        speed:
          type: number
          minimum: 0.5
          maximum: 2
          default: 1
          description: Optional playback speed for this segment. Defaults to 1.
          example: 1
      required:
        - host_id
        - text
      additionalProperties: false
      description: One spoken turn in an imported script. The host must belong to your
        organization.
      example:
        host_id: 2e6939fe
        text: Welcome to the show. Today we are talking about one small habit that can
          make the week easier.
        speed: 1
    ImportEpisodeInput:
      type: object
      properties:
        podcast_id:
          type: string
          format: uuid
          description: Podcast that will own the imported generated episode.
        script:
          type: string
          minLength: 1
          maxLength: 75000
          description: Script or transcript text. Bracketed speaker labels are resolved to
            existing or newly generated hosts.
          example: >-
            [Host One]

            Welcome to the show. Today we are talking about one small habit that
            can make the week easier.


            [Host Two]

            I like that. Let us start with something simple someone can try
            tomorrow morning.
      required:
        - podcast_id
        - script
      additionalProperties: false
      description: Fields for importing script or transcript text and generating a new
        episode asynchronously.
    UpdateEpisodeInput:
      type: object
      properties:
        title:
          type: string
          minLength: 1
          maxLength: 200
        description:
          type:
            - string
            - "null"
          maxLength: 2000
        image:
          type:
            - string
            - "null"
          format: uri
      additionalProperties: false
      description: Patchable episode fields. All optional; only provided fields are updated.
    UploadEpisodeImageResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            id:
              type: string
            image_url:
              type: string
          required:
            - id
            - image_url
      required:
        - data
      description: The uploaded image URL.
    PublishEpisodeResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            id:
              type: string
            status:
              type: string
              enum:
                - published
                - scheduled
            scheduled_time:
              type: string
          required:
            - id
            - status
      required:
        - data
      description: Result of a publish or schedule operation.
    PublishEpisodeInput:
      type: object
      properties:
        scheduled_time:
          type: string
          format: date-time
      additionalProperties: false
      description: Optional publishing options. Omit to publish immediately; provide
        `scheduled_time` to schedule for later.
    TimestampsJsonResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            episode_id:
              type: string
            speakers:
              type: array
              items:
                type: object
                properties:
                  host_id:
                    type: string
                  name:
                    type: string
                required:
                  - host_id
                  - name
            words:
              type: array
              items:
                type: object
                properties:
                  text:
                    type: string
                  start:
                    type: number
                  end:
                    type: number
                  speaker:
                    type:
                      - number
                      - "null"
                required:
                  - text
                  - start
                  - end
                  - speaker
          required:
            - episode_id
            - speakers
            - words
      required:
        - data
      description: Word-level episode timestamps with speaker attribution. Speakers
        are referenced by integer index into the `speakers` array.
    ListHostsResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/HostRow"
      required:
        - data
      description: Page of hosts. Use the `Link` response header (`rel="next"`) to paginate.
    HostRow:
      type: object
      properties:
        id:
          type: string
          readOnly: true
          description: 8-character hex host id (e.g. `2e6939fe`).
          example: 2e6939fe
        name:
          type: string
        title:
          type:
            - string
            - "null"
        backstory:
          type:
            - string
            - "null"
        personality:
          type:
            - string
            - "null"
        voice_id:
          type:
            - number
            - "null"
        voice_model:
          type:
            - string
            - "null"
        voice:
          type:
            - object
            - "null"
          properties:
            id:
              type: integer
            description:
              type:
                - string
                - "null"
            language:
              type:
                - string
                - "null"
              enum:
                - en
                - es
                - fr
                - hi
                - pt
                - zh
                - de
                - ja
                - ar
                - ru
                - ko
                - id
                - it
                - nl
                - tr
                - pl
                - sv
                - fil
                - ms
                - ro
                - uk
                - el
                - cs
                - da
                - fi
                - bg
                - hr
                - sk
                - ta
                - null
            gender:
              type: string
              enum:
                - male
                - female
                - other
            accent:
              type:
                - string
                - "null"
            voice_type:
              type: string
              enum:
                - professional
                - clone
                - design
            model:
              type:
                - string
                - "null"
          required:
            - id
            - description
            - language
            - gender
            - accent
            - voice_type
            - model
        created_at:
          type: string
          readOnly: true
        updated_at:
          type: string
          readOnly: true
      required:
        - id
        - name
        - title
        - backstory
        - personality
        - voice_id
        - voice_model
        - voice
        - created_at
        - updated_at
      description: A host (speaker/presenter) belonging to your organization.
    CreateHostInput:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
        voice_id:
          type: integer
        title:
          type: string
          minLength: 1
          maxLength: 100
        personality:
          type: string
          minLength: 1
          maxLength: 3000
        backstory:
          type: string
          minLength: 10
          maxLength: 3000
        voice_model:
          type: string
        is_guest:
          type: boolean
        image_path:
          type: string
      required:
        - name
        - voice_id
        - backstory
      additionalProperties: false
      description: Fields required to create a host.
    UpdateHostInput:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
        voice_id:
          type: integer
        title:
          type: string
          minLength: 1
          maxLength: 100
        personality:
          type: string
          minLength: 1
          maxLength: 3000
        backstory:
          type: string
          minLength: 10
          maxLength: 3000
        voice_model:
          type: string
        is_guest:
          type: boolean
        image_path:
          type: string
      additionalProperties: false
      description: Patchable host fields. All optional; only provided fields are updated.
    ListPodcastsResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Podcast"
      required:
        - data
      description: Page of podcasts. Use the `Link` response header (`rel="next"`) to
        paginate.
    Podcast:
      type: object
      properties:
        id:
          type: string
          readOnly: true
        title:
          type: string
        description:
          type: string
        image_url:
          type:
            - string
            - "null"
        host_ids:
          type:
            - array
            - "null"
          items:
            type: string
        languages:
          type: array
          items:
            type: string
        episode_order:
          type: string
          enum:
            - episodic
            - serial
          description: "Episode ordering: `episodic` (standalone) or `serial`
            (sequential). Always returned lowercase."
        visibility:
          type:
            - string
            - "null"
          enum:
            - public
            - private
            - unlisted
            - null
          description: Visibility. Always returned lowercase.
        url:
          type: string
          readOnly: true
        rss_feed_url:
          type: string
          readOnly: true
        episode_count:
          type: number
          readOnly: true
          description: Number of episodes that belong to this podcast.
        created_at:
          type: string
          readOnly: true
        updated_at:
          type: string
          readOnly: true
      required:
        - id
        - title
        - description
        - image_url
        - host_ids
        - languages
        - episode_order
        - visibility
        - url
        - rss_feed_url
        - episode_count
        - created_at
        - updated_at
      description: A podcast in your library with metadata, hosts, languages, and feed URLs.
    CreatePodcastInput:
      type: object
      properties:
        title:
          type: string
          maxLength: 100
        description:
          type: string
        host_ids:
          type: array
          items:
            type: string
          minItems: 1
        language:
          type: string
          pattern: ^[a-zA-Z]{2}$
          default: en
        episode_order:
          type: string
          enum:
            - episodic
            - serial
          default: serial
        visibility:
          type: string
          enum:
            - public
            - private
            - unlisted
          default: public
      required:
        - title
        - host_ids
      additionalProperties: false
      description: Fields required to create a podcast.
    AcceptedPodcastResponse:
      type: object
      properties:
        data:
          allOf:
            - $ref: "#/components/schemas/Podcast"
            - type: object
              properties:
                episodes:
                  type: array
                  items:
                    $ref: "#/components/schemas/EpisodeStub"
              required:
                - episodes
          description: A podcast in your library with metadata, hosts, languages, and feed
            URLs.
      required:
        - data
      description: Acknowledgement for asynchronous podcast generation.
    EpisodeStub:
      type: object
      properties:
        id:
          type: string
        title:
          type: string
        status:
          type: string
          enum:
            - generating
        episode_number:
          type: number
      required:
        - id
        - title
        - status
        - episode_number
    GeneratePodcastInput:
      type: object
      properties:
        title:
          type: string
          maxLength: 100
        description:
          type: string
        prompt:
          type: string
          minLength: 10
          maxLength: 10000
        host_ids:
          type: array
          items:
            type: string
          minItems: 1
        source_ids:
          type: array
          items:
            type: string
            format: uuid
        language:
          type: string
          pattern: ^[a-zA-Z]{2}$
          default: en
        episode_order:
          type: string
          enum:
            - episodic
            - serial
          default: serial
        visibility:
          type: string
          enum:
            - public
            - private
            - unlisted
          default: public
        options:
          type: object
          properties:
            num_episodes:
              type: integer
              minimum: 1
              maximum: 8
              default: 4
            episode_length:
              type: string
            web_search:
              type: boolean
              default: true
          default:
            num_episodes: 4
            web_search: true
      required:
        - host_ids
      additionalProperties: false
      description: Fields for generating a podcast and its first episodes in one call.
    UpdatePodcastInput:
      type: object
      properties:
        title:
          type: string
          maxLength: 100
        description:
          type: string
        host_ids:
          type: array
          items:
            type: string
          minItems: 1
        language:
          type: string
          pattern: ^[a-zA-Z]{2}$
        episode_order:
          type: string
          enum:
            - episodic
            - serial
        visibility:
          type: string
          enum:
            - public
            - private
            - unlisted
      additionalProperties: false
      description: Patchable podcast fields. All optional; only provided fields are updated.
    ListSourcesResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/SourceRow"
      required:
        - data
      description: Page of sources. Use the `Link` response header (`rel="next"`) to
        paginate.
    SourceRow:
      type: object
      properties:
        id:
          type: string
          readOnly: true
        title:
          type:
            - string
            - "null"
        type:
          type:
            - string
            - "null"
        status:
          type: string
          readOnly: true
        metadata: {}
        error_message:
          type:
            - string
            - "null"
        created_at:
          type: string
          readOnly: true
        updated_at:
          type: string
          readOnly: true
      required:
        - id
        - title
        - type
        - status
        - error_message
        - created_at
        - updated_at
      description: A content source used as input for episodes.
    AcceptedSourceResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/SourceRow"
      required:
        - data
      description: Acknowledgement for asynchronous source processing.
    CreateSourceInput:
      oneOf:
        - type: object
          properties:
            type:
              type: string
              enum:
                - url
            url:
              type: string
              format: uri
            title:
              type: string
          required:
            - type
            - url
          additionalProperties: false
        - type: object
          properties:
            type:
              type: string
              enum:
                - youtube
            url:
              type: string
              format: uri
            title:
              type: string
          required:
            - type
            - url
          additionalProperties: false
        - type: object
          properties:
            type:
              type: string
              enum:
                - text
            content:
              type: string
              maxLength: 100000
            title:
              type: string
          required:
            - type
            - content
          additionalProperties: false
      description: Body for creating a URL, YouTube, or text source. `type` is
        case-insensitive — the server lowercases it before validation. `url` is
        required when `type` is `url` or `youtube`; `content` is required when
        `type` is `text`.
    ListVoicesResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/VoiceRow"
      required:
        - data
      description: Page of voices. Use the `Link` response header (`rel="next"`) to
        paginate.
    VoiceRow:
      type: object
      properties:
        id:
          type: integer
          readOnly: true
          description: Positive integer voice id. Pass this value (as a number, not a
            string) to `voice_id` on `POST /v1/hosts`.
          example: 30142
        description:
          type:
            - string
            - "null"
        language:
          type:
            - string
            - "null"
          enum:
            - en
            - es
            - fr
            - hi
            - pt
            - zh
            - de
            - ja
            - ar
            - ru
            - ko
            - id
            - it
            - nl
            - tr
            - pl
            - sv
            - fil
            - ms
            - ro
            - uk
            - el
            - cs
            - da
            - fi
            - bg
            - hr
            - sk
            - ta
            - null
        gender:
          type: string
          enum:
            - male
            - female
            - other
        accent:
          type:
            - string
            - "null"
        voice_type:
          type: string
          enum:
            - professional
            - clone
            - design
        model:
          type:
            - string
            - "null"
          readOnly: true
          description: Provider-prefixed model string used to synthesize this voice (e.g.
            "elevenlabs/eleven_v3"). Null when the voice has no model
            configured.
          example: elevenlabs/eleven_v3
      required:
        - id
        - description
        - language
        - gender
        - accent
        - voice_type
        - model
      description: A TTS voice available to your organization.
  parameters: {}
paths:
  /health:
    get:
      operationId: health
      tags:
        - Health
      security: []
      responses:
        "200":
          description: Service is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - ok
                required:
                  - status
  /v1/account:
    get:
      operationId: getAccount
      summary: Get the authenticated organization
      tags:
        - Account
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Authenticated organization details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Account"
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/episodes/{episode_id}/analytics:
    get:
      operationId: getEpisodeAnalytics
      summary: Get streaming analytics for an episode
      tags:
        - Analytics
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Episode UUID
          required: true
          description: Episode UUID
          in: path
          name: episode_id
      responses:
        "200":
          description: Episode analytics
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/EpisodeAnalytics"
                required:
                  - data
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/podcasts/{podcast_id}/analytics:
    get:
      operationId: getPodcastAnalytics
      summary: Get streaming analytics for a podcast
      tags:
        - Analytics
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Podcast UUID
          required: true
          description: Podcast UUID
          in: path
          name: podcast_id
      responses:
        "200":
          description: Podcast analytics
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/PodcastAnalytics"
                required:
                  - data
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/episodes:
    get:
      operationId: listEpisodes
      summary: List episodes
      tags:
        - Episodes
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
          required: false
          name: cursor
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          required: false
          name: limit
          in: query
        - schema:
            type: string
            format: uuid
          required: false
          name: podcast_id
          in: query
        - schema:
            type: string
          required: false
          name: status
          in: query
      responses:
        "200":
          description: Page of episodes
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListEpisodesResponse"
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
    post:
      operationId: createEpisode
      summary: Create an empty episode
      description: Inserts an episode row with only the metadata you provide. No
        script, audio, or video is generated. Use `POST /v1/episodes/generate`
        to create an episode and trigger generation in one call.
      tags:
        - Episodes
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateEpisodeInput"
      responses:
        "201":
          description: Episode created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Episode"
                required:
                  - data
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "415":
          description: Request `Content-Type` is not supported for this endpoint.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/episodes/generate:
    post:
      operationId: generateEpisode
      summary: Generate a new episode (asynchronous)
      tags:
        - Episodes
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GenerateEpisodeInput"
      responses:
        "202":
          description: Episode queued for generation
          headers:
            Location:
              description: Relative URL clients can poll for the accepted resource.
              schema:
                type: string
            Retry-After:
              description: Suggested polling delay in seconds.
              schema:
                type: integer
                minimum: 1
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AcceptedEpisodeResponse"
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "415":
          description: Request `Content-Type` is not supported for this endpoint.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "502":
          description: The API could not enqueue the downstream asynchronous workflow.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/episodes/import:
    post:
      operationId: importEpisode
      summary: Import script text and generate a new episode (asynchronous)
      tags:
        - Episodes
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ImportEpisodeInput"
      responses:
        "202":
          description: Imported episode queued for generation
          headers:
            Location:
              description: Relative URL clients can poll for the accepted resource.
              schema:
                type: string
            Retry-After:
              description: Suggested polling delay in seconds.
              schema:
                type: integer
                minimum: 1
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AcceptedEpisodeResponse"
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "415":
          description: Request `Content-Type` is not supported for this endpoint.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "502":
          description: The API could not enqueue the downstream asynchronous workflow.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/episodes/{episode_id}:
    patch:
      operationId: updateEpisode
      summary: Update episode fields
      tags:
        - Episodes
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Episode UUID
          required: true
          description: Episode UUID
          in: path
          name: episode_id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateEpisodeInput"
          application/merge-patch+json:
            schema:
              $ref: "#/components/schemas/UpdateEpisodeInput"
      responses:
        "200":
          description: Updated episode
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Episode"
                required:
                  - data
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "415":
          description: Request `Content-Type` is not supported for this endpoint.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
    get:
      operationId: getEpisode
      summary: Get an episode
      tags:
        - Episodes
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Episode UUID
          required: true
          description: Episode UUID
          in: path
          name: episode_id
      responses:
        "200":
          description: Episode
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Episode"
                required:
                  - data
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
    delete:
      operationId: deleteEpisode
      summary: Delete an episode
      tags:
        - Episodes
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Episode UUID
          required: true
          description: Episode UUID
          in: path
          name: episode_id
      responses:
        "204":
          description: Episode deleted
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/episodes/{episode_id}/image:
    put:
      operationId: uploadEpisodeImage
      summary: Upload an episode cover image (raw image bytes in body)
      tags:
        - Episodes
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Episode UUID
          required: true
          description: Episode UUID
          in: path
          name: episode_id
      requestBody:
        required: true
        description: Raw image bytes. Set `Content-Type` to `image/jpeg`, `image/png`,
          or `image/webp`.
        content:
          application/octet-stream:
            schema:
              type: string
              format: binary
      responses:
        "200":
          description: Image uploaded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UploadEpisodeImageResponse"
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "415":
          description: Request `Content-Type` is not supported for this endpoint.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/episodes/{episode_id}/publish:
    post:
      operationId: publishEpisode
      summary: Publish or schedule an episode
      tags:
        - Episodes
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Episode UUID
          required: true
          description: Episode UUID
          in: path
          name: episode_id
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PublishEpisodeInput"
      responses:
        "200":
          description: Episode published or scheduled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublishEpisodeResponse"
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "415":
          description: Request `Content-Type` is not supported for this endpoint.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "422":
          description: Request was well-formed but the resource is in a state that does
            not allow this operation (e.g. publishing an episode that has not
            finished rendering).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/episodes/{episode_id}/unpublish:
    post:
      operationId: unpublishEpisode
      summary: Unpublish a published or scheduled episode
      tags:
        - Episodes
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Episode UUID
          required: true
          description: Episode UUID
          in: path
          name: episode_id
      responses:
        "200":
          description: Episode unpublished
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Episode"
                required:
                  - data
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "415":
          description: Request `Content-Type` is not supported for this endpoint.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "422":
          description: Request was well-formed but the resource is in a state that does
            not allow this operation (e.g. publishing an episode that has not
            finished rendering).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/episodes/{episode_id}/timestamps:
    get:
      operationId: getEpisodeTimestamps
      summary: Get episode timestamps
      description: Returns timestamps in the requested `format`. Use `format=json`
        (default) for structured timestamps, `format=srt` for SubRip, or
        `format=vtt` for WebVTT.
      tags:
        - Episodes
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Episode UUID
          required: true
          description: Episode UUID
          in: path
          name: episode_id
        - schema:
            type: string
            enum:
              - json
              - srt
              - vtt
            description: Response format. `json` returns word-level timestamps with speaker
              attribution; `srt` returns SubRip; `vtt` returns WebVTT. Defaults
              to `json`.
          required: false
          description: Response format. `json` returns word-level timestamps with speaker
            attribution; `srt` returns SubRip; `vtt` returns WebVTT. Defaults to
            `json`.
          in: query
          name: format
      responses:
        "200":
          description: Timestamps in the requested format. Response `Content-Type`
            reflects `format`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TimestampsJsonResponse"
            application/x-subrip:
              schema:
                type: string
            text/vtt:
              schema:
                type: string
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/hosts:
    get:
      operationId: listHosts
      summary: List hosts
      tags:
        - Hosts
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
          required: false
          name: cursor
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          required: false
          name: limit
          in: query
      responses:
        "200":
          description: Page of hosts
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListHostsResponse"
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
    post:
      operationId: createHost
      summary: Create a host
      tags:
        - Hosts
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateHostInput"
      responses:
        "201":
          description: Host created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/HostRow"
                required:
                  - data
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "415":
          description: Request `Content-Type` is not supported for this endpoint.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/hosts/{host_id}:
    get:
      operationId: getHost
      summary: Get a host
      tags:
        - Hosts
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            pattern: ^[0-9a-f]{8}$/i
            description: 8-character hex host id (e.g. `2e6939fe`).
            example: 2e6939fe
          required: true
          description: 8-character hex host id (e.g. `2e6939fe`).
          in: path
          name: host_id
      responses:
        "200":
          description: Host
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/HostRow"
                required:
                  - data
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
    patch:
      operationId: updateHost
      summary: Update a host
      tags:
        - Hosts
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            pattern: ^[0-9a-f]{8}$/i
            description: 8-character hex host id (e.g. `2e6939fe`).
            example: 2e6939fe
          required: true
          description: 8-character hex host id (e.g. `2e6939fe`).
          in: path
          name: host_id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateHostInput"
          application/merge-patch+json:
            schema:
              $ref: "#/components/schemas/UpdateHostInput"
      responses:
        "200":
          description: Host updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/HostRow"
                required:
                  - data
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "415":
          description: Request `Content-Type` is not supported for this endpoint.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
    delete:
      operationId: deleteHost
      summary: Delete a host
      tags:
        - Hosts
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            pattern: ^[0-9a-f]{8}$/i
            description: 8-character hex host id (e.g. `2e6939fe`).
            example: 2e6939fe
          required: true
          description: 8-character hex host id (e.g. `2e6939fe`).
          in: path
          name: host_id
      responses:
        "204":
          description: Host deleted
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/podcasts:
    get:
      operationId: listPodcasts
      summary: List podcasts
      tags:
        - Podcasts
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
          required: false
          name: cursor
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          required: false
          name: limit
          in: query
      responses:
        "200":
          description: Page of podcasts
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListPodcastsResponse"
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
    post:
      operationId: createPodcast
      summary: Create a podcast
      tags:
        - Podcasts
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePodcastInput"
      responses:
        "201":
          description: Podcast created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Podcast"
                required:
                  - data
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "415":
          description: Request `Content-Type` is not supported for this endpoint.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/podcasts/generate:
    post:
      operationId: generatePodcast
      summary: Generate a podcast and its first episodes from a topic or sources
      tags:
        - Podcasts
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GeneratePodcastInput"
      responses:
        "202":
          description: Podcast queued for generation
          headers:
            Location:
              description: Relative URL clients can poll for the accepted resource.
              schema:
                type: string
            Retry-After:
              description: Suggested polling delay in seconds.
              schema:
                type: integer
                minimum: 1
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AcceptedPodcastResponse"
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "415":
          description: Request `Content-Type` is not supported for this endpoint.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "502":
          description: The API could not enqueue the downstream asynchronous workflow.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/podcasts/{podcast_id}:
    get:
      operationId: getPodcast
      summary: Get a podcast
      tags:
        - Podcasts
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Podcast UUID
          required: true
          description: Podcast UUID
          in: path
          name: podcast_id
      responses:
        "200":
          description: Podcast
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Podcast"
                required:
                  - data
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
    patch:
      operationId: updatePodcast
      summary: Update a podcast
      tags:
        - Podcasts
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Podcast UUID
          required: true
          description: Podcast UUID
          in: path
          name: podcast_id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdatePodcastInput"
          application/merge-patch+json:
            schema:
              $ref: "#/components/schemas/UpdatePodcastInput"
      responses:
        "200":
          description: Podcast updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Podcast"
                required:
                  - data
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "415":
          description: Request `Content-Type` is not supported for this endpoint.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
    delete:
      operationId: deletePodcast
      summary: Delete a podcast
      tags:
        - Podcasts
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Podcast UUID
          required: true
          description: Podcast UUID
          in: path
          name: podcast_id
      responses:
        "204":
          description: Podcast deleted
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/podcasts/{podcast_id}/image:
    put:
      operationId: updatePodcastImage
      summary: Replace the podcast cover image
      tags:
        - Podcasts
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Podcast UUID
          required: true
          description: Podcast UUID
          in: path
          name: podcast_id
      requestBody:
        required: true
        description: Raw image bytes. Set `Content-Type` to `image/jpeg`, `image/png`,
          or `image/webp`.
        content:
          application/octet-stream:
            schema:
              type: string
              format: binary
      responses:
        "200":
          description: Image updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Podcast"
                required:
                  - data
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "415":
          description: Request `Content-Type` is not supported for this endpoint.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/sources:
    get:
      operationId: listSources
      summary: List sources
      tags:
        - Sources
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
          required: false
          name: cursor
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          required: false
          name: limit
          in: query
        - schema:
            type: string
          required: false
          name: type
          in: query
        - schema:
            type: string
          required: false
          name: status
          in: query
      responses:
        "200":
          description: Page of sources
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListSourcesResponse"
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
    post:
      operationId: createSource
      summary: Create a source from a URL, YouTube link, or text payload
      tags:
        - Sources
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateSourceInput"
      responses:
        "202":
          description: Source accepted for processing
          headers:
            Location:
              description: Relative URL clients can poll for the accepted resource.
              schema:
                type: string
            Retry-After:
              description: Suggested polling delay in seconds.
              schema:
                type: integer
                minimum: 1
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AcceptedSourceResponse"
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "415":
          description: Request `Content-Type` is not supported for this endpoint.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "502":
          description: The API could not enqueue the downstream asynchronous workflow.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/sources/{source_id}:
    get:
      operationId: getSource
      summary: Get a source
      tags:
        - Sources
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Source UUID
          required: true
          description: Source UUID
          in: path
          name: source_id
      responses:
        "200":
          description: Source
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/SourceRow"
                required:
                  - data
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
    delete:
      operationId: deleteSource
      summary: Delete a source
      tags:
        - Sources
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Source UUID
          required: true
          description: Source UUID
          in: path
          name: source_id
      responses:
        "204":
          description: Source deleted
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/voices:
    get:
      operationId: listVoices
      summary: List voices
      tags:
        - Voices
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
          required: false
          name: cursor
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          required: false
          name: limit
          in: query
        - schema:
            type: string
            pattern: ^[a-zA-Z]{2}$
          required: false
          name: language
          in: query
        - schema:
            type: string
          required: false
          name: gender
          in: query
        - schema:
            type: string
          required: false
          name: voice_type
          in: query
      responses:
        "200":
          description: Page of voices
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListVoicesResponse"
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /v1/voices/{voice_id}:
    get:
      operationId: getVoice
      summary: Get a voice
      tags:
        - Voices
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            pattern: ^[1-9]\d*$
            description: Voice ID (positive integer).
            example: "30142"
          required: true
          description: Voice ID (positive integer).
          in: path
          name: voice_id
      responses:
        "200":
          description: Voice
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/VoiceRow"
                required:
                  - data
        "400":
          description: Validation failed or malformed request.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Missing or invalid API key.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403":
          description: Authenticated, but this API key does not have access to this
            resource.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Resource not found.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Rate limit exceeded. Response includes `Retry-After`, `RateLimit`,
            and `RateLimit-Policy` headers (IETF draft).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "500":
          description: Internal server error.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiError"
webhooks: {}
