Skip to content

Reliability requirements

Clinical audio must never be lost silently. A production client must follow every rule on this page. Sahl AI's own clients follow the same rules.

1. Buffer until acknowledged

Keep each chunk in a map of seq_id → { payload, sent } until its ack arrives, then delete it. On reconnect, mark all buffered entries unsent and resend them in ascending seq_id order, about 100 ms apart.

2. Never reset seq_id

seq_id counts from 1 across the whole consultation, including reconnects and pauses. The server uses it to remove duplicate chunks you resend and to keep audio in order.

3. Detect dead connections

Record the time of the last message received, of any type. Check it every 5 seconds. If nothing has arrived for 25 seconds, treat the socket as dead: close it and reconnect. A half-open TCP connection can look healthy while audio is being lost.

4. Keep the socket alive

Send ping every 30 seconds from open to close: while recording, while paused and while waiting for task_status.

5. Reconnect correctly

On an unexpected close, if the consultation has not finished:

  1. Wait about one second, then reopen the socket with the same token. Back off further if it keeps failing.
  2. Send start again with encounter_id set to the value from encounter_created.
  3. Resend every unacknowledged chunk (rule 1) before sending new audio.

6. Handle network changes

Listen for the browser or operating system going offline. When it does, close the socket and stop sending. Chunks keep collecting in the buffer. When the network comes back, reconnect as in rule 5. The buffered audio is then sent in order, so nothing said during the outage is lost.

7. Queue the end of the consultation

Put end_transcript in the same buffer as the audio, so it is sent only after every earlier chunk has gone. The number of buffered chunks is a reliable progress figure for an "Uploading…" indicator after the clinician presses End.

Checklist

  • Token passed as the second subprotocol after listen-protocol
  • Audio is PCM-16, 16 kHz, mono, base64, 100–200 ms per chunk
  • start sent before the first chunk, with your prompt_id
  • encounter_id stored when encounter_created arrives
  • seq_id starts at 1, increases by one per chunk and never resets
  • Unacknowledged chunks buffered and resent after reconnect
  • ping every 30 seconds for the life of the socket
  • 25-second inbound silence triggers a reconnect
  • Reconnect sends start with the existing encounter_id
  • Socket kept open after end_transcript until task_status arrives
  • task_status with status: "FAILED" handled as a failure