Detect when a speaker has finished their turn.
Streaming transcription is delivered in turns. A turn is one continuous unit of speech belonging to a single speaker. Deciding when a turn has ended, called endpointing, determines how an interactive voice application feels. End turns too early and you cut speakers off mid-thought or split a complex entity like a phone number into multiple parts. End turns too late and your voice agent feels unresponsive.
How turn detection works
Universal-3.5 Pro Streaming does not endpoint on silence alone. When a speaker pauses, the model evaluates both acoustic and contextual cues, meaning how the speech sounded and what has been said so far, to judge whether the speaker is finished or just pausing mid-thought.
Two silence thresholds control this behavior.
| Parameter | What it does | Default |
|---|
min_turn_silence | Silence in milliseconds before the model runs an end-of-turn check. If the turn reads as complete, the turn ends. Otherwise a partial is emitted and the turn stays open. | Set by mode (min_latency: 128, balanced: 224, max_accuracy: 800) |
max_turn_silence | Maximum silence in milliseconds before the turn is forced to end, regardless of content. This is the fallback for turns that never read as complete. | 1536 |
Lower values end turns faster. Higher values tolerate longer pauses without fragmenting the turn. Both parameters can be set on the connection and updated mid-stream, and their defaults come from the mode preset you select.
Speaker labels change the defaultsWhen speaker_labels is enabled, the server applies diarization-tuned defaults instead (min_turn_silence: 640, max_turn_silence: 768, continuous partials disabled) so turns break cleanly at speaker changes.
The lifecycle of a turn
Each turn produces a predictable message sequence of one SpeechStarted message, one or more partial Turn messages, and a final Turn message.
Every Turn message re-transcribes the full turn audio, so each message supersedes the previous one. Always replace what you have rendered rather than appending. This also means transcripts get more accurate as the turn progresses, since each pass gives the model more audio and more context to work with. For maximum accuracy, use the final transcript marked end_of_turn: true. Use partials where responsiveness matters more than accuracy.
SpeechStarted
The server sends SpeechStarted once per turn, immediately before the turn’s first Turn message. It carries the turn’s start timestamp in milliseconds from the start of the audio stream, and the confidence of a new turn starting.
{
"type": "SpeechStarted",
"timestamp": 1216,
"confidence": 0.98
}
SpeechStarted is only emitted once the model produces an actual transcript, so background noise alone does not trigger it. That makes it a reliable barge-in signal for voice agents. When it arrives, stop your agent’s TTS playback and start listening.
Partials
While the speaker talks, the session emits Turn messages with end_of_turn: false.
{
"type": "Turn",
"turn_order": 0,
"end_of_turn": false,
"turn_is_formatted": false,
"transcript": "my number is five five five",
"end_of_turn_confidence": 0.0
}
Partials are emitted at three points.
- Early partials are emitted shortly after speech begins, timed by the
interruption_delay parameter.
- Pause partials are emitted when a pause triggers an end-of-turn check but the turn reads as incomplete.
- Continuous partials are emitted roughly every 3 seconds during uninterrupted speech (
continuous_partials, enabled by default), so long utterances keep updating even without pauses.
Partials are suited to live captions, barge-in handling, and speculative LLM prefetching, where you want text immediately and can tolerate it being revised.
Finals
When the turn ends, the session emits a Turn message with end_of_turn: true. The final is fully formatted, with punctuation, casing, and numbers and entities rendered in written form.
{
"type": "Turn",
"turn_order": 0,
"end_of_turn": true,
"turn_is_formatted": true,
"transcript": "My number is 555-0134.",
"end_of_turn_confidence": 1.0
}
The final is the most accurate transcript of the turn. Send it to your LLM, store it, and act on it whenever accuracy matters.
Tuning interruption sensitivity
SpeechStarted and early partials are what trigger interruptions in a voice agent, and two parameters control how eagerly they fire.
| Parameter | What it does | Default |
|---|
vad_threshold | Confidence between 0 and 1 required to classify an audio frame as speech. | 0.2 |
interruption_delay | How long in milliseconds after speech begins the first partial is emitted, from 0 to 1000. | Set by mode (min_latency: 0, balanced: 500, max_accuracy: 500) |
If quiet or distant speech is being missed, lower vad_threshold so softer audio counts as speech. If background noise or the agent’s own TTS audio is triggering false interruptions, raise vad_threshold so the session requires stronger evidence of speech, increase interruption_delay so it waits longer before emitting the first partial, or enable Voice Focus to suppress background audio before it reaches the model.
Like the silence thresholds, both can be set on the connection and updated mid-stream.
Capturing entities like phone numbers
Speakers naturally pause between the groups of a phone number, credit card, or address, and each of those pauses can read as a completed thought, splitting one entity across several turns. When your application knows an entity is coming (for example, your voice agent just asked for a callback number), raise min_turn_silence mid-stream so those pauses don’t end the turn. Afterward, set your mode again to restore the preset’s defaults. Changes take effect immediately, without reconnecting.
Python
Python SDK
Javascript
JavaScript SDK
# Before capturing the entity
websocket.send('{"type": "UpdateConfiguration", "min_turn_silence": 1000}')
# After the response, re-apply your mode to restore its defaults
websocket.send('{"type": "UpdateConfiguration", "mode": "balanced"}')
# Before capturing the entity
client.update_configuration(min_turn_silence=1000)
# After the response, re-apply your mode to restore its defaults
client.update_configuration(mode="balanced")
// Before capturing the entity
websocket.send('{"type": "UpdateConfiguration", "min_turn_silence": 1000}');
// After the response, re-apply your mode to restore its defaults
websocket.send('{"type": "UpdateConfiguration", "mode": "balanced"}');
// Before capturing the entity
transcriber.updateConfiguration({ minTurnSilence: 1000 });
// After the response, re-apply your mode to restore its defaults
transcriber.updateConfiguration({ mode: "balanced" });
If turns are still force-ended during very long pauses, raise max_turn_silence the same way. See Updating configuration mid-stream for the full list of mid-stream parameters.
Bring your own turn detection
If you run your own VAD or turn detection model, send a ForceEndpoint message to end the current turn immediately. The server responds with the turn’s final Turn message right away, without waiting for silence.
Python
Python SDK
Javascript
JavaScript SDK
websocket.send('{"type": "ForceEndpoint"}')
websocket.send('{"type": "ForceEndpoint"}');
transcriber.forceEndpoint();
Going further