> Agent-readable docs index: /llms.txt. Download /docs.zip to grep all markdown files locally.

---
title: WebSocket
api: "WSS /v1/tts/websocket"
description: Sentence-by-sentence streaming synthesis over a WebSocket.
gridGap: 30
---

# Stream speech over WebSocket

`WSS` `wss://api.vakyam.ai/v1/tts/websocket`

Real-time, sentence-by-sentence synthesis. Authenticate with the `Authorization`
header on connect, send a `config` message, then one `text` message per
sentence. See the [realtime guide](/guides/realtime-websocket) for the full
message reference.

{/* Hidden marker: the framework only widens the right column to the HTTP-page
       width (460px) when it finds a RequestExample/ResponseExample in the aside.
       Visibility renders nothing, but the node still triggers that width. */}





### Handshake

#### wss

```text
wss://api.vakyam.ai/v1/tts/websocket

Header   Authorization: Bearer vak_live_<key>
Status   101 Switching Protocols
```



### Messages sent

#### config

```json
{
  "type": "config",
  "model_id": "raaga-v1",
  "voice": "Archana",
  "language": "ta-IN",
  "output_format": "pcm",
  "speed": 1.0
}
```



#### text

```json
{ "type": "text", "text": "நான் சரியாக இருக்கிறேன்." }
```



#### cancel

```json
{ "type": "cancel" }
```



#### ping

```json
{ "type": "ping" }
```



#### disconnect

```json
{ "type": "disconnect" }
```



### Messages received

#### connected

```json
{
  "type": "connected",
  "user_id": "f1e2d3c4-..."
}
```



#### configured

```json
{
  "type": "configured",
  "model_id": "raaga-v1",
  "voice": "Archana",
  "language": "ta-IN",
  "output_format": "pcm",
  "speed": 1.0
}
```



#### end_of_utterance

```json
{
  "type": "end_of_utterance",
  "characters_used": 22,
  "duration_seconds": 1.8,
  "truncated": false
}
```



#### cancellation

```json
{
  "type": "cancellation",
  "characters_used": 12,
  "duration_seconds": 0.86
}
```



#### pong

```json
{
  "type": "pong"
}
```



#### error

```json
{
  "type": "error",
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Too many requests. Retry after 12 seconds.",
    "retry_after_seconds": 12
  }
}
```

## Authorization

- `Authorization` (string, required) — Bearer token sent on connect: `Bearer vak_live_<key>`. An invalid key closes the connection with code `4001`.

## Messages you send

Every client message is a JSON object with a `type` field. Send `config` first,
then one `text` per sentence. Send `cancel` to interrupt the current utterance,
`ping` to keep the socket alive, and `disconnect` to close cleanly.

#### config

Sets the session voice, language, model, format, and speed. Must be the first
    message after connecting.

    ```json
    {
      "type": "config",
      "model_id": "raaga-v1",
      "voice": "Archana",
      "language": "ta-IN",
      "output_format": "pcm",
      "sample_rate": 24000,
      "speed": 1.0
    }
    ```


- `type` (string, required) — Always `config`.


- `model_id` (string, required) — Model identifier. Must be `raaga-v1`.


- `voice` (string, required) — Voice selector. A preset voice name (e.g. `Archana`) or a custom voice ID beginning with `vc_`. A preset voice must form a valid pair with `language`. Send either `voice` or the deprecated `voice_name`, not both.


- `voice_name` (string, deprecated) — **Deprecated** — use `voice` instead. Temporarily accepted for preset voices only.


- `language` (string, required) — BCP 47 language code: `en-IN`, `hi-IN`, `ta-IN`, `te-IN`, `kn-IN`, `mr-IN`, `gu-IN`, or `bn-IN`.


- `output_format` (string, default pcm) — Audio format: `pcm`, `mp3`, `wav`, or `mulaw`. Defaults to `pcm` over WebSocket to avoid container overhead.


- `sample_rate` (integer, default 24000) — Output sample rate in Hz. One of `8000`, `16000`, `24000`, or `48000`.


- `speed` (number, default 1.0) — Playback speed multiplier, `0.5`–`2.0`.



#### text

Synthesizes one complete sentence with the current config. Wait for
    `end_of_utterance` before sending the next `text` message.

    ```json
    { "type": "text", "text": "நான் சரியாக இருக்கிறேன்." }
    ```


- `type` (string, required) — Always `text`.


- `text` (string, required) — One complete sentence to synthesize. Maximum 3000 Unicode characters.



#### cancel

