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

# WSS /v1/tts/stream

> One socket, a whole conversation, with turn-level barge-in.

```
wss://tts.mayaresearch.ai/v1/tts/stream
```

The handshake is paid once instead of per request, you push text while your LLM
is still writing it, and you can cut a turn off the moment the user starts
talking.

Audio comes back **base64-encoded inside JSON frames** — the same PCM as HTTP.

## Frames you send

<ParamField body="start" type="object" required>
  First frame. Must carry `"v2": true`, and may set `voice` and `language` for
  the connection.
</ParamField>

<ParamField body="text" type="object">
  One sentence of a turn. Carries `context_id` and `continue`.
</ParamField>

<ParamField body="cancel" type="object">
  Drops a turn — queued and in-flight — and replies `cancelled` instead of
  `end`.
</ParamField>

<ParamField body="ping" type="object">
  Keepalive. Answered with `pong`.
</ParamField>

## Frames you receive

<ResponseField name="metadata" type="object">
  Sample rate, channels, encoding, session id. Arrives **after** your `start`.
</ResponseField>

<ResponseField name="audio" type="object">
  Base64 PCM, tagged with its `context_id`.
</ResponseField>

<ResponseField name="end" type="object">
  Exactly one per closed turn.
</ResponseField>

<ResponseField name="cancelled" type="object">
  Sent instead of `end` when a turn was cancelled.
</ResponseField>

<Warning>
  **Send `start` first, then read `metadata`.** The server does not speak until
  you do — waiting for `metadata` before sending `start` leaves both sides
  waiting forever.
</Warning>

<Warning>
  **`"v2": true` is required on the first frame.** It selects the protocol for
  the whole connection and is latched on the first turn. Without it you get v1
  frames — untyped audio, no `context_id`, no `end` — and your turn never
  terminates.
</Warning>

<RequestExample>
  ```python Python theme={null}
  import asyncio, json, base64, websockets

  URL = "wss://tts.mayaresearch.ai/v1/tts/stream"
  HDR = {"Authorization": f"Bearer {API_KEY}"}

  async def main():
      async with websockets.connect(URL, additional_headers=HDR,
                                    max_size=None) as ws:
          await ws.send(json.dumps({"type": "start", "v2": True,
                                    "voice": "Ananya", "language": "hi"}))
          await ws.recv()                      # metadata

          await ws.send(json.dumps({"type": "text", "context_id": "t1",
                                    "text": "आपका ऑर्डर कल पहुँच जाएगा।",
                                    "continue": False}))

          pcm = bytearray()
          while True:
              m = json.loads(await ws.recv())
              if m["type"] == "audio":
                  pcm += base64.b64decode(m["audio"])
              elif m["type"] in ("end", "cancelled"):
                  break

  asyncio.run(main())
  ```
</RequestExample>

<ResponseExample>
  ```json Frames theme={null}
  {"type":"metadata","sample_rate":24000,"channels":1,"encoding":"pcm_s16le"}
  {"type":"audio","context_id":"t1","audio":"+v/7//3//v///wAA..."}
  {"type":"audio","context_id":"t1","audio":"3xMaFkoYdRqNHIse..."}
  {"type":"end","context_id":"t1"}
  ```
</ResponseExample>
