Realtime TTS-2 is live. Built for realtime conversation that feels human. Read the Realtime TTS-2 announcement

Capabilities

Script synthesis

Generate dialogue or a monologue in one request, with combined or separate utterance audio

A script is an ordered list of utterances, each with text and a stored voice. One request can produce a conversation between speakers or a monologue with one voice. TTS-2 keeps a bounded audio history within the script to help later utterances follow earlier turns naturally. Separate requests do not share this history.

Scripts work with the unary, HTTP streaming, async, and batch APIs. This request shape does not apply to WebSocket or OpenAI-compatible synthesis. Async and batch remain preview APIs.

Build a script

Send script instead of the top-level text, voiceId, or voiceDesign. Use modelId: "inworld-tts-2". Every utterance needs its own text and voiceConfig.voiceId; inline voice design is not supported inside a script.

bash
curl --request POST 'https://api.inworld.ai/tts/v1/voice' \
  --header "Authorization: Basic $INWORLD_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
    "modelId": "inworld-tts-2",
    "language": "en-US",
    "audioConfig": {"audioEncoding": "MP3", "speakingRate": 1.0},
    "timestampType": "WORD",
    "outputMode": "PER_UTTERANCE",
    "script": {
      "voices": [
        {"voiceId": "Dennis", "speakingRate": 0.9, "deliveryMode": "STABLE"}
      ],
      "utterances": [
        {"text": "Welcome to the observatory.", "voiceConfig": {"voiceId": "Dennis"}},
        {"text": "The telescope is ready for tonight.", "voiceConfig": {"voiceId": "Ashley"}},
        {"text": "Let us look at the stars.", "voiceConfig": {"voiceId": "Dennis", "speakingRate": 1.1}}
      ]
    }
  }'

script.voices is an optional list of defaults keyed by unique voiceId. It does not declare speakers or replace voiceConfig.voiceId on each utterance. For speakingRate and deliveryMode, an utterance override takes precedence over its voice defaults, which take precedence over the request settings. In this example, Dennis's first turn uses rate 0.9, Ashley inherits 1.0, and Dennis's last turn overrides the rate to 1.1 while retaining STABLE delivery.

Scripts accept 1–100 utterances. The endpoint's text limit applies to the sum of all utterance texts, measured in UTF-16 code units: 2,000 for unary and 4,000 for HTTP streaming; async and batch use their existing job limits. Splitting text into more utterances does not increase the limit. Usage counts the text across the script once.

Audio encoding, sample rate, language, model, and timestamp type are shared request settings. Per-utterance controls are voiceId, speakingRate (0.5–1.5), and deliveryMode.

Request fields

scriptobject

Ordered speech utterances for TTS-2. Replaces the request's top-level text, voiceId, and voiceDesign. Bounded audio history carries between utterances within this request. See script synthesis for examples and output formats.

Show child attributes — script fields

utterancesobject[]required

Between 1 and 100 utterances, generated in array order. The endpoint's text limit applies to the sum of their texts, measured in UTF-16 code units.

Show child attributes — utterance fields

textstringrequired

Text to synthesize for this utterance.

voiceConfigobjectrequired

Stored voice and optional delivery overrides for this utterance.

Show child attributes — voiceConfig fields

voiceIdstringrequired

Stored voice to use for this utterance, or the key for an entry in script.voices. Required even when voice defaults are supplied. Inline voice design is not supported inside scripts.

speakingRatenumber

Speaking rate from 0.5 to 1.5. An utterance's value overrides its matching script.voices entry, which overrides audioConfig.speakingRate on the request.

deliveryModeenum

Delivery mode: STABLE, BALANCED, or CREATIVE. An utterance's value overrides its matching script.voices entry, which overrides the request's deliveryMode. Omit this field to inherit. An explicit DELIVERY_MODE_UNSPECIFIED selects BALANCED behavior.

voicesobject[]

Optional defaults keyed by unique voiceId. Each utterance still requires its own voiceConfig.voiceId.

Show child attributes — voice default fields

voiceIdstringrequired

Stored voice to use for this utterance, or the key for an entry in script.voices. Required even when voice defaults are supplied. Inline voice design is not supported inside scripts.

speakingRatenumber

Speaking rate from 0.5 to 1.5. An utterance's value overrides its matching script.voices entry, which overrides audioConfig.speakingRate on the request.

deliveryModeenum

Delivery mode: STABLE, BALANCED, or CREATIVE. An utterance's value overrides its matching script.voices entry, which overrides the request's deliveryMode. Omit this field to inherit. An explicit DELIVERY_MODE_UNSPECIFIED selects BALANCED behavior.

outputModeenum

COMBINED (default) returns one recording. PER_UTTERANCE returns separate utterance recordings and requires script. Both modes retain the same generation history. Async and batch per-utterance results require ZIP packaging.

Choose the output

outputMode controls whether the service returns one combined recording or separate recordings. Omitting it selects COMBINED.

EndpointCOMBINEDPER_UTTERANCE
UnaryTop-level audioContent and optional timestampInfoComplete clips in scriptResult.utterances[]
HTTP streamingTop-level audio and timestamps on one timelineTop-level audio and timestamps tagged with scriptResult.utteranceIndex
AsyncExisting single-recording resultscriptResult.utterances[] in the ZIP results document
BatchExisting single-recording result per itemscriptResult.utterances[] on each script item in the ZIP results document

