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

# Session Config

> The config handshake — every field, its default, server-side clamping, and the session_ready echo

## The config frame

Your **first** message after connecting must be a single JSON text frame with `type: "config"`. It declares your audio format, an optional language hint, how speech should be chunked, and whether to separate speakers. Send it immediately on open — the server waits about **10 seconds** for it.

```json theme={null}
{
  "type": "config",
  "audio":   { "encoding": "pcm_s16le", "sample_rate": 16000, "channels": 1 },
  "language": "de",
  "chunk":    { "strategy": "balanced" },
  "diarization": { "enabled": false, "max_speakers": 2 },
  "timeline_offset_seconds": 0
}
```

<Warning>
  Unknown or extra top-level fields are **rejected** — a config with fields the server does not recognize fails validation and the socket is closed with a `config_invalid` error. Send only the fields documented below.
</Warning>

## Fields

### type

<ParamField path="type" type="string" required>
  Must be the literal string `"config"`.
</ParamField>

### audio

<ParamField path="audio.encoding" type="string" default="pcm_s16le">
  Audio wire format. One of `"pcm_s16le"` (raw 16-bit little-endian PCM — preferred) or `"wav"`.
</ParamField>

<ParamField path="audio.sample_rate" type="integer" default="16000">
  Sample rate in Hz. Accepted range **8000–48000**. `16000` is recommended.
</ParamField>

<ParamField path="audio.channels" type="integer" default="1">
  Channel count. `1` (mono, recommended) or `2`.
</ParamField>

### language

