{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/ditto-audio/json-schema/ditto-audio-create-audio-batch-dto-schema.json",
  "title": "CreateAudioBatchDto",
  "x-generated": "2026-11-05",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/ditto-audio-openapi.json#/components/schemas/CreateAudioBatchDto",
  "type": "object",
  "properties": {
    "model": {
      "type": "string",
      "enum": [
        "kokoro",
        "xtts",
        "pocket"
      ],
      "description": "Default model for every item that doesn’t set its own."
    },
    "voice": {
      "type": "string",
      "description": "Default voice for every item that doesn’t set its own. With no voice anywhere (or `default` / `auto` / a language tag), each item gets the voice the server picks for its language, as POST speech does."
    },
    "gender": {
      "type": "string",
      "enum": [
        "female",
        "male",
        "neutral"
      ],
      "description": "With no voice named: prefer a voice of this gender."
    },
    "language": {
      "type": "string",
      "description": "Default language. Omit to infer per item from its own text; text with no clear sign of its language is read in the item voice’s own."
    },
    "speed": {
      "type": "number",
      "default": 1,
      "minimum": 0.5,
      "maximum": 2
    },
    "response_format": {
      "type": "string",
      "enum": [
        "mp3",
        "mp3_64",
        "mp3_160",
        "wav",
        "wav24",
        "wav32f",
        "flac",
        "flac24",
        "pcm",
        "pcm_f32",
        "aiff",
        "caf",
        "au",
        "mulaw",
        "alaw"
      ],
      "default": "mp3",
      "description": "Default audio encoding for every item that doesn’t set its own. Every item is a COMPLETE file — real sizes in its header, not the streaming placeholder — so the result is directly playable and directly saveable. Hi-fidelity formats bill at 1.2× the normal rate, multiplied by the model’s own weighting — the audio is identical, but the bytes are several times larger. `mp3` (the default), `mp3_64`, `mulaw` and `alaw` are unchanged at 1×. Each format’s exact multiplier is in GET /v1/audio/format and in the `X-Ditto-Audio-Cost-Multiplier` response header."
    },
    "pauses": {
      "description": "Default for every item; an item’s own `pauses` override it field by field. Pauses placed between engine calls, in milliseconds. Long text is synthesized in several calls (by sentence: about 120 characters a call for xtts and pocket, 60 for Pocket Japanese and 75 for Pocket Korean; one sentence a call for kokoro) and joined; inside a call the model places every pause itself. Omit a field (or the whole object) and that join gets the pause the model itself leaves at that kind of boundary, scaled by `speed`. A value you set is exact: not scaled by `speed`. The pause is what a listener hears between the two words: whatever silence each side already ends or starts with counts toward it. The clip also ends with its last boundary's pause, so clips can be concatenated. Defaults at speed 1 (ms): `xtts`: sentence 540 (460 after ?, 500 after !), clause 330 (after a comma), paragraph 810; `kokoro`: sentence 430 (420 after ?, 440 after !), clause 150 (after a comma), paragraph 640; `pocket`: sentence 770 (850 after ?, 670 after !), clause 240 (after a comma), paragraph 1160.",
      "allOf": [
        {
          "$ref": "#/$defs/PausesDto"
        }
      ]
    },
    "items": {
      "maxItems": 1000,
      "description": "Up to 1000 items. They are accepted, persisted and worked through in the background — the response comes back immediately with a batch id, not with audio.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/SpeechBatchItemDto"
      }
    },
    "metadata": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      },
      "description": "Opaque string map stored with the batch and returned on every read. We never interpret it — it exists so you can correlate a batch with whatever created it without keeping your own side table."
    },
    "on_invalid_item": {
      "type": "string",
      "enum": [
        "fail_item",
        "refuse_batch"
      ],
      "default": "fail_item",
      "description": "What an item whose voice or language cannot be used does to the batch. `fail_item` (the default): the batch is created and that item fails on its own, with its `errorCode` (`UNKNOWN_VOICE`, `UNKNOWN_LANGUAGE`, `UNSUPPORTED_LANGUAGE`, `VOICE_NOT_HOSTED`, `VOICE_MODEL_MISMATCH`). `refuse_batch`: nothing is created; the answer is a 400 with that code, its message naming the item (`items[3]: Unknown voice ...`, with the ids it looks like), so it can be fixed and the batch sent again."
    },
    "wait_ms": {
      "type": "number",
      "minimum": 0,
      "maximum": 30000,
      "default": 0,
      "description": "Wait up to this many milliseconds (max 30000) for the batch to finish before answering. A batch that finishes in time, with at most 20 items, is answered WITH its `items` (presigned URLs included), so one short synthesis costs one request. Otherwise the batch is returned as it stands; poll with `GET /v1/audio/batches/{id}?wait_ms=`."
    }
  },
  "required": [
    "items"
  ],
  "$defs": {
    "PausesDto": {
      "type": "object",
      "properties": {
        "sentence": {
          "type": "number",
          "minimum": 0,
          "maximum": 5000,
          "description": "Milliseconds after a sentence (. ! ? …). Setting it sends every sentence as its own engine call, so EVERY sentence boundary gets exactly this pause instead of the model’s own."
        },
        "clause": {
          "type": "number",
          "minimum": 0,
          "maximum": 5000,
          "description": "Milliseconds where a sentence too long for one engine call is cut at a comma, semicolon, colon or dash. Commas inside a call are paced by the model and are not affected."
        },
        "paragraph": {
          "type": "number",
          "minimum": 0,
          "maximum": 5000,
          "description": "Milliseconds at a blank line in `input`."
        }
      }
    },
    "SpeechBatchItemDto": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "Your own correlation id, echoed back on this item’s result. Never interpreted by us and never required to be unique — results also carry `index`, which always identifies the item unambiguously."
        },
        "model": {
          "type": "string",
          "enum": [
            "kokoro",
            "xtts",
            "pocket"
          ],
          "description": "Overrides the batch default for this item."
        },
        "voice": {
          "type": "string",
          "description": "Overrides the batch default for this item. Omit both and the server picks the voice for the item’s language."
        },
        "gender": {
          "type": "string",
          "enum": [
            "female",
            "male",
            "neutral"
          ],
          "description": "Overrides the batch default for this item: with no voice named, prefer this gender."
        },
        "input": {
          "type": "string",
          "maxLength": 8000,
          "description": "The text to speak. The only required field."
        },
        "language": {
          "type": "string",
          "description": "Overrides the batch default for this item. See GET /v1/audio/languages."
        },
        "speed": {
          "type": "number",
          "minimum": 0.5,
          "maximum": 2,
          "description": "Overrides the batch default for this item."
        },
        "response_format": {
          "type": "string",
          "enum": [
            "mp3",
            "mp3_64",
            "mp3_160",
            "wav",
            "wav24",
            "wav32f",
            "flac",
            "flac24",
            "pcm",
            "pcm_f32",
            "aiff",
            "caf",
            "au",
            "mulaw",
            "alaw"
          ],
          "description": "Overrides the batch default for this item — each result is its own complete file, so a batch may mix formats freely. Billed at this item’s own rate."
        },
        "pauses": {
          "description": "Overrides the batch default for this item, field by field. Pauses placed between engine calls, in milliseconds. Long text is synthesized in several calls (by sentence: about 120 characters a call for xtts and pocket, 60 for Pocket Japanese and 75 for Pocket Korean; one sentence a call for kokoro) and joined; inside a call the model places every pause itself. Omit a field (or the whole object) and that join gets the pause the model itself leaves at that kind of boundary, scaled by `speed`. A value you set is exact: not scaled by `speed`. The pause is what a listener hears between the two words: whatever silence each side already ends or starts with counts toward it. The clip also ends with its last boundary's pause, so clips can be concatenated. Defaults at speed 1 (ms): `xtts`: sentence 540 (460 after ?, 500 after !), clause 330 (after a comma), paragraph 810; `kokoro`: sentence 430 (420 after ?, 440 after !), clause 150 (after a comma), paragraph 640; `pocket`: sentence 770 (850 after ?, 670 after !), clause 240 (after a comma), paragraph 1160.",
          "allOf": [
            {
              "$ref": "#/$defs/PausesDto"
            }
          ]
        }
      },
      "required": [
        "input"
      ]
    }
  }
}