scriptResult is omitted for combined output. With separate recordings, timestamps are relative to the start of each utterance's audio; combined output uses one cumulative timeline. Separate output requires a script and does not also produce a combined recording.

Unary response

For PER_UTTERANCE, decode each entry's audioContent as its own complete recording. Indexes are zero-based positions in the request's script.utterances array. The following excerpt omits usage and timestamp details, and uses illustrative base64 placeholders:

json
{
  "scriptResult": {
    "utterances": [
      {"utteranceIndex": 0, "audioContent": "<base64 recording 0>"},
      {"utteranceIndex": 1, "audioContent": "<base64 recording 1>"},
      {"utteranceIndex": 2, "audioContent": "<base64 recording 2>"}
    ]
  }
}

When requested, alignment belongs to each entry's timestampInfo. Top-level audioContent is empty; a top-level timestamp object may contain empty alignment arrays and is not the script's alignment. Read the top-level usage for the whole request.

HTTP streaming response

Send the same body to /tts/v1/voice:stream. The endpoint returns newline-delimited JSON, with each response inside result. In PER_UTTERANCE mode, audio stays in result.audioContent; the wrapper supplies the index:

json
{"result":{"audioContent":"<first chunk of utterance 0>","scriptResult":{"utteranceIndex":0}}}
{"result":{"audioContent":"<next chunk of utterance 0>","scriptResult":{"utteranceIndex":0}}}
{"result":{"audioContent":"<first chunk of utterance 1>","scriptResult":{"utteranceIndex":1}}}

The excerpt omits timestamps and usage. Group audio and timestamp updates by index. Base64-decode each audio chunk separately before concatenating its bytes. MP3 chunks for one index form one recording; follow the audio format rules for other encodings.

Indexes arrive in order. An increment means all audio and timestamp updates for the previous utterance have finished. Successful end-of-stream completes the last utterance; an error or disconnected stream does not. There is no separate utterance-completed event. An utterance with no audio still emits its index, so do not require an audio chunk on every response. Timestamp-only updates also belong to their indicated index.

Async results

Async and batch use the same packaging rules:

ResultsPackaging
Hosted, COMBINEDFILES by default; ZIP optional
Hosted, any PER_UTTERANCE outputExplicit ZIP required
Delivered to outputConfig.resultsUriZIP required and selected by default, regardless of output mode

For /tts/v1/voice:synthesizeAsync with PER_UTTERANCE, add "outputConfig": {"packaging": "ZIP"} to the script request. See results delivery for destination requirements.

Poll the returned operation as described in async synthesis. On success, its response.resultsUri points to the ZIP and response.resultsPath names the results document inside it. The operation does not inline the utterance list.

For the three-turn example, the results document contains the following structure. Other result fields are omitted:

json
{
  "scriptResult": {
    "utterances": [
      {"utteranceIndex": 0, "audioPath": "utterances/0000/audio.mp3", "timestampsPath": "utterances/0000/timestamps.json"},
      {"utteranceIndex": 1, "audioPath": "utterances/0001/audio.mp3", "timestampsPath": "utterances/0001/timestamps.json"},
      {"utteranceIndex": 2, "audioPath": "utterances/0002/audio.mp3", "timestampsPath": "utterances/0002/timestamps.json"}
    ]
  }
}

Paths are relative to the results document inside the archive. Follow the manifest's paths instead of constructing filenames. Each audio file is independently playable. Timestamp files contain a JSON array of alignment updates and are present only when timestamps were requested.

Batch results

Place each script request inside an item's request, and set outputConfig once at the batch root:

json
{
  "outputConfig": {"packaging": "ZIP"},
  "items": [
    {
      "customId": "observatory-dialogue",
      "request": {
        "modelId": "inworld-tts-2",
        "audioConfig": {"audioEncoding": "MP3"},
        "outputMode": "PER_UTTERANCE",
        "script": {
          "utterances": [
            {"text": "Welcome to the observatory.", "voiceConfig": {"voiceId": "Dennis"}},
            {"text": "The telescope is ready for tonight.", "voiceConfig": {"voiceId": "Ashley"}}
          ]
        }
      }
    },
    {
      "customId": "closing-narration",
      "request": {"modelId": "inworld-tts-2", "voiceId": "Dennis", "text": "Thank you for visiting."}
    }
  ]
}

A batch can mix scripts and ordinary single-voice requests. Hosted combined batches can use FILES; any PER_UTTERANCE item or delivery to your storage requires ZIP. Each script is generated independently; audio history is not shared between batch items.

After the batch operation completes, download response.resultsUri and open the file named by response.resultsPath inside the ZIP. Each successful script item has its own wrapper, while combined items retain their existing shape. This results excerpt corresponds to the request above:

json
{
  "completedItems": 2,
  "failedItems": 0,
  "results": [
    {
      "customId": "observatory-dialogue",
      "scriptResult": {
        "utterances": [
          {"utteranceIndex": 0, "audioPath": "items/0/utterances/0000/audio.mp3"},
          {"utteranceIndex": 1, "audioPath": "items/0/utterances/0001/audio.mp3"}
        ]
      }
    },
    {"customId": "closing-narration", "audioPath": "items/1.mp3"}
  ]
}

With timestamps enabled on a script item, each utterance also has a timestampsPath. Inspect item errors and the success/failure counts: a completed batch operation does not mean every item succeeded. A failed script item does not return a successful prefix of its utterances.