Turn detection is what makes a call feel human. Your agent should stop the instant the caller starts talking (barge-in) and reply the instant the caller finishes (end-of-turn) — without cutting off someone who only paused to think, and without cancelling itself when the caller mutters “mhm” to keep it going. You get this by picking a detection mode per agent and dialing in a handful of timings. The interrupt signal is wired end to end for you across every transport — PSTN, browser, and the WebRTC fallback leg — so the same configuration behaves the same way on a phone call and in a browser tab.

Three ways a turn gets detected

Every agent uses exactly one of these. The right choice follows from how your pipeline is shaped.
ModeWho decides the turnUse it when
On-device VAD (default)The runtime, on the caller’s audio framesCascaded ASR → LLM → TTS agents (for example Deepgram → your LLM → ElevenLabs). Works on any transport.
server_vadThe realtime provider’s own endpointingRealtime speech-to-speech models that already emit speech-start / speech-stop events (OpenAI Realtime, Gemini Live).
Client-commitYour app / the runtime’s local detectorSelf-hosted speech-to-speech models that expose no server-side VAD — turn boundaries are committed explicitly.
On-device VAD is the default because most agents are cascaded and need a local trigger to know when to hand the utterance to the ASR stage. When an agent’s entry node is a realtime provider and you do not set a mode explicitly, the runtime auto-promotes the default to server_vad — the realtime session owns the audio path end to end, so a local detector would never actually run.
Two on-device backends exist: a lightweight built-in energy gate (type: "energy") and a higher-accuracy on-device VAD model, which is the default you get by omitting type. Both feed the identical turn-taking and barge-in logic below, so switching backends never changes your timing knobs.

Configure turn detection per agent

Turn detection lives in your agent config under turn_detection. Every field is optional — omit the block entirely and you get the phone-safe defaults.
{
  "entry_node": "ASR",
  "pipeline": { "ASR": { }, "LLM": { }, "TTS": { } },
  "turn_detection": {
    "prefix_padding_ms": 300,
    "silence_duration_ms": 500,
    "params": {
      "barge_confirm_ms": 300,
      "response_min_speech_ms": 600,
      "answer_min_speech_ms": 150
    }
  }
}
{
  "entry_node": "REALTIME",
  "pipeline": { "REALTIME": { } }
  // turn_detection omitted → auto-promoted to { "type": "server_vad" }
}
You can always override the auto-promotion — set "type": "energy" on a realtime agent to force local detection instead. An explicit type wins over the default in every case. Set this on the agent from your workspace console at agent.telequick.dev, or push it through the control-plane API. See Runtime configuration for the full agent-config shape.

Tune when the agent replies

Start with the defaults, then reach for these. Lower for snappier replies; raise for callers who pause to gather their thoughts.
FieldDefaultWhat it doesWhen to change
silence_duration_ms500Trailing silence before end-of-turn firesLower (~300) for snappier replies; raise (~800) for deliberate callers
prefix_padding_ms300Pre-speech audio kept ahead of the speech-start markerRaise if the ASR clips first syllables
threshold0.5Speech-probability gate for the on-device VAD modelRaise on noisy trunks to reject background chatter
params.answer_min_speech_ms150Shortest burst that counts as a real answer once the agent has yieldedKeep low so terse replies (“yes”, a digit) still commit
params.response_min_speech_ms600Shortest burst that counts as a real interruption over the agentRaise to suppress more backchannels; lower to barge more eagerly
params.silence_floor5Accumulator level treated as “quiet again”Rarely touched; lower on very quiet lines
The split between answer_min_speech_ms and response_min_speech_ms is deliberate: a short burst means opposite things depending on when it lands. In the answering window (the agent has stopped and is waiting), even a one-syllable “yes” is a real turn. Over the agent while it is talking, that same short burst is almost always a continuer (“mhm”, “ok”, “right”) — so the over-agent floor is higher, and backchannels never cancel the response.

Hold-and-confirm barge-in

Barge-in is armed automatically whenever the agent is speaking. The mechanism is hold-and-confirm, controlled by params.barge_confirm_ms (default 300):
1

Onset arms a pending barge

The instant the caller starts speaking over the agent, a pending barge is armed — but the agent is not cancelled yet.
2

Sustained speech confirms it