Interrupts (barges in on) the current utterance. Send it while audio is
    streaming to stop the turn early. Cancelling clears the worker's internal
    text buffer, so it immediately stops generating audio for the remaining text
    instead of finishing the queued utterance. The connection stays open.

    ```json
    { "type": "cancel" }
    ```


- `type` (string, required) — Always `cancel`.

    After sending `cancel`, keep reading from the socket — drain any remaining
    binary frames — until the server's `cancellation` message arrives; stopping
    early can stall on backpressure. You are billed only for audio produced
    before the cancel. Sending `cancel` while nothing is being synthesized is
    acknowledged with `characters_used: 0` and is not billed.



#### ping

Keepalive. The server replies with a `pong` and resets the idle timer.

    ```json
    { "type": "ping" }
    ```


- `type` (string, required) — Always `ping`.



#### disconnect

Closes the session cleanly. The server then closes the socket with code `1000`.

    ```json
    { "type": "disconnect" }
    ```


- `type` (string, required) — Always `disconnect`.

## Messages you receive

Between `configured` and `end_of_utterance`, the server streams the audio as raw
**binary** WebSocket frames (not JSON). Reassemble them in arrival order. All
other server messages are JSON objects with a `type` field.

#### connected

Sent once after the connection is authenticated.

    ```json
    {
      "type": "connected",
      "user_id": "f1e2d3c4-..."
    }
    ```


- `type` (string) — Always `connected`.


- `user_id` (string) — The authenticated account's ID.



#### configured

Confirms the active session config, echoed back from your `config` message.

    ```json
    {
      "type": "configured",
      "model_id": "raaga-v1",
      "voice": "Archana",
      "language": "ta-IN",
      "output_format": "pcm",
      "speed": 1.0
    }
    ```


- `type` (string) — Always `configured`.


- `model_id, voice, language, output_format, speed` — The config now active for the session.



#### end_of_utterance

Sent after all binary audio chunks for a sentence have been streamed.

    ```json
    {
      "type": "end_of_utterance",
      "characters_used": 22,
      "duration_seconds": 1.8,
      "truncated": false
    }
    ```


- `type` (string) — Always `end_of_utterance`.


- `characters_used` (integer) — Credits consumed for this sentence.


- `duration_seconds` (number) — Length of the audio just streamed, in seconds.


- `truncated` (boolean) — `false` on a normal completion. `true` if synthesis stopped early at a safety limit, in which case `characters_used` is `0`, a `reason` (e.g. `step_budget_exceeded`) is included, and the turn is not billed.



#### cancellation

Acknowledges a `cancel` (barge-in). Replaces `end_of_utterance` for the
    interrupted turn; the connection stays open.

    ```json
    {
      "type": "cancellation",
      "characters_used": 12,
      "duration_seconds": 0.86
    }
    ```


- `type` (string) — Always `cancellation`.


- `characters_used` (integer) — Worker-reported billable count for audio produced before the cancel — the amount you are charged. `0` if the cancel arrived before any audio or while idle (not billed).


- `duration_seconds` (number) — Length of the audio produced before cancellation, in seconds.



#### pong

Reply to a `ping`.

    ```json
    {
      "type": "pong"
    }
    ```


- `type` (string) — Always `pong`.



#### error

Per-message problems (rate limit, concurrency limit, credits, validation). The connection stays open.

    ```json
    {
      "type": "error",
      "error": {
        "code": "rate_limit_exceeded",
        "message": "Too many requests. Retry after 12 seconds.",
        "retry_after_seconds": 12
      }
    }
    ```


- `type` (string) — Always `error`.


- `error.code` (string) — Machine-readable code: `rate_limit_exceeded`, `concurrency_limit_exceeded`, `insufficient_credits`, `voice_language_not_found`, `text_too_long`, `missing_websocket_config`, `validation_error`, or `internal_error`.


- `error.message` (string) — Human-readable description of the problem.


- `error.retry_after_seconds` (integer) — Seconds to wait before retrying. Present on `rate_limit_exceeded` only; `concurrency_limit_exceeded` has no fixed reset, so it is omitted there.

## Idle timeout

Idle connections close automatically after **60 seconds** without an incoming
message. Any message you send — including a `ping` — resets the timer, so send a
periodic `ping` to keep a session open between utterances. The Python and
JavaScript SDKs send a keepalive `ping` for you by default.

## Close codes

| Code | Reason                                                                        |
| ---- | ----------------------------------------------------------------------------- |
| 1000 | Normal closure                                                                |
| 1011 | Session lost (concurrency or worker capacity revoked mid-session) — reconnect |
| 4001 | Authentication failed                                                         |
| 4002 | Internal server error                                                         |

---

*Powered by [holocron.so](https://holocron.so)*
