Ditto Audio Models API

Voice-model catalog — browse, download & upload custom voices

Operations 30

GET /v1/models Browse the published voice-model catalog #
GET /v1/models/catalog The whole published catalogue in one answer, with the version to keep a copy by #
GET /v1/models/languages The languages the catalogue has voices recorded in, with how many #
GET /v1/models/taxonomy The voice taxonomy: languages, accents (a hierarchy per language), genders… #
GET /v1/models/mine Your models — per-user refs joined to catalog metadata (for the reader's Models… #
GET /v1/models/premium/usage Your premium-voice quota: used / limit / claimed voices (drives upgrade prompts) #
GET /v1/models/support/rvc The RVC engine's support files (ContentVec, FCPE), presigned — what the desktop… #
GET /v1/models/support/pocket The Pocket engine's shared ONNX bundle, presigned — what the desktop app… #
GET /v1/models/support/pocket-voices Pocket's stock voices, presigned: one reference clip per voice, named for it #
GET /v1/models/support/morpho-tones Ditto Morpho's stock tone models, presigned: one folder per model… #
GET /v1/models/{slug} Get a published model with its file list (no URLs) #
GET /v1/models/{slug}/samples/{sample} Stream a pre-rendered preview clip #
GET /v1/models/{slug}/download Get presigned download URLs for every file. #
GET /v1/models/{slug}/files/{fileId}/download Get a presigned URL for one file. #
POST /v1/models/custom Create a custom XTTS voice and reserve latent upload URLs #
POST /v1/models/custom/{slug}/confirm Finalize a custom voice after its latents are uploaded #
GET /v1/models/custom/by-source/{sourceHash} Look up YOUR earlier pth→onnx conversion of a source model.pth, by its sha-256 #
GET /v1/models/custom/{slug}/download Owner manifest: presigned latents + base voice slug/version #
DELETE /v1/models/custom/{slug} Delete a custom voice (blobs + row + ref) #
GET /v1/custom-voices/{id} One of your custom voices: metadata, files and preview clips #
PATCH /v1/custom-voices/{id} Describe one of your custom voices: name, description, language, accent… #
DELETE /v1/custom-voices/{id} Delete one of your custom voices (row, account ref, private files) #
GET /v1/custom-voices/{id}/samples/{sample} Stream one of your custom voice's preview clips (owner-only) #
PUT /v1/custom-voices/{id}/samples/{sample} Save a preview clip you rendered on your own device (owner-only) #
GET /v1/custom-voices/{id}/download Owner manifest: presigned files + the base voice it overlays #
GET /v1/models/imported List the user's URL-imported voices #
POST /v1/models/imported Register a voice imported from a URL #
GET /v1/models/imported/{id} One imported voice #
PATCH /v1/models/imported/{id} Rename, or refresh the cached file list after URLs expire #
DELETE /v1/models/imported/{id} Remove an imported voice (soft delete) #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/ditto-audio:ditto-audio-models-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

ditto-audio-models-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: ditto.audio Models API
  description: Cloud API behind ditto.audio. The reader (reader.ditto.audio) and the voice studio (studio.ditto.audio) are separate apps sharing this one backend — see /docs/reader and /docs/inference for each app’s public surface. Third-party integrations want /docs/public (rendered for humans at https://ditto.audio/docs).
  version: '1.0'
  contact: {}
servers: []
tags:
- name: Models
  description: Voice-model catalog — browse, download & upload custom voices
paths:
  /v1/models:
    get:
      operationId: VoiceModelsController_list
      parameters:
      - name: q
        required: false
        in: query
        description: free-text search over name/description/tags
        schema:
          type: string
      - name: engine
        required: false
        in: query
        schema:
          type: string
          enum:
          - piper
          - xtts
          - kokoro
          - pocket
          - rvc
          - system
          - morpho
      - name: language
        required: false
        in: query
        schema:
          example: en
          type: string
      - name: gender
        required: false
        in: query
        description: 'Only voices that sound like this: female, male, neutral (comma-separated for any of several).'
        schema:
          example: female
          type: string
      - name: accent
        required: false
        in: query
        description: Only voices with one of these accents (comma-separated keys of GET /v1/models/taxonomy). An accent includes every accent under it (Latin American includes Mexican), stated or meant by the voice's language tag (`es-MX`).
        schema:
          example: es-latin-american
          type: string
      - name: age
        required: false
        in: query
        description: 'Only voices that sound this old: young, middle-aged, older (comma-separated).'
        schema:
          example: young
          type: string
      - name: style
        required: false
        in: query
        description: 'Only voices with ALL of these styles (comma-separated taxonomy keys): each style narrows.'
        schema:
          example: warm,deep
          type: string
      - name: use
        required: false
        in: query
        description: Only voices suited to one of these uses (comma-separated taxonomy keys).
        schema:
          example: narration
          type: string
      - name: tag
        required: false
        in: query
        description: single keyword filter
        schema:
          type: string
      - name: featuredOnly
        required: false
        in: query
        schema:
          default: false
          type: boolean
      - name: sortBy
        required: false
        in: query
        schema:
          default: popular
          type: string
          enum:
          - newest
          - popular
          - name
      - name: page
        required: false
        in: query
        schema:
          default: 1
          type: number
      - name: limit
        required: false
        in: query
        schema:
          maximum: 100
          default: 24
          type: number
      - name: locale
        required: false
        in: query
        description: 'The language of the site page asking (one of the site''s languages: en, de, es, fr, ...; anything else is English). A catalogue voice''s description comes back translated when a current translation exists, else in English; each item says which in `descriptionLanguage`. Names are never translated.'
        schema:
          example: de
          type: string
      responses:
        '200':
          description: ''
      summary: Browse the published voice-model catalog
      tags:
      - Models
  /v1/models/catalog:
    get:
      description: 'Every voice GET /v1/models lists (its default order, all pages at once), in the language asked for, and `version`: a hash of what it lists, download counts aside. A client that keeps a copy sends the version it holds as `have` and gets `{ version, unchanged: true }` while that copy is current, else the catalogue. Never cached by a shared cache: every answer is current.'
      operationId: VoiceModelsController_catalog
      parameters:
      - name: locale
        required: false
        in: query
        description: 'A site language code (en, de, ja, ...): descriptions come back in it when translated.'
        schema:
          example: de
          type: string
      - name: have
        required: false
        in: query
        description: The `version` of the copy the client holds.
        schema:
          example: 3f6c0a9e2b71d48c5e10
          type: string
      responses:
        '200':
          description: ''
      summary: The whole published catalogue in one answer, with the version to keep a copy by
      tags:
      - Models
  /v1/models/languages:
    get:
      description: Counts what GET /v1/models lists with the same filters (`q`, `engine`, `gender`, `age`, `accent`, `style`, `use`, `tag`, `featuredOnly`), grouped by primary language (en-US and en-GB are both English), largest first; `language` itself is never applied to its own counts. Each `code` is the `language` to pass to GET /v1/models to list those voices.
      operationId: VoiceModelsController_languages
      parameters:
      - name: q
        required: false
        in: query
        description: free-text search over name/description/tags
        schema:
          type: string
      - name: engine
        required: false
        in: query
        schema:
          type: string
          enum:
          - piper
          - xtts
          - kokoro
          - pocket
          - rvc
          - system
          - morpho
      - name: language
        required: false
        in: query
        schema:
          example: en
          type: string
      - name: gender
        required: false
        in: query
        description: 'Only voices that sound like this: female, male, neutral (comma-separated for any of several).'
        schema:
          example: female
          type: string
      - name: accent
        required: false
        in: query
        description: Only voices with one of these accents (comma-separated keys of GET /v1/models/taxonomy). An accent includes every accent under it (Latin American includes Mexican), stated or meant by the voice's language tag (`es-MX`).
        schema:
          example: es-latin-american
          type: string
      - name: age
        required: false
        in: query
        description: 'Only voices that sound this old: young, middle-aged, older (comma-separated).'
        schema:
          example: young
          type: string
      - name: style
        required: false
        in: query
        description: 'Only voices with ALL of these styles (comma-separated taxonomy keys): each style narrows.'
        schema:
          example: warm,deep
          type: string
      - name: use
        required: false
        in: query
        description: Only voices suited to one of these uses (comma-separated taxonomy keys).
        schema:
          example: narration
          type: string
      - name: tag
        required: false
        in: query
        description: single keyword filter
        schema:
          type: string
      - name: featuredOnly
        required: false
        in: query
        schema:
          default: false
          type: boolean
      - name: sortBy
        required: false
        in: query
        schema:
          default: popular
          type: string
          enum:
          - newest
          - popular
          - name
      - name: page
        required: false
        in: query
        schema:
          default: 1
          type: number
      - name: limit
        required: false
        in: query
        schema:
          maximum: 100
          default: 24
          type: number
      - name: locale
        required: false
        in: query
        description: 'The language of the site page asking (one of the site''s languages: en, de, es, fr, ...; anything else is English). A catalogue voice''s description comes back translated when a current translation exists, else in English; each item says which in `descriptionLanguage`. Names are never translated.'
        schema:
          example: de
          type: string
      responses:
        '200':
          description: ''
      summary: The languages the catalogue has voices recorded in, with how many
      tags:
      - Models
  /v1/models/taxonomy:
    get:
      description: The controlled vocabulary of every voice's `accent`, `gender`, `age`, `styles` and `useCases`, with display labels. A filter on an accent includes every accent under it. Keys are stable; labels may be reworded.
      operationId: VoiceModelsController_taxonomy
      parameters: []
      responses:
        '200':
          description: ''
      summary: 'The voice taxonomy: languages, accents (a hierarchy per language), genders…'
      tags:
      - Models
  /v1/models/mine:
    get:
      operationId: VoiceModelsController_mine
      parameters: []
      responses:
        '200':
          description: ''
      security:
      - bearer: []
      summary: Your models — per-user refs joined to catalog metadata (for the reader's Models…
      tags:
      - Models
  /v1/models/premium/usage:
    get:
      operationId: VoiceModelsController_premiumUsage
      parameters: []
      responses:
        '200':
          description: ''
      security:
      - bearer: []
      summary: 'Your premium-voice quota: used / limit / claimed voices (drives upgrade prompts)'
      tags:
      - Models
  /v1/models/support/rvc:
    get:
      description: 'Stable route in front of the catalog model that carries the bytes (RVC_SUPPORT_MODEL_SLUG). Free: no login needed.'
      operationId: VoiceModelsController_rvcSupport
      parameters: []
      responses:
        '200':
          description: ''
      security:
      - bearer: []
      summary: The RVC engine's support files (ContentVec, FCPE), presigned — what the desktop…
      tags:
      - Models
  /v1/models/support/pocket:
    get:
      description: 'Stable route in front of the catalog model that carries the bytes (POCKET_SUPPORT_MODEL_SLUG). Free: no login needed.'
      operationId: VoiceModelsController_pocketSupport
      parameters: []
      responses:
        '200':
          description: ''
      security:
      - bearer: []
      summary: The Pocket engine's shared ONNX bundle, presigned — what the desktop app…
      tags:
      - Models
  /v1/models/support/pocket-voices:
    get:
      description: 'What a desktop plugin fetches when its Pocket voices are asked for (TTS Synth bought on its own has no app). Free: no login needed.'
      operationId: VoiceModelsController_pocketVoices
      parameters: []
      responses:
        '200':
          description: ''
      summary: 'Pocket''s stock voices, presigned: one reference clip per voice, named for it'
      tags:
      - Models
  /v1/models/support/morpho-tones:
    get:
      description: 'What Ditto Morpho (and the app''s Morph page) fetches when its window opens: a plugin bought on its own has no app. Free: no login needed. 503 while no tone model is published.'
      operationId: VoiceModelsController_morphoTones
      parameters: []
      responses:
        '200':
          description: ''
      summary: 'Ditto Morpho''s stock tone models, presigned: one folder per model…'
      tags:
      - Models
  /v1/models/{slug}:
    get:
      operationId: VoiceModelsController_detail
      parameters:
      - name: slug
        required: true
        in: path
        schema:
          type: string
      - name: locale
        required: false
        in: query
        description: 'The site page''s language: a current translation of the description, else English (`descriptionLanguage` says which).'
        schema:
          type: string
      responses:
        '404':
          description: Not found / not published
      summary: Get a published model with its file list (no URLs)
      tags:
      - Models
  /v1/models/{slug}/samples/{sample}:
    get:
      description: The address every catalog `samples[].url` points at. Supports byte ranges and ETag revalidation; a request carrying the current `?v=` is cacheable for a year.
      operationId: VoiceModelsController_sample
      parameters:
      - name: slug
        required: true
        in: path
        schema:
          type: string
      - name: sample
        required: true
        in: path
        schema:
          type: string
      responses:
        '404':
          description: No such public voice or clip
      summary: Stream a pre-rendered preview clip
      tags:
      - Models
  /v1/models/{slug}/download:
    get:
      operationId: VoiceModelsController_download
      parameters:
      - name: slug
        required: true
        in: path
        schema:
          type: string
      responses:
        '429':
          description: 'More new voices from this network than its allowance: at most 2, 5, 10 new voices a minute, in 10 minutes and a day per client address (2x for a Ditto subscriber, whose session is read from its bearer token). A voice this address already downloaded in the last 24 hours is free, and an engine (a base bundle voices are built on, such as xtts-v2, kokoro-v1-0 or a pocket-<code>-v1 language model; the clone encoders; a tone model) is never counted. The body says `code: DOWNLOAD_LIMIT` and `retryAfterSec`; wait the `Retry-After` header (whole seconds) and ask again.'
          headers:
            Retry-After:
              description: Seconds until this network may download a new voice again.
              schema:
                type: integer
      security:
      - bearer: []
      summary: Get presigned download URLs for every file.
      tags:
      - Models
  /v1/models/{slug}/files/{fileId}/download:
    get:
      operationId: VoiceModelsController_downloadFile
      parameters:
      - name: slug
        required: true
        in: path
        schema:
          type: string
      - name: fileId
        required: true
        in: path
        schema:
          type: string
      responses:
        '429':
          description: 'More new voices from this network than its allowance: at most 2, 5, 10 new voices a minute, in 10 minutes and a day per client address (2x for a Ditto subscriber, whose session is read from its bearer token). A voice this address already downloaded in the last 24 hours is free, and an engine (a base bundle voices are built on, such as xtts-v2, kokoro-v1-0 or a pocket-<code>-v1 language model; the clone encoders; a tone model) is never counted. The body says `code: DOWNLOAD_LIMIT` and `retryAfterSec`; wait the `Retry-After` header (whole seconds) and ask again.'
          headers:
            Retry-After:
              description: Seconds until this network may download a new voice again.
              schema:
                type: integer
      security:
      - bearer: []
      summary: Get a presigned URL for one file.
      tags:
      - Models
  /v1/models/custom:
    post:
      operationId: CustomVoicesController_create
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCustomVoiceDto'
      responses:
        '201':
          description: ''
      security:
      - bearer: []
      summary: Create a custom XTTS voice and reserve latent upload URLs
      tags:
      - Models
  /v1/models/custom/{slug}/confirm:
    post:
      operationId: CustomVoicesController_confirm
      parameters:
      - name: slug
        required: true
        in: path
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfirmCustomVoiceDto'
      responses:
        '201':
          description: ''
      security:
      - bearer: []
      summary: Finalize a custom voice after its latents are uploaded
      tags:
      - Models
  /v1/models/custom/by-source/{sourceHash}:
    get:
      description: Returns the file manifest (names + content hashes) of the conversion the CALLER already made of this exact model, so the client can link the stored files instead of converting and uploading again. Never answers from another account's voice. 200 with {found:false} when unknown.
      operationId: CustomVoicesController_bySource
      parameters:
      - name: sourceHash
        required: true
        in: path
        schema:
          type: string
      responses:
        '200':
          description: ''
      security:
      - bearer: []
      summary: Look up YOUR earlier pth→onnx conversion of a source model.pth, by its sha-256
      tags:
      - Models
  /v1/models/custom/{slug}/download:
    get:
      operationId: CustomVoicesController_download
      parameters:
      - name: slug
        required: true
        in: path
        schema:
          type: string
      responses:
        '200':
          description: ''
      security:
      - bearer: []
      summary: 'Owner manifest: presigned latents + base voice slug/version'
      tags:
      - Models
  /v1/models/custom/{slug}:
    delete:
      operationId: CustomVoicesController_remove
      parameters:
      - name: slug
        required: true
        in: path
        schema:
          type: string
      responses:
        '200':
          description: ''
      security:
      - bearer: []
      summary: Delete a custom voice (blobs + row + ref)
      tags:
      - Models
  /v1/custom-voices/{id}:
    get:
      operationId: CustomVoicePagesController_detail
      parameters:
      - name: id
        required: true
        in: path
        schema:
          type: string
      responses:
        '404':
          description: No such voice, or not yours
      security:
      - bearer: []
      summary: 'One of your custom voices: metadata, files and preview clips'
      tags:
      - Models
    patch:
      description: 'Every field is optional: one left out stays, a null clears it. Accent, styles and use cases are words of the voice taxonomy (GET /v1/models/taxonomy: keys, labels or aliases), stored as keys; a word that names nothing is a 400 that says which. Every voice list filters your voices by what you say here.'
      operationId: CustomVoicePagesController_update
      parameters:
      - name: id
        required: true
        in: path
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateCustomVoiceDto'
      responses:
        '404':
          description: No such voice, or not yours
      security:
      - bearer: []
      summary: 'Describe one of your custom voices: name, description, language, accent…'
      tags:
      - Models
    delete:
      operationId: CustomVoicePagesController_remove
      parameters:
      - name: id
        required: true
        in: path
        schema:
          type: string
      responses:
        '200':
          description: ''
      security:
      - bearer: []
      summary: Delete one of your custom voices (row, account ref, private files)
      tags:
      - Models
  /v1/custom-voices/{id}/samples/{sample}:
    get:
      operationId: CustomVoicePagesController_sample
      parameters:
      - name: id
        required: true
        in: path
        schema:
          type: string
      - name: sample
        required: true
        in: path
        schema:
          type: string
      responses:
        '200':
          description: ''
      security:
      - bearer: []
      summary: Stream one of your custom voice's preview clips (owner-only)
      tags:
      - Models
    put:
      description: 'Custom voices are never rendered server-side: the page synthesizes the canonical snippet in the browser and uploads the WAV here (field "file"), once. From then on it streams from GET :id/samples/:sample on every device. `sample` is intro, passage or cozy; the body must be PCM WAV of at most 3 minutes.'
      operationId: CustomVoicePagesController_savePreview
      parameters:
      - name: id
        required: true
        in: path
        schema:
          type: string
      - name: sample
        required: true
        in: path
        schema:
          type: string
      responses:
        '200':
          description: ''
      security:
      - bearer: []
      summary: Save a preview clip you rendered on your own device (owner-only)
      tags:
      - Models
  /v1/custom-voices/{id}/download:
    get:
      operationId: CustomVoicePagesController_download
      parameters:
      - name: id
        required: true
        in: path
        schema:
          type: string
      responses:
        '200':
          description: ''
      security:
      - bearer: []
      summary: 'Owner manifest: presigned files + the base voice it overlays'
      tags:
      - Models
  /v1/models/imported:
    get:
      operationId: ImportedVoicesController_list
      parameters: []
      responses:
        '200':
          description: ''
      security:
      - bearer: []
      summary: List the user's URL-imported voices
      tags:
      - Models
    post:
      description: Stores only the source URL, the resolved file list and provenance. Re-posting an existing sourceUrl refreshes that record instead of creating a duplicate.
      operationId: ImportedVoicesController_create
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateImportedVoiceDto'
      responses:
        '201':
          description: ''
      security:
      - bearer: []
      summary: Register a voice imported from a URL
      tags:
      - Models
  /v1/models/imported/{id}:
    get:
      operationId: ImportedVoicesController_get
      parameters:
      - name: id
        required: true
        in: path
        schema:
          type: string
      responses:
        '200':
          description: ''
      security:
      - bearer: []
      summary: One imported voice
      tags:
      - Models
    patch:
      operationId: ImportedVoicesController_update
      parameters:
      - name: id
        required: true
        in: path
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateImportedVoiceDto'
      responses:
        '200':
          description: ''
      security:
      - bearer: []
      summary: Rename, or refresh the cached file list after URLs expire
      tags:
      - Models
    delete:
      operationId: ImportedVoicesController_remove
      parameters:
      - name: id
        required: true
        in: path
        schema:
          type: string
      responses:
        '200':
          description: ''
      security:
      - bearer: []
      summary: Remove an imported voice (soft delete)
      tags:
      - Models
components:
  schemas:
    ConfirmCustomVoiceDto:
      type: object
      properties:
        files:
          description: the uploaded files (s3Key + sha256)
          type: array
          items:
            $ref: '#/components/schemas/ConfirmUploadDto'
      required:
      - files
    ImportedVoiceFileDto:
      type: object
      properties:
        fileName:
          type: string
          example: model.pth
        role:
          type: string
          example: model
          description: model | index | config | sidecar | other
        sizeBytes:
          type: number
          example: 55019812
        url:
          type: string
          description: Direct byte URL (may expire; sourceUrl is the fallback).
        md5:
          type: string
          description: Upstream md5, when the source published one.
        insideArchive:
          type: object
          description: 'Set when the file is a member of a zip: { archiveUrl, memberPath }.'
      required:
      - fileName
      - role
    UpdateCustomVoiceDto:
      type: object
      properties:
        name:
          type: string
          example: Grandma, bedtime
        description:
          type:
          - object
          - 'null'
        language:
          type:
          - object
          - 'null'
          example: es-MX
          description: The language the voice was recorded in (a BCP 47 tag).
        accent:
          type:
          - object
          - 'null'
          example: es-rioplatense
        gender:
          type:
          - string
          - 'null'
          enum:
          - female
          - male
          - neutral
          - null
        age:
          type:
          - string
          - 'null'
          enum:
          - young
          - middle-aged
          - older
          - null
        styles:
          type: array
          items:
            type: string
        useCases:
          type: array
          items:
            type: string
    CreateCustomVoiceDto:
      type: object
      properties:
        name:
          type: string
          example: My Cloned Narrator
        engine:
          type: string
          enum:
          - piper
          - xtts
          - kokoro
          - pocket
          - rvc
          - system
          - morpho
          description: xtts (default) or rvc. RVC voices persist model.onnx + model.pth.
        sourceHash:
          type: string
          description: sha-256 of the source model.pth (for an in-browser pth→onnx voice). Stored so a future client can skip re-converting via GET /models/custom/by-source/:hash.
        baseSlug:
          type: string
          example: xtts-v2
          description: base XTTS voice to overlay (omit for a full self-contained bundle)
        files:
          description: the files to upload (full-bundle mode)
          type: array
          items:
            $ref: '#/components/schemas/CustomVoiceFileDto'
        language:
          type: string
          example: en
        sampleRate:
          type: number
          example: 24000
        description:
          type: string
          example: My grandmother reading bedtime stories
        gender:
          type: string
          enum:
          - female
          - male
          - neutral
        age:
          type: string
          enum:
          - young
          - middle-aged
          - older
        accent:
          type: string
          example: en-scottish
        styles:
          example:
          - warm
          type: array
          items:
            type: string
        useCases:
          example:
          - narration
          type: array
          items:
            type: string
      required:
      - name
    UpdateImportedVoiceDto:
      type: object
      properties:
        name:
          type: string
          example: My better name
        files:
          description: Refreshed file list after a re-resolve (URLs expire).
          type: array
          items:
            type: string
    CreateImportedVoiceDto:
      type: object
      properties:
        sourceUrl:
          type: string
          example: https://voice-models.com/model/9Pk
        name:
          type: string
          example: Mencho Wey 200 (Mexico)
        engine:
          type: string
          example: rvc
          default: rvc
        files:
          type: array
          items:
            $ref: '#/components/schemas/ImportedVoiceFileDto'
        sourceMeta:
          type: object
          description: 'Provenance: page title/author/license/pageUrl.'
        via:
          description: Locator chain that resolved the URL.
          type: array
          items:
            type: string
        sampleRate:
          type: number
          example: 40000
        encoderBaseSlug:
          type: string
          description: Catalog slug whose HuBERT/RMVPE encoders this import overlays.
        hasIndex:
          type: boolean
          description: Whether a FAISS/npy retrieval index came with the model.
      required:
      - sourceUrl
      - name
    CustomVoiceFileDto:
      type: object
      properties:
        fileName:
          type: string
          example: xtts_gpt.onnx
        role:
          type: string
          enum:
          - model
          - config
          - vocab
          - speakers
          - index
          - sample
          - archive
          - other
        contentHash:
          type: string
          description: sha-256 of the file — drives dedup (skip re-upload if present)
        sizeBytes:
          type: number
      required:
      - fileName
      - contentHash
    ConfirmUploadDto:
      type: object
      properties:
        s3Key:
          type: string
          description: s3Key returned by the presign call
        sha256:
          type: string
          description: hex sha-256 for integrity
      required:
      - s3Key
  securitySchemes:
    bearer:
      scheme: bearer
      bearerFormat: ditto_sk_live_… API key, ditto_oat_… OAuth access token, or a session JWT
      type: http
      description: An API key (`ditto_sk_live_…`, made at https://ditto.audio/account/api-keys) authenticates /v1/audio/* and /v1/reader/*; an OAuth connection's one-hour access token (`ditto_oat_…`) holds the audio:read and audio:speech scopes only. Account routes (/v1/api-keys, /v1/usage, /v1/subscription) take the signed-in session's token (a JWT) only.