Only if that speech continues past barge_confirm_ms does the runtime actually fire the interrupt and cancel the in-flight response.
3

Backchannels self-cancel

A short “mhm” or “ok” ends before the window elapses, so the pending barge is dropped and the agent keeps talking. The only cost of a real interruption is the ~barge_confirm_ms of extra latency before the cut.
Set barge_confirm_ms: 0 to disable the hold and fire the interrupt on the very first frame of over-talk. Use 0 only on transports with client-side echo cancellation (the browser path), where a snappy cut is wanted and the agent’s own audio cannot bleed back into the mic.
There is no acoustic echo cancellation in the telephony media path. On PSTN the agent’s own TTS would otherwise leak back through the caller’s mic and self-trigger a barge. To prevent this, the runtime raises the during-speech mic gate (params.margin_while_tts) to match the quiet-line gate (params.margin) on phone calls, so the agent’s voice can never cancel the agent. On the browser path, where the client cancels echo, you can lower margin_while_tts for a faster barge.
Backchannel suppression here is acoustic only — it is based on how long the caller speaks, not what they say. Lexical gating (treating “stop” differently from “uh-huh”) and provider-side semantic endpointing are not built today. If you need word-aware interrupts, handle them in your own tool logic.

Where barge-in actually works

Barge-in quality depends on whether the downstream provider can be cancelled mid-flight. The on-device VAD always detects the interruption; whether the agent’s audio actually stops cleanly is the honest question. A “partial” row is a known limitation, not a misconfiguration.
Provider pathMid-flight cancelWhat happens on barge
OpenAI Realtime / Gemini Live (server_vad)CleanRuntime sends the provider’s interrupt/truncate; the session stops generating
Streaming TTS (ElevenLabs, Deepgram, and similar)CleanOutbound audio buffer is flushed within a frame; playback halts immediately
Streaming ASRCleanCancel propagates; the partial transcript is discarded
HTTP (non-streaming) LLMPartialPlayback is silenced locally, but the request runs to completion server-side and is still billed
And by transport leg, where the caller’s audio actually comes from:
TransportInterrupt sourceStatus
SIP / RTP (PSTN trunk)On-device VAD on the caller’s RTP → runtime cancelEnd to end
Browser (WebTransport / media-over-QUIC)Caller mic decoded → on-device VADSame path as PSTN
WebRTC fallback legServer-side WebRTC termination → on-device VADSame path as PSTN
External vendor bridge (LiveKit / Vapi / Twilio)Depends on the vendor’s own interrupt eventVaries by adapter
When you route media through an external agent-orchestration vendor rather than the built-in pipeline, the barge signal is only as good as that vendor’s adapter. For the tightest interrupt budgets, keep the media on the native SIP/RTP, browser, or WebRTC-fallback path.

Idle watchdog

The flip side of barge-in is silence. When both the caller and the agent have gone quiet, the idle watchdog can re-engage or end the call — configured under turn_detection.idle:
  • soft_prompt_s — after this many idle seconds, nudge the model to speak (soft_prompt_text, or a bare re-engage on a realtime model).
  • hangup_s — after this many idle seconds, drop the AI leg.
Both default to 0 (off) in the raw schema, but the runtime ships sensible ceilings — a re-engage prompt, a hangup, a capped response length, and a hard maximum session duration — so a stuck agent can’t burn a call forever. See Session lifecycle for those guardrails.

Common gotchas

silence_duration_ms too low (< 300 ms). End-of-turn fires on the natural pauses between words, so the agent replies mid-sentence. Raise it before you blame the ASR.
server_vad on a cascaded agent. Only a realtime provider emits server-side VAD events. A Deepgram → LLM → ElevenLabs agent set to server_vad will never see a turn boundary and will sit silent. Use on-device VAD (omit type) instead.
Delayed barge on an HTTP LLM. Non-streaming LLM calls run to completion server-side. The runtime silences playback the moment the caller barges, but the provider still finishes — and bills — the full response. For tight barge budgets, use a streaming or realtime model.

Runtime configuration

The full agent-config shape, including where turn_detection lives.

Handoffs

What a barge-in and end-of-turn feed into when you transfer to a human.

Bring your own speech-to-speech

The client-commit path for self-hosted realtime models with no server VAD.

Debug barge-in

Trace why an interrupt did or didn’t fire on a live call.