# WebSocket protocol

> Munsit runs two WebSockets: one that streams text in and returns audio chunks, and one that streams audio in and returns live transcripts. This page is the full wire protocol for both — connection, auth, message shapes and lifecycle.

## The two sockets

Same host, same `/api/v1` prefix, opposite directions.

| Socket | Path | You send | You receive |
| --- | --- | --- | --- |
| WSS **Text to speech** | `/websocket/text-to-speech` | JSON messages carrying text chunks | Base64-encoded PCM audio chunks |
| WSS **Speech to text** (legacy) | `/websocket/speech-to-text` | Audio chunks (WAV, then WAV or raw PCM) | Cumulative Arabic transcript strings |

> When to use the TTS socket. It shines when input text is streamed or generated in chunks and you need low-latency, real-time audio. If the entire text is available upfront, partial generation adds buffering — a standard HTTP request can be lower latency and is much simpler for quick prototyping.

## Text to speech — audio out

Streaming synthesisWSS/websocket/text-to-speech

```
wss://api.munsit.com/api/v1/websocket/text-to-speech?x-api-key=YOUR_API_KEY
```

**Authentication.** API key via the `x-api-key` query parameter, or in the initial connection message as `x_api_key`. After the socket opens you must send an `initConnection` message before any text.

```
{
  "type": "initConnection",
  "model_id": "faseeh-v1-preview",
  "voice_id": "ar-najdi-male-2",
  "voice_settings": {
    "stability": 0.5,
    "similarity_boost": 0.75,
    "speed": 1.0
  },
  "output_format": "pcm_24000",
  "x_api_key": "YOUR_API_KEY"
}
```

```
{
  "type": "text",
  "text": "مرحبا بك في فصيح ",
  "flush": false,
  "try_trigger_generation": false
}
```

```
{
  "type": "clear"
}

// Clears the current text buffer. No response message.
```

```
{
  "type": "closeConnection"
}

// Closes the WebSocket connection gracefully.
```

Response

```
{ "type": "connectionInitialized" }
```

Response

```
{ "audio": "base64_encoded_audio_data", "sampleRate": 24000 }
```

Response

```
// none
```

Response

```
// connection closes
```

**`initConnection` fields.**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | string | **Yes** | Must be `"initConnection"`. |
| `model_id` | string | No | Model ID to use (default: `"faseeh-v1-preview"`). |
| `voice_id` | string | **Yes** | The voice ID to use for synthesis. |
| `voice_settings` | object | No | Voice configuration. |
| `voice_settings.stability` | number | No | Stability setting (default: 0.5). |
| `voice_settings.similarity_boost` | number | No | Similarity boost (default: 0.75). |
| `voice_settings.speed` | number | No | Speed setting, range 0.7–1.2 (default: 1.0). |
| `output_format` | string | No | Audio output format: `"pcm_8000"`, `"pcm_16000"`, `"pcm_22050"`, `"pcm_24000"` (default: `"pcm_24000"`). This socket tops out at 24 kHz — for 48 kHz engine-native audio use the HTTP endpoints ([synthesize](/text-to-speech/synthesize), [streaming output](/text-to-speech/audio-streaming-output), [voice preview](/text-to-speech/voice-preview)) with `sample_rate=48000`. |
| `x_api_key` | string | No | API key (if not provided in query parameter). |

**`text` fields and the audio response.**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | string | **Yes** | Must be `"text"`. |
| `text` | string | **Yes** | Text to convert to speech. |
| `flush` | boolean | No | Force generation of audio even if buffer is small (default: `false`). |
| `try_trigger_generation` | boolean | No | Attempt to trigger generation immediately (default: `false`). |

| Response field | Type | Description |
| --- | --- | --- |
| `audio` | string | Base64-encoded PCM audio data. |
| `sampleRate` | number | Sample rate of the audio (typically 24000 Hz). |

## Speech to text — audio in

> The speech-to-text socket below is deprecated. WS /websocket/speech-to-text remains available for existing integrations but receives no new recognition features — no word timestamps, turn events, hotwords, confidence, sentiment or gender. New integrations should use WS /api/v1/listen. Finalization (end_of_stream) and min_buffer_seconds were added here as correctness fixes, not as new capability. It is frozen, not scheduled for removal, and stays available for existing integrations; any change to that would be announced on the Changelog.

Streaming transcriptionWSS/websocket/speech-to-text

```
wss://api.munsit.com/api/v1/websocket/speech-to-text?x-api-key=YOUR_MUNSIT_API_KEY&model=munsit
```

**Authentication.** The server accepts any one of three methods; at least one is required. If auth is invalid, the connection is rejected or closed — there is no dedicated `authentication_error` event.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `x-api-key` | string | No | API key in header or query. |
| `Authorization` | string | No | Bearer token header. |
| `token` | string | No | Token query param fallback for browsers. |

