Handling Interruptions & Silence

Silence, pauses, VAD end-of-speech behavior, and connection keepalive.

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 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

  • Silence exceeding idle_timeout_seconds closes the connection with code 4408.
  • The actual value is returned in session.created.limits.idle_timeout_seconds; do not hard-code it.
  • To keep the connection alive, continue sending PCM16LE audio frames, even when they contain silence (all-zero samples).

For example, a 100 ms silent binary frame at 16 kHz mono is new Int16Array(1600).fill(0). See Audio Input for frame-size guidance and 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:

  • You know a speech segment has ended (e.g. the user released a push-to-talk button or tapped an end-of-utterance button).
  • Force the upstream to process already-sent audio instead of waiting for VAD.
  • Reduce latency by not waiting for vadSilenceMs.

Max speech segment duration

vadMaxSpeechMs limits the maximum duration of a single speech segment. When exceeded:

  • The current segment is finalized: input_audio_buffer.speech_stoppedconversation.item.input_audio_transcription.completed.
  • A new segment starts immediately with input_audio_buffer.speech_started.
  • The reason field of speech_stopped explains why the segment ended (e.g. end_of_speech or max_duration). See Server Events for the full list of reason values.

Interruption handling

Speaker interrupted

In live scenarios, if a speaker is interrupted:

  • Upstream detects the speech boundary.
  • Current segment: input_audio_buffer.speech_stoppedconversation.item.input_audio_transcription.completed.
  • A new segment starts with 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.
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);
  }
};