How silence, pauses, and speech interruptions are handled in realtime transcription.
VAD and speech segmentation
Server-side VAD automatically detects speech segments. The WebSocket events pushed to your client are:
1. Speech detected → input_audio_buffer.speech_started (abbreviated as speech_started)
2. Speech continues → audio accumulated
3. Silence detected → input_audio_buffer.speech_stopped (abbreviated as speech_stopped)
4. Transcription complete → conversation.item.input_audio_transcription.completed (abbreviated as completed)
Use the full type string in your event dispatcher. See [Server Events](/docs/api-reference/server-events) for the complete payload schemas.
Clients do not need to implement VAD — just consume events.
Silence behavior
Short pauses
Normal speaking pauses (commas, sentence ends) do not trigger segment splits. VAD has a silence threshold (vadSilenceMs); only silence exceeding this duration triggers end-of-speech.
Long silence
idle_timeout_seconds closes the connection with code 4408.session.created.limits.idle_timeout_seconds; do not hard-code it.For example, a 100 ms silent binary frame at 16 kHz mono is new Int16Array(1600).fill(0). See [Audio Input](/docs/realtime/audio-input) for frame-size guidance and [Connection Lifecycle](/docs/realtime/connection-lifecycle) for close-code details.
Manual flush
Send input_audio_buffer.flush (or its alias flush) to force-end the current speech segment:
{ "type": "input_audio_buffer.flush" }
Use cases:
vadSilenceMs.Max speech segment duration
vadMaxSpeechMs limits the maximum duration of a single speech segment. When exceeded:
input_audio_buffer.speech_stopped → conversation.item.input_audio_transcription.completed.input_audio_buffer.speech_started.reason field of speech_stopped explains why the segment ended (e.g. end_of_speech or max_duration). See [Server Events](/docs/api-reference/server-events) for the full list of reason values.Interruption handling
Speaker interrupted
In live scenarios, if a speaker is interrupted:
input_audio_buffer.speech_stopped → conversation.item.input_audio_transcription.completed.input_audio_buffer.speech_started.Client strategy
1. Each conversation.item.input_audio_transcription.completed event is independent and final.
2. No need to cancel or roll back already-displayed text.
3. New segments are ordered by utterance_index. If two segments overlap in time, use audio_duration_ms / speech_duration_ms to position them on a timeline rather than relying on arrival order.
Recommended client handling
ws.onmessage = (event) => {
if (typeof event.data !== "string") return;
const data = JSON.parse(event.data);
if (data.type === "input_audio_buffer.speech_started") {
showListeningIndicator(data.utterance_index);
} else if (data.type === "input_audio_buffer.speech_stopped") {
showProcessingIndicator(data.utterance_index);
} else if (data.type === "conversation.item.input_audio_transcription.completed") {
appendTranscript(data.utterance_index, data.text);
hideIndicators(data.utterance_index);
}
};
Related
input_audio_buffer.flushreason values