{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/ditto-audio/json-schema/ditto-audio-speech-batch-dto-schema.json",
  "title": "SpeechBatchDto",
  "x-generated": "2026-11-05",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/ditto-audio-openapi.json#/components/schemas/SpeechBatchDto",
  "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": 64,
      "description": "Up to 64 items, answered on this one connection. For more than that (or for work you don’t want to hold a socket open for), create an async job with POST /v1/audio/batches instead.",
      "type": "array",
      "items": {
        "$ref": "#/$defs/SpeechBatchItemDto"
      }
    },
    "stream": {
      "type": "boolean",
      "default": true,
      "description": "true (default): `application/x-ndjson` — one JSON object per line, written the moment that item finishes, so you can start using results before the batch is done. false: one buffered `application/json` envelope `{ results, summary }` with results in REQUEST order."
    }
  },
  "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"
      ]
    }
  }
}