> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.narilabs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.narilabs.com/_mcp/server.

# Stream text to speech

GET /v1/text-to-speech/{voice_id}/stream-input

Stream text and receive base64-encoded 24 kHz PCM audio over an authenticated WebSocket.

## Message flow

1. Send `{"text":" "}` immediately after connecting. This initialization space is not billed.
2. Send text fragments while receiving `audio` chunks. Set `flush:true` to finish a segment without closing the connection. Audio may arrive before a flush.
3. Send `{"text":""}` to end input. Keep receiving until `{"isFinal":true}`, then the connection closes.

Output streams automatically. Only automatic generation is supported. There is no public flush-completion event; `isFinal` completes the whole connection. See the [TTS input streaming guide](/streaming-text) for a runnable example.

Reference: https://docs.narilabs.com/api-reference/text-to-speech/input-stream/stream-text-to-speech

## AsyncAPI Specification

```yaml
asyncapi: 2.6.0
info:
  title: Stream text to speech
  version: subpackage_inputStream.Stream text to speech
  description: >
    Stream text and receive base64-encoded 24 kHz PCM audio over an
    authenticated WebSocket.


    ## Message flow


    1. Send `{"text":" "}` immediately after connecting. This initialization
    space is not billed.

    2. Send text fragments while receiving `audio` chunks. Set `flush:true` to
    finish a segment without closing the connection. Audio may arrive before a
    flush.

    3. Send `{"text":""}` to end input. Keep receiving until `{"isFinal":true}`,
    then the connection closes.


    Output streams automatically. Only automatic generation is supported. There
    is no public flush-completion event; `isFinal` completes the whole
    connection. See the [TTS input streaming guide](/streaming-text) for a
    runnable example.
channels:
  /v1/text-to-speech/{voice_id}/stream-input:
    description: >
      Stream text and receive base64-encoded 24 kHz PCM audio over an
      authenticated WebSocket.


      ## Message flow


      1. Send `{"text":" "}` immediately after connecting. This initialization
      space is not billed.

      2. Send text fragments while receiving `audio` chunks. Set `flush:true` to
      finish a segment without closing the connection. Audio may arrive before a
      flush.

      3. Send `{"text":""}` to end input. Keep receiving until
      `{"isFinal":true}`, then the connection closes.


      Output streams automatically. Only automatic generation is supported.
      There is no public flush-completion event; `isFinal` completes the whole
      connection. See the [TTS input streaming guide](/streaming-text) for a
      runnable example.
    parameters:
      voice_id:
        description: Voice ID from the selected model catalog, such as diana.
        schema:
          type: string
    bindings:
      ws:
        query:
          type: object
          properties:
            model_id:
              $ref: '#/components/schemas/inputStream_model_id'
            output_format:
              $ref: '#/components/schemas/inputStream_output_format'
              default: pcm_24000
            language_code:
              type: string
              default: auto
            seed:
              type: integer
              minimum: 0
              maximum: 4294967295
              default: 0
            inactivity_timeout:
              type: integer
              minimum: 1
              maximum: 180
              default: 20
            auto_mode:
              type: boolean
              default: true
            sync_alignment:
              type: boolean
              default: false
            enable_ssml_parsing:
              type: boolean
              default: false
            apply_text_normalization:
              $ref: '#/components/schemas/inputStream_apply_text_normalization'
              default: 'off'
    publish:
      operationId: subpackage_inputStream.Stream text to speech-publish
      summary: Server messages
      message:
        oneOf:
          - $ref: >-
              #/components/messages/subpackage_inputStream.Stream text to
              speech-server-0-Audio
          - $ref: >-
              #/components/messages/subpackage_inputStream.Stream text to
              speech-server-1-Final
          - $ref: >-
              #/components/messages/subpackage_inputStream.Stream text to
              speech-server-2-Error
    subscribe:
      operationId: subpackage_inputStream.Stream text to speech-subscribe
      summary: Client messages
      message:
        oneOf:
          - $ref: >-
              #/components/messages/subpackage_inputStream.Stream text to
              speech-client-0-Initialize
          - $ref: >-
              #/components/messages/subpackage_inputStream.Stream text to
              speech-client-1-Text
          - $ref: >-
              #/components/messages/subpackage_inputStream.Stream text to
              speech-client-2-End
servers:
  Production:
    url: wss://api.narilabs.com/
    protocol: wss
    x-default: true
components:
  messages:
    subpackage_inputStream.Stream text to speech-server-0-Audio:
      name: Audio
      title: Audio chunk
      description: >-
        Decode the base64 `audio` as raw PCM16 at 24 kHz. Chunks arrive in
        order.
      payload:
        $ref: '#/components/schemas/inputStream_Audio'
    subpackage_inputStream.Stream text to speech-server-1-Final:
      name: Final
      title: Connection complete
      description: >-
        The `isFinal:true` message confirms that all audio for the connection
        has been sent.
      payload:
        $ref: '#/components/schemas/inputStream_Final'
    subpackage_inputStream.Stream text to speech-server-2-Error:
      name: Error
      title: Error
      description: A terminal error may be followed by a non-1000 WebSocket close.
      payload:
        $ref: '#/components/schemas/inputStream_Error'
    subpackage_inputStream.Stream text to speech-client-0-Initialize:
      name: Initialize
      title: Initialize
      description: Send `{"text":" "}` once to initialize the connection.
      payload:
        $ref: '#/components/schemas/inputStream_Initialize'
    subpackage_inputStream.Stream text to speech-client-1-Text:
      name: Text
      title: Send text / flush
      description: >-
        Append text. Set `flush:true` to finish the current segment and keep the
        connection open.
      payload:
        $ref: '#/components/schemas/inputStream_Text'
    subpackage_inputStream.Stream text to speech-client-2-End:
      name: End
      title: End input
      description: Send empty text and keep receiving until `isFinal` arrives.
      payload:
        $ref: '#/components/schemas/inputStream_End'
  schemas:
    inputStream_model_id:
      type: string
      enum:
        - qwen3-tts
        - qwen3-tts-fast
      title: inputStream_model_id
    inputStream_output_format:
      type: string
      enum:
        - pcm_24000
      default: pcm_24000
      title: inputStream_output_format
    inputStream_apply_text_normalization:
      type: string
      enum:
        - 'off'
      default: 'off'
      title: inputStream_apply_text_normalization
    inputStream_Audio:
      type: object
      properties:
        audio:
          type: string
          description: >-
            Base64-encoded 24 kHz signed 16-bit little-endian mono PCM, without
            a WAV header.
        isFinal:
          type: boolean
      required:
        - audio
        - isFinal
      title: inputStream_Audio
    inputStream_Final:
      type: object
      properties:
        isFinal:
          type: boolean
          description: Entire connection completed; not emitted for each flush.
      required:
        - isFinal
      title: inputStream_Final
    inputStream_Error:
      type: object
      properties:
        error:
          type: string
          description: Lowercase machine-readable error code.
        message:
          type: string
          description: Human-readable explanation.
        requestId:
          type: string
          description: >-
            Present on Gateway-generated errors; may be omitted on worker
            errors. Also keep the x-request-id handshake header.
      required:
        - error
        - message
      title: inputStream_Error
    ChannelsInputStreamMessagesInitializeText:
      type: string
      enum:
        - ' '
      description: >-
        Exactly one space. Send once immediately after connecting; it is not
        billed.
      title: ChannelsInputStreamMessagesInitializeText
    VoiceSettingsStability:
      type: string
      enum:
        - '0.5'
      title: VoiceSettingsStability
    VoiceSettingsSimilarityBoost:
      type: string
      enum:
        - '0.75'
      title: VoiceSettingsSimilarityBoost
    VoiceSettingsStyle:
      type: string
      enum:
        - '0'
      title: VoiceSettingsStyle
    VoiceSettingsSpeed:
      type: string
      enum:
        - '1'
      title: VoiceSettingsSpeed
    VoiceSettings:
      type: object
      properties:
        stability:
          $ref: '#/components/schemas/VoiceSettingsStability'
          default: 0.5
        similarity_boost:
          $ref: '#/components/schemas/VoiceSettingsSimilarityBoost'
          default: 0.75
        style:
          $ref: '#/components/schemas/VoiceSettingsStyle'
          default: 0
        use_speaker_boost:
          type: boolean
          default: true
        speed:
          $ref: '#/components/schemas/VoiceSettingsSpeed'
          default: 1
      title: VoiceSettings
    inputStream_Initialize:
      type: object
      properties:
        text:
          $ref: '#/components/schemas/ChannelsInputStreamMessagesInitializeText'
          description: >-
            Exactly one space. Send once immediately after connecting; it is not
            billed.
        voice_settings:
          oneOf:
            - $ref: '#/components/schemas/VoiceSettings'
            - type: 'null'
          description: >-
            Optional default settings for compatibility. Must not change after
            initialization.
        xi_api_key:
          type:
            - string
            - 'null'
          description: >-
            Compatibility field only; ignored for public API authentication. Use
            the Authorization handshake header.
        xi-api-key:
          type:
            - string
            - 'null'
          description: >-
            Compatibility field only; ignored for public API authentication. Use
            the Authorization handshake header.
        authorization:
          type:
            - string
            - 'null'
          description: >-
            Compatibility field only; ignored for public API authentication. Use
            the Authorization handshake header.
      required:
        - text
      title: inputStream_Initialize
    inputStream_Text:
      type: object
      properties:
        text:
          type: string
          minLength: 1
          description: >-
            Append text verbatim. A standalone single space is a keepalive,
            excluded from synthesis and billing.
        flush:
          type: boolean
          default: false
          description: >-
            Seal pending text as a segment, drain its audio, and keep the
            connection open.
        voice_settings:
          oneOf:
            - $ref: '#/components/schemas/VoiceSettings'
            - type: 'null'
          description: >-
            Optional default settings for compatibility. Must not change after
            initialization.
      required:
        - text
      title: inputStream_Text
    ChannelsInputStreamMessagesEndText:
      type: string
      enum:
        - ''
      description: End input and drain remaining audio; keep receiving until isFinal.
      title: ChannelsInputStreamMessagesEndText
    inputStream_End:
      type: object
      properties:
        text:
          $ref: '#/components/schemas/ChannelsInputStreamMessagesEndText'
          description: End input and drain remaining audio; keep receiving until isFinal.
        voice_settings:
          oneOf:
            - $ref: '#/components/schemas/VoiceSettings'
            - type: 'null'
          description: >-
            Optional default settings for compatibility. Must not change after
            initialization.
        flush:
          type: boolean
          default: false
          description: Empty text ends input regardless of this value.
      required:
        - text
      title: inputStream_End

```