Realtime
WebSocket-based realtime bidirectional voice conversations (OpenAI Realtime protocol) — built for voice assistants, live translation, and other low-latency scenarios.
Connecting
The model name goes in the query string; auth is the same as the HTTP endpoints:
wss://test.kailin.com.cn/v1/realtime?model=gpt-4o-realtime-preview
const ws = new WebSocket(
'wss://test.kailin.com.cn/v1/realtime?model=gpt-4o-realtime-preview',
{
headers: {
Authorization: 'Bearer sk-your-token',
'OpenAI-Beta': 'realtime=v1',
},
}
);
Browser new WebSocket cannot set custom headers. Either pass the token via subprotocol parameters, or proxy the WebSocket through your own backend (recommended, keeps the token off the frontend).
Message Format
All messages are JSON text frames with a type field.
Session Setup
After connecting, the server sends session.created. Clients usually send session.update first to configure voice and turn detection:
{
"type": "session.update",
"session": {
"instructions": "You are a voice assistant. Keep answers short.",
"voice": "alloy",
"input_audio_format": "pcm16",
"output_audio_format": "pcm16",
"input_audio_transcription": { "model": "whisper-1" },
"turn_detection": { "type": "server_vad" }
}
}
Sending User Audio
Audio is sent in chunks as Base64-encoded PCM16 (24kHz mono):
{ "type": "input_audio_buffer.append", "audio": "<base64 pcm16>" }
{ "type": "input_audio_buffer.commit" }
With server_vad enabled, the server detects the end of speech and commits automatically — no manual commit needed.
Triggering a Response
{
"type": "response.create",
"response": { "modalities": ["text", "audio"], "instructions": "Be enthusiastic." }
}
Server Events
| Event | Description |
|---|---|
session.created / session.updated | Session created / config applied |
input_audio_buffer.speech_started / _stopped | VAD detected speech start / end |
input_audio_buffer.committed | Audio buffer committed |
conversation.item.created | Conversation item created |
response.audio_transcript.delta / _done | Reply transcript delta / done |
response.audio.delta / _done | Reply audio delta (Base64 PCM16) |
response.text.delta / _done | Text-only reply delta |
response.done | Current response finished |
error | Error event |
Full Flow Example
const ws = new WebSocket(url);
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'session.update',
session: { voice: 'alloy', turn_detection: { type: 'server_vad' } },
}));
// send an audio chunk…
ws.send(JSON.stringify({
type: 'input_audio_buffer.append',
audio: btoa(String.fromCharCode(...pcmChunk)),
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.type) {
case 'response.audio.delta':
playPcmChunk(atob(msg.delta));
break;
case 'response.audio_transcript.delta':
appendSubtitle(msg.delta);
break;
case 'response.done':
console.log('turn finished', msg.response.usage);
break;
}
};
Realtime endpoints bill by audio duration and cost significantly more than text. Watch your session length while testing. Availability depends on the models configured in your channel.