> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inworld.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Synthesize speech (WebSocket)

> Synthesize speech for many independent contexts over one WebSocket connection. Each context has its own voice, settings and text, and every response names its contextId.

Each synthesis in a context uses the earlier ones to carry the voice's delivery from one to the next. Set `autoMode: true` in `create` to let the service manage the context's history, so explicit flushes are unnecessary. It keeps fewer, longer turns for smoother prosody.

With the default strategy, `CLIENT_SEGMENTED`, each `sendText` is expected to contain a complete sentence or phrase. Your client controls where sentences break. With `SENTENCE_BOUNDARY` (Preview, `inworld-tts-2` and `inworld-tts-2-flash`), send tokens one at a time as the LLM response streams. The server handles sentence segmentation.

See [Choose how text is buffered](https://docs.inworld.ai/tts/synthesize-speech-websocket.md#choose-how-text-is-buffered) for details. The [WebSocket guides for conversational agents](https://github.com/inworld-ai/inworld-api-examples/tree/main/tts/python/websocket_guides) include runnable Python clients.

For connection and concurrency limits, see [Rate limits](https://docs.inworld.ai/resources/rate-limits.md).

## API reference

**Channel:** `WSS wss://api.inworld.ai/tts/v1/voice:streamBidirectional`

### Client messages

#### Create Context

Create a new context with specified voice and configuration. A context is an independent conversation happening over the connection. The configurations for each context are completely separate – you can have different voice ids, models, output formats, etc. between contexts. See [Rate limits](https://docs.inworld.ai/resources/rate-limits.md) for connection and concurrency limits. If you don't need multiple contexts, you can omit the contextId in the message to use a single context connection.

- `create` (object; required)
  - `voiceId` (string; required) — The identifier of the voice to use for the synthesis
  - `modelId` (string; required) — The ID of the model to use for synthesizing speech. See [Models](../../../tts/tts-models) for available models.
  - `audioConfig` (object)
    - `audioEncoding` (enum<string>; default: "MP3"; options: "AUDIO_ENCODING_UNSPECIFIED", "LINEAR16", "MP3", "OGG_OPUS", "ALAW", "MULAW", "PCM", "WAV") — The desired output format of the synthesized audio. Defaults to `MP3`.
       - `LINEAR16`: Uncompressed 16-bit signed little-endian samples (Linear PCM). The WAV header is included in every audio chunk.
       - `MP3`: MP3 audio.
       - `OGG_OPUS`: Opus encoded audio wrapped in an ogg container. The result will be a file which can be played natively on Android, and in browsers (at least Chrome and Firefox). The quality of the encoding is considerably higher than MP3 while using approximately the same bitrate.
       - `ALAW`: ALAW encoded audio. 8-bit companded PCM.
       - `MULAW`: MULAW encoded audio. 8-bit companded PCM.
       - `PCM`: PCM audio. Uncompressed 16-bit signed little-endian samples with no WAV header.
       - `WAV`: WAV audio. Uncompressed 16-bit signed little-endian samples. The WAV header is included in the first audio chunk only. On each `flushCompleted` response, the next audio chunk will also start with a header.
    - `sampleRateHertz` (integer) — The synthesis sample rate (in hertz) for this audio. Accepts values within the range [8000, 48000].
      
      If you request a supported rate that differs from the model's native output rate, the service converts the audio to that rate. Conversion may reduce audio quality. The request fails if the chosen encoding does not support the rate, or if the rate exceeds a known native output rate.
      
      When omitted, the default is 8,000 Hz for `MULAW` and `ALAW`. For other encodings, it is 48,000 Hz, capped at the model's native output rate when known.
    - `bitRate` (integer) — Bits per second of the audio. Only for compressed audio formats (`MP3`, `OGG_OPUS`). The default is 128,000.
    - `speakingRate` (number) — Speaking rate/speed, in the range [0.5, 1.5]. The default is 1.0, which is the normal native speed supported by the specific voice. We recommend using values above 0.8 to ensure high quality.
  - `temperature` (number) — *Ignored on `inworld-tts-2`. Use [`deliveryMode`](#deliveryMode) instead.*
    
    Determines the degree of randomness when sampling audio tokens to generate the response.
    
    Defaults to 1.0. Accepts values between 0 (exclusive) and 2 (inclusive). Higher values will make the output more random and can lead to more expressive results. Lower values will make it more deterministic. If 0 is provided, the default value will be used.
    
    For the most stable results, we recommend using the default value.
  - `timestampType` (enum<string>; default: "TIMESTAMP_TYPE_UNSPECIFIED"; options: "TIMESTAMP_TYPE_UNSPECIFIED", "WORD", "CHARACTER") — Controls timestamp metadata returned with the audio. When enabled, the response includes timing arrays, which can be useful for word-highlighting, karaoke-style captions, and lipsync.
    
    - WORD: Output arrays under `timestampInfo.wordAlignment` (words, wordStartTimeSeconds, wordEndTimeSeconds).
    - CHARACTER: Output arrays under `timestampInfo.characterAlignment` (characters, characterStartTimeSeconds, characterEndTimeSeconds).
    - `TIMESTAMP_TYPE_UNSPECIFIED`: Do not compute alignment; timestamp arrays will be empty or omitted.
    
    **Latency note:** Alignment adds additional computation. Enabling alignment can increase latency.
    
    The timestamps reset per flush, either triggered manually or automatically by the server. When you receive a `flushCompleted` message, the timestamps for subsequent chunks will start from 0.
  - `maxBufferDelayMs` (integer) — Manual-mode buffer delay in milliseconds. Applies only when autoMode is false; auto mode ignores this field. The timer starts with the first buffered text and is canceled when that buffer is flushed. Omission or 0 disables the timer. In manual mode, an explicit flush, the size threshold, or the timer can trigger synthesis. In `SENTENCE_BOUNDARY` mode, explicitly flush or close to release an unfinished tail.
  - `bufferCharThreshold` (integer) — Advanced buffering safeguard for unusually long input. Most applications should leave this unset and use explicit flushes or autoMode to control synthesis. Omission or 0 selects 1000; accepted values range from 0 to 2000. In `SENTENCE_BOUNDARY` mode, this safeguard can force a cut without sentence-ending punctuation, preferring word boundaries. Protected markup stays intact, subject to the API text safety limit.
  - `applyTextNormalization` (enum<string>; default: "APPLY_TEXT_NORMALIZATION_UNSPECIFIED"; options: "APPLY_TEXT_NORMALIZATION_UNSPECIFIED", "ON", "OFF") — When enabled, text normalization automatically expands and standardizes things like numbers, dates, times, and abbreviations before converting them to speech. For example, Dr. Smith becomes Doctor Smith, and 3/10/25 is spoken as March tenth, twenty twenty-five. Turning this off may reduce latency, but the speech output will read the text exactly as written. Defaults to automatically deciding whether to apply text normalization.
  - `autoMode` (boolean; default: false) — Enable server-controlled flushing and batching of buffered text. Use autoModeStrategy to choose between complete client-supplied sentences/phrases (`CLIENT_SEGMENTED`, the default strategy) and incremental text segmented by the server (`SENTENCE_BOUNDARY`). Defaults to false: explicitly flush or close when your text is ready. Buffering guardrails still apply.
  - `autoModeStrategy` (enum<string>; default: "AUTO_MODE_STRATEGY_UNSPECIFIED"; options: "AUTO_MODE_STRATEGY_UNSPECIFIED", "SENTENCE_BOUNDARY", "CLIENT_SEGMENTED") — Select the auto-mode strategy when creating a context. Ignored when autoMode is false.
    
    - `CLIENT_SEGMENTED`: Send complete sentences or phrases. The server controls flushing and can combine queued text while earlier audio streams.
    - `SENTENCE_BOUNDARY` (Preview): Send incremental text, including individual LLM tokens. The server admits text at sentence and complete-break boundaries, or at the bufferCharThreshold size limit. Explicitly flush or close to release remaining text; maxBufferDelayMs is ignored. Supported by inworld-tts-2 and inworld-tts-2-flash where available; unsupported models return `INVALID_ARGUMENT` when auto mode is enabled.
    - `AUTO_MODE_STRATEGY_UNSPECIFIED` or omission: Uses `CLIENT_SEGMENTED`, preserving existing auto-mode behavior.
  - `timestampTransportStrategy` (enum<string>; default: "TIMESTAMP_TRANSPORT_STRATEGY_UNSPECIFIED"; options: "TIMESTAMP_TRANSPORT_STRATEGY_UNSPECIFIED", "SYNC", "ASYNC") — How word and character timestamps are delivered. We recommend setting it explicitly rather than relying on the default.
    
    - `ASYNC`: Timestamps can arrive in a trailing message after their audio. The lowest-latency option.
    - `SYNC`: Timestamps arrive in the same message as their audio. Each message waits until its whole words are aligned, which adds latency.
    - `TIMESTAMP_TRANSPORT_STRATEGY_UNSPECIFIED`: `ASYNC` when `lipsyncConfig` is set, since lip-sync implies a real-time experience; otherwise `SYNC`. The default is expected to move to `ASYNC`.
    
    Visemes in `lipsyncAlignment` always arrive with their audio, whichever strategy is used.
  - `language` (string) — BCP-47 language tag (e.g., `en-US`, `fr-FR`, `ja-JP`) specifying the language that the given voice should speak the text in. If a localized voice prompt exists for the language, it will be used. When omitted, the original voice prompt will be used and the language will be auto-detected from the input text. If an invalid language code is provided, an error will be returned.
    
    See [Languages](../../../tts/capabilities/multilingual) for more details.
  - `deliveryMode` (enum<string>; default: "DELIVERY_MODE_UNSPECIFIED"; options: "DELIVERY_MODE_UNSPECIFIED", "STABLE", "BALANCED", "CREATIVE") — *Only supported by `inworld-tts-2`. The field is ignored on other models.*
    
    Controls how varied the output is. 
    
    - `DELIVERY_MODE_UNSPECIFIED`: Defaults to `BALANCED` behavior.
    - `STABLE`: Optimizes for more consistent, predictable output.
    - `BALANCED`: Balanced between stability and diversity.
    - `CREATIVE`: Optimizes for increased emotional range and variation.
  - `lipsyncConfig` (object) — Requests viseme (mouth-shape) timing for lip-sync, returned in `timestampInfo.lipsyncAlignment`. Its presence turns lip-sync on; an empty object is enough. Visemes are computed from the synthesized audio, so they add no latency and work with every model and language. Independent of `timestampType`: request them on their own or together with word or character timestamps. Fixed when the context is created: it applies to the whole context and cannot be turned on or off mid-context.
    - `visemeSet` (enum<string>; default: "OPENXR"; options: "OPENXR") — The viseme vocabulary of `lipsyncAlignment.visemes`. `OPENXR`, the default and the only supported value, is the 15-shape OpenXR set: `sil`, `PP`, `FF`, `TH`, `DD`, `kk`, `CH`, `SS`, `nn`, `RR`, `aa`, `E`, `ih`, `oh`, `ou`. Other values are rejected with `INVALID_ARGUMENT`.
- `contextId` (string) — Optional context ID. If not provided, one will be generated

**Example payload:**

```json
{
  "create": {
    "voiceId": "Dennis",
    "modelId": "inworld-tts-2",
    "autoMode": true,
    "autoModeStrategy": "SENTENCE_BOUNDARY",
    "timestampType": "WORD",
    "timestampTransportStrategy": "ASYNC"
  },
  "contextId": "ctx-1"
}
```

#### Send Text

Send text to be synthesized for a specific context. Each message accepts up to 2000 UTF-16 code units. With autoMode enabled, `CLIENT_SEGMENTED` expects complete sentences or phrases; `SENTENCE_BOUNDARY` accepts incremental text and buffers it until a boundary or size cut. Include `flushContext` to release remaining text explicitly.

- `send_text` (object; required)
  - `text` (string; required) — The text to synthesize. Maximum 2000 UTF-16 code units per message. `SENTENCE_BOUNDARY` mode accepts partial sentences and markup tags across messages; explicitly flush or close once the text source finishes to release remaining text.
  - `flush_context` (object) — Flush a context and start synthesis of all accumulated text
- `contextId` (string) — The target context for this message. Optional if only one context has been opened

**Example payload:**

```json
{
  "send_text": {
    "text": "Hello, what a wonderful day to be a text-to-speech model!",
    "flush_context": {}
  },
  "contextId": "ctx-1"
}
```

#### Flush Context

Release accumulated text for synthesis while keeping the context open. In `SENTENCE_BOUNDARY` mode, this releases the unfinished tail without requiring final punctuation; incomplete markup at flush returns `INVALID_ARGUMENT`, so complete all tags first. The buffer size threshold can also trigger synthesis automatically.

- `flush_context` (object; required)
- `contextId` (string) — The context to flush. Optional if only one context has been opened

**Example payload:**

```json
{
  "flush_context": {},
  "contextId": "ctx-1"
}
```

#### Close Context

Close an existing context and release all of its resources. Sending a close context message is equivalent to sending a flush message right before, so all text in the buffer will be synthesized before the context is closed. Note that the session will automatically be closed after 10 minutes of inactivity across any context.

- `close_context` (object; required)
- `contextId` (string) — The context to close

**Example payload:**

```json
{
  "close_context": {},
  "contextId": "ctx-1"
}
```

### Server messages

#### Context Created

Event sent when a new TTS context has been successfully created

- `result` (object)
  - `contextId` (string) — The ID of the created context
  - `contextCreated` (object)
    - `voiceId` (string) — The identifier of the voice to use for the synthesis
    - `modelId` (string) — The ID of the model to use for synthesizing speech. See [Models](../../../tts/tts-models) for available models.
    - `audioConfig` (object)
      - `audioEncoding` (enum<string>; default: "MP3"; options: "AUDIO_ENCODING_UNSPECIFIED", "LINEAR16", "MP3", "OGG_OPUS", "ALAW", "MULAW", "PCM", "WAV") — The desired output format of the synthesized audio. Defaults to `MP3`.
         - `LINEAR16`: Uncompressed 16-bit signed little-endian samples (Linear PCM). The WAV header is included in every audio chunk.
         - `MP3`: MP3 audio.
         - `OGG_OPUS`: Opus encoded audio wrapped in an ogg container. The result will be a file which can be played natively on Android, and in browsers (at least Chrome and Firefox). The quality of the encoding is considerably higher than MP3 while using approximately the same bitrate.
         - `ALAW`: ALAW encoded audio. 8-bit companded PCM.
         - `MULAW`: MULAW encoded audio. 8-bit companded PCM.
         - `PCM`: PCM audio. Uncompressed 16-bit signed little-endian samples with no WAV header.
         - `WAV`: WAV audio. Uncompressed 16-bit signed little-endian samples. The WAV header is included in the first audio chunk only. On each `flushCompleted` response, the next audio chunk will also start with a header.
      - `sampleRateHertz` (integer) — The synthesis sample rate (in hertz) for this audio. Accepts values within the range [8000, 48000].
        
        If you request a supported rate that differs from the model's native output rate, the service converts the audio to that rate. Conversion may reduce audio quality. The request fails if the chosen encoding does not support the rate, or if the rate exceeds a known native output rate.
        
        When omitted, the default is 8,000 Hz for `MULAW` and `ALAW`. For other encodings, it is 48,000 Hz, capped at the model's native output rate when known.
      - `bitRate` (integer) — Bits per second of the audio. Only for compressed audio formats (`MP3`, `OGG_OPUS`). The default is 128,000.
      - `speakingRate` (number) — Speaking rate/speed, in the range [0.5, 1.5]. The default is 1.0, which is the normal native speed supported by the specific voice. We recommend using values above 0.8 to ensure high quality.
    - `temperature` (number) — *Ignored on `inworld-tts-2`. Use [`deliveryMode`](#deliveryMode) instead.*
      
      Determines the degree of randomness when sampling audio tokens to generate the response.
      
      Defaults to 1.0. Accepts values between 0 (exclusive) and 2 (inclusive). Higher values will make the output more random and can lead to more expressive results. Lower values will make it more deterministic. If 0 is provided, the default value will be used.
      
      For the most stable results, we recommend using the default value.
    - `timestampType` (enum<string>; default: "TIMESTAMP_TYPE_UNSPECIFIED"; options: "TIMESTAMP_TYPE_UNSPECIFIED", "WORD", "CHARACTER") — Controls timestamp metadata returned with the audio. When enabled, the response includes timing arrays, which can be useful for word-highlighting, karaoke-style captions, and lipsync.
      
      - WORD: Output arrays under `timestampInfo.wordAlignment` (words, wordStartTimeSeconds, wordEndTimeSeconds).
      - CHARACTER: Output arrays under `timestampInfo.characterAlignment` (characters, characterStartTimeSeconds, characterEndTimeSeconds).
      - `TIMESTAMP_TYPE_UNSPECIFIED`: Do not compute alignment; timestamp arrays will be empty or omitted.
      
      **Latency note:** Alignment adds additional computation. Enabling alignment can increase latency.
      
      The timestamps reset per flush, either triggered manually or automatically by the server. When you receive a `flushCompleted` message, the timestamps for subsequent chunks will start from 0.
    - `maxBufferDelayMs` (integer) — Manual-mode buffer delay in milliseconds. Applies only when autoMode is false; auto mode ignores this field. The timer starts with the first buffered text and is canceled when that buffer is flushed. Omission or 0 disables the timer. In manual mode, an explicit flush, the size threshold, or the timer can trigger synthesis. In `SENTENCE_BOUNDARY` mode, explicitly flush or close to release an unfinished tail.
    - `bufferCharThreshold` (integer) — Advanced buffering safeguard for unusually long input. Most applications should leave this unset and use explicit flushes or autoMode to control synthesis. Omission or 0 selects 1000; accepted values range from 0 to 2000. In `SENTENCE_BOUNDARY` mode, this safeguard can force a cut without sentence-ending punctuation, preferring word boundaries. Protected markup stays intact, subject to the API text safety limit.
    - `applyTextNormalization` (enum<string>; default: "APPLY_TEXT_NORMALIZATION_UNSPECIFIED"; options: "APPLY_TEXT_NORMALIZATION_UNSPECIFIED", "ON", "OFF") — When enabled, text normalization automatically expands and standardizes things like numbers, dates, times, and abbreviations before converting them to speech. For example, Dr. Smith becomes Doctor Smith, and 3/10/25 is spoken as March tenth, twenty twenty-five. Turning this off may reduce latency, but the speech output will read the text exactly as written. Defaults to automatically deciding whether to apply text normalization.
    - `autoMode` (boolean; default: false) — Enable server-controlled flushing and batching of buffered text. Use autoModeStrategy to choose between complete client-supplied sentences/phrases (`CLIENT_SEGMENTED`, the default strategy) and incremental text segmented by the server (`SENTENCE_BOUNDARY`). Defaults to false: explicitly flush or close when your text is ready. Buffering guardrails still apply.
    - `autoModeStrategy` (enum<string>; default: "AUTO_MODE_STRATEGY_UNSPECIFIED"; options: "AUTO_MODE_STRATEGY_UNSPECIFIED", "SENTENCE_BOUNDARY", "CLIENT_SEGMENTED") — Select the auto-mode strategy when creating a context. Ignored when autoMode is false.
      
      - `CLIENT_SEGMENTED`: Send complete sentences or phrases. The server controls flushing and can combine queued text while earlier audio streams.
      - `SENTENCE_BOUNDARY` (Preview): Send incremental text, including individual LLM tokens. The server admits text at sentence and complete-break boundaries, or at the bufferCharThreshold size limit. Explicitly flush or close to release remaining text; maxBufferDelayMs is ignored. Supported by inworld-tts-2 and inworld-tts-2-flash where available; unsupported models return `INVALID_ARGUMENT` when auto mode is enabled.
      - `AUTO_MODE_STRATEGY_UNSPECIFIED` or omission: Uses `CLIENT_SEGMENTED`, preserving existing auto-mode behavior.
    - `timestampTransportStrategy` (enum<string>; default: "TIMESTAMP_TRANSPORT_STRATEGY_UNSPECIFIED"; options: "TIMESTAMP_TRANSPORT_STRATEGY_UNSPECIFIED", "SYNC", "ASYNC") — How word and character timestamps are delivered. We recommend setting it explicitly rather than relying on the default.
      
      - `ASYNC`: Timestamps can arrive in a trailing message after their audio. The lowest-latency option.
      - `SYNC`: Timestamps arrive in the same message as their audio. Each message waits until its whole words are aligned, which adds latency.
      - `TIMESTAMP_TRANSPORT_STRATEGY_UNSPECIFIED`: `ASYNC` when `lipsyncConfig` is set, since lip-sync implies a real-time experience; otherwise `SYNC`. The default is expected to move to `ASYNC`.
      
      Visemes in `lipsyncAlignment` always arrive with their audio, whichever strategy is used.
    - `language` (string) — BCP-47 language tag (e.g., `en-US`, `fr-FR`, `ja-JP`) specifying the language that the given voice should speak the text in. If a localized voice prompt exists for the language, it will be used. When omitted, the original voice prompt will be used and the language will be auto-detected from the input text. If an invalid language code is provided, an error will be returned.
      
      See [Languages](../../../tts/capabilities/multilingual) for more details.
    - `deliveryMode` (enum<string>; default: "DELIVERY_MODE_UNSPECIFIED"; options: "DELIVERY_MODE_UNSPECIFIED", "STABLE", "BALANCED", "CREATIVE") — *Only supported by `inworld-tts-2`. The field is ignored on other models.*
      
      Controls how varied the output is. 
      
      - `DELIVERY_MODE_UNSPECIFIED`: Defaults to `BALANCED` behavior.
      - `STABLE`: Optimizes for more consistent, predictable output.
      - `BALANCED`: Balanced between stability and diversity.
      - `CREATIVE`: Optimizes for increased emotional range and variation.
    - `lipsyncConfig` (object) — Requests viseme (mouth-shape) timing for lip-sync, returned in `timestampInfo.lipsyncAlignment`. Its presence turns lip-sync on; an empty object is enough. Visemes are computed from the synthesized audio, so they add no latency and work with every model and language. Independent of `timestampType`: request them on their own or together with word or character timestamps. Fixed when the context is created: it applies to the whole context and cannot be turned on or off mid-context.
      - `visemeSet` (enum<string>; default: "OPENXR"; options: "OPENXR") — The viseme vocabulary of `lipsyncAlignment.visemes`. `OPENXR`, the default and the only supported value, is the 15-shape OpenXR set: `sil`, `PP`, `FF`, `TH`, `DD`, `kk`, `CH`, `SS`, `nn`, `RR`, `aa`, `E`, `ih`, `oh`, `ou`. Other values are rejected with `INVALID_ARGUMENT`.
  - `status` (object) — Status information for gRPC responses, including any error details if applicable
    - `code` (integer) — The status code, as specified by [gRPC status codes](https://grpc.io/docs/guides/status-codes/).
    - `message` (string) — A short description of the error
    - `details` (object[]) — Additional status or error details

**Example payload:**

```json
{
  "result": {
    "contextId": "ctx-1",
    "contextCreated": {
      "voiceId": "Dennis",
      "audioConfig": {
        "audioEncoding": "LINEAR16",
        "sampleRateHertz": 16000
      },
      "modelId": "inworld-tts-2",
      "timestampType": "WORD",
      "maxBufferDelayMs": 3000,
      "autoMode": true,
      "autoModeStrategy": "SENTENCE_BOUNDARY",
      "timestampTransportStrategy": "SYNC",
      "language": "en-US",
      "deliveryMode": "BALANCED"
    },
    "status": {
      "code": 0,
      "message": "",
      "details": []
    }
  }
}
```

#### Audio Chunk

Audio data chunk containing synthesized speech

- `result` (object)
  - `contextId` (string) — The context this audio chunk belongs to
  - `audioChunk` (object)
    - `audioContent` (string) — Base64 encoded audio data
    - `usage` (object)
      - `processedCharactersCount` (integer) — Number of characters of the input text processed so far
      - `modelId` (string) — The model used for speech synthesis
    - `timestampInfo` (object) — Timestamp alignment information when alignment or lip-sync is enabled
      - `wordAlignment` (object) — Word-level alignment when timestampType is WORD
        - `words` (string[]) — Aligned words in order
        - `wordStartTimeSeconds` (number[]) — Start time for each word in seconds from the beginning of the audio
        - `wordEndTimeSeconds` (number[]) — End time for each word in seconds from the beginning of the audio
      - `characterAlignment` (object) — Character-level alignment when timestampType is CHARACTER
        - `characters` (string[]) — Aligned characters (including punctuation and spaces) in order
        - `characterStartTimeSeconds` (number[]) — Start time for each character in seconds from the beginning of the audio
        - `characterEndTimeSeconds` (number[]) — End time for each character in seconds from the beginning of the audio
      - `lipsyncAlignment` (object) — Viseme spans for lip-sync, present when the request set `lipsyncConfig`. If lip-sync is temporarily unavailable, synthesis still succeeds without this field, so check for it rather than assuming it is present. The spans tile the audio with no gaps, and adjacent identical visemes are merged. In streaming responses, every message that carries audio includes the spans for that audio. A span that continues across two messages is split at the boundary. Times are measured from the start of the stream. Like word timestamps, viseme times restart at zero after each `flushCompleted`.
        - `visemes` (string[]) — Viseme names in order, from the requested `visemeSet`. Silence and pauses are `sil`.
        - `visemeStartTimeSeconds` (number[]) — Start time of each viseme span in seconds.
        - `visemeEndTimeSeconds` (number[]) — End time of each viseme span in seconds.
    - `status` (object) — Status information for gRPC responses, including any error details if applicable
      - `code` (integer) — The status code, as specified by [gRPC status codes](https://grpc.io/docs/guides/status-codes/).
      - `message` (string) — A short description of the error
      - `details` (object[]) — Additional status or error details

**Example payload:**

```json
{
  "result": {
    "contextId": "ctx-1",
    "audioChunk": {
      "audioContent": "UklGRgSYAABXQVZFZm10IBAAAAABAAEAgD4AAAB9AAACABAAZGF0YeCX=",
      "usage": {
        "processedCharactersCount": 79,
        "modelId": "inworld-tts-2"
      },
      "timestampInfo": {
        "wordAlignment": {
          "words": [
            "Hello,",
            "what",
            "a",
            "wonderful",
            "day",
            "to",
            "be",
            "a",
            "text-to-speech",
            "model."
          ],
          "wordStartTimeSeconds": [
            0.031,
            0.375,
            0.901,
            1.002,
            1.386,
            1.548,
            1.649,
            1.771,
            1.852,
            2.58
          ],
          "wordEndTimeSeconds": [
            0.355,
            0.86,
            0.921,
            1.326,
            1.528,
            1.609,
            1.71,
            1.791,
            2.539,
            2.802
          ]
        }
      },
      "status": {
        "code": 0,
        "message": "",
        "details": []
      }
    }
  }
}
```

#### Context Closed

Event sent when a context has been closed

- `result` (object)
  - `contextId` (string) — The ID of the closed context
  - `contextClosed` (object)
  - `status` (object) — Status information for gRPC responses, including any error details if applicable
    - `code` (integer) — The status code, as specified by [gRPC status codes](https://grpc.io/docs/guides/status-codes/).
    - `message` (string) — A short description of the error
    - `details` (object[]) — Additional status or error details

**Example payload:**

```json
{
  "result": {
    "contextId": "ctx-1",
    "contextClosed": {},
    "status": {
      "code": 0,
      "message": "",
      "details": []
    }
  }
}
```

#### Flush Completed

Event sent when speech synthesis for a flush of text is completed. Alignment timestamps reset for the next flush. Auto mode can combine eligible text while earlier audio is streaming, so there is not necessarily one flushCompleted event per sendText message or detected sentence.

- `result` (object)
  - `contextId` (string) — The ID of the context for which the flush completed
  - `flushCompleted` (object)
  - `status` (object) — Status information for gRPC responses, including any error details if applicable
    - `code` (integer) — The status code, as specified by [gRPC status codes](https://grpc.io/docs/guides/status-codes/).
    - `message` (string) — A short description of the error
    - `details` (object[]) — Additional status or error details

**Example payload:**

```json
{
  "result": {
    "contextId": "ctx-1",
    "flushCompleted": {},
    "status": {
      "code": 0,
      "message": "",
      "details": []
    }
  }
}
```