<ParamField path="language" type="string">
  Optional ISO 639-1 language **hint** (for example `"de"`, `"en"`, `"fr"`). Case-insensitive. The service auto-detects language regardless, and the detected code is returned on each transcript as `language_detected`. An unrecognized code is tolerated as a soft hint (a warning is surfaced in `session_ready`). See [supported languages](#supported-languages).
</ParamField>

### chunk

Controls how the incoming stream is cut into finalized segments. Pick a **strategy** to trade latency against accuracy and density; you rarely need the explicit overrides.

<ParamField path="chunk.strategy" type="string" default="balanced">
  One of:

  * `"low_latency"` — shortest segments, fastest captions.
  * `"balanced"` — the default; a good compromise of latency and accuracy.
  * `"high_throughput"` — longest segments, highest accuracy and text density (most context per segment).
</ParamField>

<ParamField path="chunk.target_seconds" type="number">
  Optional explicit target segment length, in seconds. Overrides the strategy's target. Server-clamped to **\[0.5, 25]**.
</ParamField>

<ParamField path="chunk.max_seconds" type="number">
  Optional hard cap on segment length, in seconds. Overrides the strategy's cap. Server-clamped to **\[target, 25]**.
</ParamField>

<Note>
  Silence is trimmed and segments are cut on natural pauses automatically. You do not need to align your binary frames to any boundary — send audio in whatever size is convenient and the server re-frames it internally.
</Note>

### diarization

<ParamField path="diarization.enabled" type="boolean" default="false">
  Turn speaker separation on or off. When on, transcript frames include a `speakers` array of per-speaker turns. **Off by default.**
</ParamField>

<ParamField path="diarization.max_speakers" type="integer" default="2">
  A hint for how many distinct speakers to expect (range **1–20**). For a clinician/patient conversation, `2` is typical.
</ParamField>

### timeline\_offset\_seconds

<ParamField path="timeline_offset_seconds" type="number" default="0">
  A constant (seconds, `>= 0`) added to **every** emitted segment, word, and speaker timestamp for this session. Leave it at `0` for a normal session. On a **reconnect mid-recording**, set it to the last `processed_until` you received so all transcripts stay on one continuous absolute timeline with no client-side re-anchoring. See [Reconnection](/websocket/reconnection).
</ParamField>

## Strategy behavior

<CardGroup cols={3}>
  <Card title="low_latency" icon="gauge-high">
    Emits short segments for the fastest possible captions. Best for live on-screen dictation where responsiveness matters most.
  </Card>

  <Card title="balanced" icon="scale-balanced">
    The default. Segments are long enough for strong accuracy while still feeling live. A good starting point for most integrations.
  </Card>

  <Card title="high_throughput" icon="layer-group">
    Emits longer segments with the most context, maximizing accuracy and text density. Best when a small extra delay is acceptable.
  </Card>
</CardGroup>

## Defaults & clamping

If you omit a field, its default is applied. Numeric segment lengths are then **clamped** to server-safe bounds, and any adjustment is reported back in `session_ready.warnings`.

| Field                      | Default           | Notes                    |
| -------------------------- | ----------------- | ------------------------ |
| `audio.encoding`           | `pcm_s16le`       | `wav` also accepted      |
| `audio.sample_rate`        | `16000`           | clamped to 8000–48000    |
| `audio.channels`           | `1`               | 1 or 2                   |
| `language`                 | *(none)*          | auto-detect; hint only   |
| `chunk.strategy`           | `balanced`        |                          |
| `chunk.target_seconds`     | *(from strategy)* | clamped to \[0.5, 25]    |
| `chunk.max_seconds`        | *(from strategy)* | clamped to \[target, 25] |
| `diarization.enabled`      | `false`           |                          |
| `diarization.max_speakers` | `2`               | 1–20                     |
| `timeline_offset_seconds`  | `0`               | `>= 0`                   |

<Tip>
  Do not hard-code specific segment lengths unless you have a reason to. Choosing a `strategy` and letting the server pick the target keeps your integration forward-compatible.
</Tip>

## session\_ready

Once your config is validated, the server replies with `session_ready`, echoing the **effective** (validated and clamped) configuration and any warnings. This is your signal to start streaming audio.

```json theme={null}
{
  "type": "session_ready",
  "session_id": "f0c1a9b2c3d4e5f6a7b8c9d0e1f2a3b4",
  "effective": {
    "language": "de",
    "diarization": false,
    "chunk": {
      "strategy": "balanced",
      "target_seconds": 5.0,
      "max_seconds": 20.0
    },
    "timeline_offset_seconds": 0
  },
  "server": {
    "max_segment_seconds": 25.0,
    "protocol_version": "2.0"
  },
  "warnings": []
}
```

<ResponseField name="session_id" type="string">
  Unique identifier for this session.
</ResponseField>

<ResponseField name="effective" type="object">
  The configuration the server actually applied, after defaults and clamping.

  <Expandable title="effective">
    <ResponseField name="language" type="string | null">
      The language hint in effect, or `null` if none was provided.
    </ResponseField>

    <ResponseField name="diarization" type="boolean">
      Whether speaker separation is on for this session.
    </ResponseField>

    <ResponseField name="chunk" type="object">
      The chunking policy in effect: `strategy`, `target_seconds`, and `max_seconds`.
    </ResponseField>

    <ResponseField name="timeline_offset_seconds" type="number">
      The absolute-timeline offset applied to every emitted timestamp.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="server" type="object">
  Server-side limits and metadata: `max_segment_seconds` (the hard maximum segment length) and `protocol_version` (currently `"2.0"`).
</ResponseField>

<ResponseField name="warnings" type="string[]">
  Human-readable notes about adjustments the server made — for example a clamped segment length, or an unrecognized language hint. Empty when nothing was changed.
</ResponseField>

<Note>
  The `effective` object may include additional server-selected fields beyond those listed here. Treat any field you do not recognize as opaque and ignore it — your integration should key only on the documented fields.
</Note>

### Example: a clamped config

If you request `chunk.target_seconds: 40`, the server clamps it and tells you:

```json theme={null}
{
  "type": "session_ready",
  "session_id": "9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d",
  "effective": {
    "language": "en",
    "diarization": true,
    "chunk": { "strategy": "high_throughput", "target_seconds": 25.0, "max_seconds": 25.0 },
    "timeline_offset_seconds": 0
  },
  "server": { "max_segment_seconds": 25.0, "protocol_version": "2.0" },
  "warnings": [
    "target_seconds clamped to [0.5, 25.0]",
    "max_seconds clamped to [25.0, 25.0]"
  ]
}
```

## Supported languages

Language is auto-detected; the `language` field is only a hint. The following European languages are recognized (ISO 639-1). Mid-conversation language switches are handled automatically.

| Language  | Code | Language   | Code | Language   | Code |
| --------- | ---- | ---------- | ---- | ---------- | ---- |
| Bulgarian | `bg` | Greek      | `el` | Portuguese | `pt` |
| Croatian  | `hr` | Hungarian  | `hu` | Romanian   | `ro` |
| Czech     | `cs` | Italian    | `it` | Russian    | `ru` |
| Danish    | `da` | Latvian    | `lv` | Slovak     | `sk` |
| Dutch     | `nl` | Lithuanian | `lt` | Slovenian  | `sl` |
| English   | `en` | Maltese    | `mt` | Spanish    | `es` |
| Estonian  | `et` | Polish     | `pl` | Swedish    | `sv` |
| Finnish   | `fi` | German     | `de` | Ukrainian  | `uk` |
| French    | `fr` |            |      |            |      |

<Note>
  Passing a code outside this set is not an error — it is accepted as a soft hint (with a warning in `session_ready`), and detection still runs.
</Note>

## Next steps

<CardGroup cols={2}>
  <Card title="Messages" icon="messages" href="/websocket/messages">
    Stream audio and read transcript, ack, backpressure, and status frames.
  </Card>

  <Card title="Reconnection" icon="rotate" href="/websocket/reconnection">
    Use `timeline_offset_seconds` to resume on one continuous timeline.
  </Card>
</CardGroup>
