Streaming API¶
WSS wss://medistream-staging.sahl.ai/ws/summary
Use the streaming API to build your own recording experience. Stream consultation audio over one WebSocket and receive a live transcript, then a structured clinical note when the consultation ends.
One WebSocket connection is one consultation.
sequenceDiagram
autonumber
participant C as Your client
participant S as Sahl AI
C->>S: Connect (subprotocols: listen-protocol, token)
C->>S: start
S-->>C: encounter_created
loop Every ~200 ms
C->>S: audio_chunk (seq_id n)
S-->>C: ack (seq_id n)
S-->>C: transcript_item
end
C->>S: end_transcript
S-->>C: ack (remaining seq_ids)
S-->>C: task_status (SUCCESS, summary[])
S--xC: Socket closed by server
Connect¶
Get a token with username and password or a signed JWT. With a signed JWT, exchange the assertion for a session token first: the WebSocket does not accept the assertion itself. Pass it as the second WebSocket subprotocol, after listen-protocol. Browsers cannot set headers on a WebSocket handshake, so the token travels in the subprotocol list.
If the token is invalid or expired, the server rejects the handshake or closes the socket right after it opens. Refresh the token before a consultation starts, never partway through.
Audio format¶
| Property | Value |
|---|---|
| Encoding | Raw PCM, signed 16-bit little-endian. Not WAV, MP3 or WebM. |
| Sample rate | 16,000 Hz |
| Channels | 1 (mono) |
| Chunk length | 100–200 ms per message (3,072 samples ≈ 192 ms works well) |
| Transport | Base64 string inside a JSON text frame |
Every message in this protocol is a JSON text frame with an object field. Do not send binary frames.
Converting Web Audio Float32Array samples (already resampled to 16 kHz mono):
function toBase64Pcm16(float32) {
const pcm = new Int16Array(float32.length);
for (let i = 0; i < float32.length; i++) {
const s = Math.max(-1, Math.min(1, float32[i]));
pcm[i] = s < 0 ? s * 0x8000 : s * 0x7fff;
}
const bytes = new Uint8Array(pcm.buffer);
let bin = "";
for (let i = 0; i < bytes.length; i += 0x8000) {
bin += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
}
return btoa(bin);
}
Minimal session¶
// → connect wss://medistream-staging.sahl.ai/ws/summary subprotocols ["listen-protocol", "<TOKEN>"]
→ {"object":"start","prompt_id":"<PROMPT_ID>","consultation_name":"Follow-up","show_transcript":true,"ext_patient_id":"MRN-884213","visit_id":"VISIT-2211045"}
← {"object":"encounter_created","encounter_id":"6b0d5c39-1f2a-4e77-b0aa-2c9d4e81f5b3"}
→ {"object":"audio_chunk","payload":"M/8x/y3/…","seq_id":1}
← {"object":"ack","seq_id":1}
← {"object":"transcript_item","id":"…","text":"Good morning, how are you feeling today?","start_offset_ms":0,"end_offset_ms":2600,"timestamp":"2026-11-01T09:14:20.001Z"}
→ {"object":"end_transcript","seq_id":842}
← {"object":"ack","seq_id":842}
← {"object":"task_status","status":"SUCCESS","summary":[{"title":"Chief Complaint","body":["…"],"edited_body":null,"show":true}],"errorMsg":""}
// ← server closes the socket
Next, read the full message reference and the reliability requirements. The reliability rules are mandatory for production clients.