| Query parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | string | No | ASR model to use: `munsit` (default) or `munsit-en-ar` (mixed Arabic-English with code-switching). |
| `min_buffer_seconds` | number | No | Seconds of audio that must accumulate before an interim `transcription` is emitted. Default `0.5`, clamped to `0.1`–`5.0`. Lower it for faster partials on short answers. It does not gate the final result — the pass triggered by `end_of_stream` runs at any duration. |

**Audio input.** The first chunk must be WAV (with headers); subsequent chunks can be WAV or raw PCM. `audioBuffer` must be an array of byte values (0–255). Any chunk size works — 100–500 ms is typical for live audio, and smaller chunks lower latency without changing what is recognized. Two client message formats are accepted:

```
{
  "event": "audio_chunk",
  "data": {
    "audioBuffer": [1, 2, 3]
  }
}
```

```
{
  "audioBuffer": [1, 2, 3]
}
```

**Server events.**

| Event | Direction | Type | Meaning |
| --- | --- | --- | --- |
| `audio_chunk` | client → server | `Array<Uint8>` | Audio bytes; first chunk should include full WAV headers, PCM accepted after. |
| `end_of_stream` | client → server | — | Signals end of audio. Transcribes whatever is still buffered, at any duration, and replies with a final `transcription` then `finalized`. Send this before closing. |
| `transcription` | server → client | string | Cumulative Arabic transcript generated from all received chunks. Carries an `isFinal` boolean. |
| `finalized` | server → client | string | The complete transcript for the session. Emitted once, in response to `end_of_stream`. Safe to close the socket after this. |
| `transcription_error` | server → client | string | Error details during streaming transcription. |

**Finalizing a stream.** Closing the socket does **not** flush buffered audio, and waiting after the audio stops does not produce a final result — an interim hypothesis stays `isFinal: false` indefinitely, and audio shorter than `min_buffer_seconds` produces no event at all. Send `end_of_stream`, wait for `finalized`, then close.

```
{ "event": "end_of_stream" }
```

```
{ "event": "transcription", "data": "نعم", "isFinal": true }
{ "event": "finalized", "data": "نعم" }
```

> Single-word answers — «نعم», «لا», a city name — are typically under min_buffer_seconds, so end_of_stream is the only thing that will return them. Clients that open one socket per utterance must send it every time. On WS /api/v1/listen the equivalent is the CloseStream control frame, and the flag is is_final.

**Recommended flow.** Connect with auth → confirm the socket is open on the client side → emit `audio_chunk` payloads as audio arrives → listen for `transcription_error` and handle failures → listen for `transcription` and render live text updates → send `end_of_stream` when the speaker stops → wait for `finalized` → close the socket.

## Errors

On the TTS socket, errors arrive as a typed JSON message. On the STT socket, listen for the `transcription_error` event; invalid auth is handled by rejecting or closing the connection.

Error messagetext-to-speech socket

```
{
  "type": "error",
  "errorCode": 40101,
  "errorMessage": "Invalid API key"
}
```

| Field | Type | Description |
| --- | --- | --- |
| `type` | string | Always `"error"`. |
| `errorCode` | number | Numeric error code (e.g. 40101, 40001). |
| `errorMessage` | string | Human-readable error message. |

## Full lifecycle example

Open, initialize, stream text, collect audio, handle errors — the complete TTS round-trip in the browser or Node.

```javascript
const ws = new WebSocket('wss://api.munsit.com/api/v1/websocket/text-to-speech?x-api-key=YOUR_API_KEY');

ws.onopen = () => {
  // Initialize connection
  ws.send(JSON.stringify({
    type: "initConnection",
    model_id: "faseeh-v1-preview",
    voice_id: "ar-najdi-male-2",
    voice_settings: {
      stability: 0.5,
      similarity_boost: 0.75,
      speed: 1.0
    },
    output_format: "pcm_24000"
  }));
};

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);

  if (data.type === "connectionInitialized") {
    // Connection ready, send text
    ws.send(JSON.stringify({
      type: "text",
      text: "مرحبا بك في فصيح "
    }));
  } else if (data.audio) {
    // Process audio chunk
    const audioData = atob(data.audio);
    // Handle audio playback
  } else if (data.type === "error" || data.errorCode) {
    console.error("Error:", data.errorMessage);
  }
};

ws.onerror = (error) => {
  console.error("WebSocket error:", error);
};

ws.onclose = () => {
  console.log("WebSocket closed");
};
```

## Best practices

Five habits that keep streams smooth.

| Practice | Why |
| --- | --- |
| **Always initialize** | Send `initConnection` immediately after opening the TTS connection. |
| **Handle errors** | Check for error messages in responses. |
| **Flush when done** | Use `flush: true` when sending the last text chunk to ensure all audio is generated. |
| **Close gracefully** | Send `closeConnection` before closing the WebSocket. |
| **Buffer audio** | Collect audio chunks and play them sequentially for smooth playback. |

- [TTS streaming guide](/text-to-speech/audio-streaming-output) — The task-oriented walkthrough for streamed synthesis. — `WSS /websocket/text-to-speech`

- [STT streaming guide](/speech-to-text/streaming) — The task-oriented walkthrough for live transcription. — `WSS /websocket/speech-to-text`
