Quick answer
Gate the tool, not the audio. Deepgram Voice Agent holds agent audio until the turn is confirmed but dispatches function calls immediately: its per-function defer_until_eot field defaults to false. Of 6 platforms checked it is the only per-tool gate conditioned on turn state; LiveKit Agents ships preemptive generation on by default. Put every tool on a read list or a commit list, then gate the commit list.
One layer only: what the tool layer does while a turn is still open. Endpointing sits upstream and is ceded – which engine decides a turn, and with what tunable range, is compared across Deepgram Flux, ElevenLabs Scribe and Speechmatics in our comparison of STT endpointing surfaces, and the framework silence timers above the engine are in why your AI voice agent interrupts callers. Assume your endpointing is as good as it will get, and that callers still sometimes carry on after the agent thought they had stopped.
Why did my AI voice agent book the appointment while the caller was still talking?
Because two things happen at different moments and only one of them waits. Deepgram’s documentation names both: the agent starts thinking when speech-to-text is moderately confident the caller has stopped, and is cleared to reply when it is confident. The gap between them is the speculative window, and working inside it is where the latency saving comes from. If the caller turns out not to have finished, the turn resumes and the speculative reply is discarded.
Audio is safe in that window. Deepgram’s three-row table of what waits for turn confirmation gives agent audio Yes and the user’s ConversationText Yes. The third row is the problem: function calls get No, by default, because a tool call is dispatched as soon as the LLM emits it.
So end_call hangs up the line, book_appointment writes the row and send_sms sends the message – on a turn the caller then continued. Deepgram’s own worked example is end_call: the caller says that is all they need, the agent starts thinking, end_call fires, the caller says actually, put me through to a person, and the turn resumes. Deepgram’s verdict on what cancellation achieves at that point is one sentence: cancellation is bookkeeping, and the telephony call is already hung up.
This is not the fault where the agent goes quiet while a tool runs – that is a latency chain, diagnosed in why your AI voice agent goes silent on tool calls. Here the tool ran too early, not too slowly.
Does a tool dispatch before the turn is confirmed? Six platforms, read 20 September 2026
Deepgram publishes three rows for its own stack. The table below extends the question across six platforms. Every cell was read from the named vendor’s own documentation on 20 September 2026; where a vendor publishes no answer, the cell says so rather than guessing.
| Platform | Does a tool dispatch before the turn is confirmed? | Per-tool gate | Default | What you get when it is cancelled |
|---|---|---|---|---|
| Deepgram Voice Agent | Yes. Deepgram’s own table answers No, by default to whether function calls are gated on confirmation | Yes – defer_until_eot on any function in agent.think.functions |
false |
FunctionCallCancelled listing the cancelled ids – but only for calls the client already received. A server-side function that reached your endpoint already ran |
| LiveKit Agents (Python 1.8.2, Node 1.9.0) | Preemptive generation starts LLM generation before the turn is confirmed and is on by default; the docs state only that TTS waits. What happens to a tool call emitted in that window is not documented | No turn-confirmation gate. Tool flags are NONE, IGNORE_ON_ENTER, CANCELLABLE; the documented remedy for writes is disallow_interruptions(), which protects a tool already running |
preemptive_generation.enabled True; preemptive_tts False; max_speech_duration 10.0 s |
Not documented. livekit/agents-js issue 2374, open since 29 August 2026, reports no output record, no span error attribute and no log line |
| Pipecat (pipecat-ai 1.11.0) | Only with the eager window on, and Pipecat does not document the function-call case either way. Pipecat documents that the response is held and the context written from the committed transcript; its eager-turn documentation never mentions function calls | No turn-confirmation gate. Per-tool options are cancel_on_interruption, timeout_secs and cancellable_by_llm |
enable_eager_end_of_turn False; cancel_on_interruption True; cancellable_by_llm False |
FunctionCallCancelFrame. Issue 5823, open 17 September 2026, reports a result arriving while the user is still speaking is silently dropped with no retry; issue 5779, open 15 September 2026, reports a call stuck in progress |
| Vapi | Not published as a turn-state question. No speculative window is documented: Vapi exposes Deepgram Flux’s committed end-of-turn threshold but no eager-window parameter | Yes, but on a different axis – rejectionPlan blocks a tool on regex or Liquid conditions over recent messages |
conditions: [], and Vapi states that when conditions are empty or omitted the tool always executes |
Not published |
| Retell AI | Retell answers the barge-in half directly, and the answer is no protection: an interrupted custom function request is not cancelled and runs to completion, so any action it takes still happens | No turn-confirmation gate. Per-function timing options are Talk While Waiting and Talk After Action Completed | Talk While Waiting off; Talk After Action Completed on; endpoint timeout 120000 ms, range 1000 to 600000 | Nothing. Retell’s own guidance is to make side-effecting actions idempotent so a repeat call is safe |
| ElevenLabs Agents | Yes, if you switch it on. ElevenLabs publishes speculative_turn, which starts generating the LLM response before full turn confidence is reached; it defaults to false, so the window is off until you ask for it. What happens to a tool call emitted in that window is not documented |
Yes, but on a different axis – interruption_mode per tool: allow, disable_during_tool, disable_during_tool_and_turn |
allow; speculative_turn false |
Not published |
Two readings matter more than the individual cells. First, of the six, Deepgram’s defer_until_eot is the only per-tool gate conditioned on turn state. Vapi’s rejectionPlan is genuinely evaluated before the tool executes, but on what the recent messages say rather than on whether the turn is confirmed – a regex that matches a goodbye will match it mid-sentence too. LiveKit’s disallow_interruptions(), ElevenLabs’ interruption_mode and Pipecat’s cancel_on_interruption all act after dispatch: they protect or cancel a call that is already running, and none of them delays the moment the request leaves. Second, the two vendors that document the cancellation case – Deepgram and Retell AI – reach the same conclusion from opposite directions: the request reached your endpoint, so your endpoint ran.
The read list and commit list rule
Call it the read list / commit list rule.
Every tool the agent can call goes on exactly one of two lists. Read-list tools may dispatch inside the speculative window. Commit-list tools must not dispatch until the turn is confirmed.
The test is one question, stricter than it sounds: if this call runs and the caller then keeps talking, can the agent itself undo it, unprompted, with a tool it already has, leaving nothing the caller will notice? If yes it is a read-list tool; if no it is a commit-list tool. Three things fail that test in ways teams routinely miss:
- Your API supporting deletion is not the test. The agent must hold the delete tool, be allowed to call it unprompted, and the deletion must leave no trace the caller sees. A cancellation email already in their inbox is a trace.
- Control-flow tools are commit tools.
end_calland transfer-to-number feel like routing rather than writing, which is why they get left off the list. They are the least reversible actions an agent has. - A read that reserves is a write. A
check_availabilitycall that places a hold on the slot is a commit-list tool wearing a read-list name. Check what the integration does, not what it is called – the subject of whether an AI agent can actually book into your calendar.
The second half keeps the agent fast. Deepgram notes that dispatching most functions early is where the latency advantage comes from, and that deferring one function does not delay the others. Gating your read list buys nothing and costs milliseconds on every lookup.
The five steps, starting on day one
Half a day on a stack you already run, with no new dependency.
- Day one, first hour: write the two lists. Open your tool definitions and put every tool on exactly one list, built-ins included – Vapi’s End Call and Transfer Call, Retell’s End Call and Send SMS, ElevenLabs’
end_callsystem tool, LiveKit’s prebuiltEndCallTool. Step one is finished when you have a file with two lists, no tool on both and none missing. - Find out whether your platform has a speculative window, and whether it is on. On Deepgram Voice Agent it is always on and function calls are not gated. On LiveKit Agents
preemptive_generation.enableddefaults toTruewithmax_speech_durationat 10.0 seconds, so it is attempted on nearly every caller turn of a booking call. On Pipecat the eager path is opt-in:enable_eager_end_of_turnonDeepgramFluxSTTServicedefaults toFalse. On ElevenLabs Agents it isconversation_config.turn.speculative_turn, which defaults tofalse, so the window is off until you turn it on. On Vapi and Retell AI it is not published – treat it as unknown and measure. - Gate the commit list with the platform’s own field, if it has one. On Deepgram that is one line per function:
"defer_until_eot": trueinsideagent.think.functions. Read the ordering caveat in Deepgram’s reference first – a function that did not opt in still dispatches immediately even with a deferred call queued ahead of it, so calls within a turn can complete out of order. If your commit tool depends on a read tool’s result, that ordering is yours to enforce. - Where there is no field, move the commit behind a confirmation the turn cannot outrun. Make the in-window tool a proposal: it validates, reserves nothing and returns the arguments it would have used. Your server commits only once your application has seen a confirmed end-of-turn for that turn. Where the platform offers a content gate, use it as a second layer – Vapi’s
rejectionPlandocumentation carries this pattern as its first example, rejectingendCallunless the caller’s most recent message matches a goodbye. Do not mistakedisallow_interruptions()orinterruption_modefor this step: they protect a call already in flight. - Make every commit-list endpoint idempotent, then test the resume. Retell AI’s own FAQ says why: the request is not cancelled, so the action still happens, and Deepgram says the same for the server-side case. Then run the one test that reproduces it – a scripted call where the caller pauses about two seconds mid-sentence and then continues, timed to land where the agent would commit. Build it into the pre-launch pass in how to test an AI voice agent before go-live; unscripted test calls do not reproduce it reliably.
You are finished when, for every commit-list tool, you can name the mechanism holding it, produce one log line showing it dispatched after turn confirmation, and the scripted resume test leaves one record in the booking system rather than two or zero.
One caveat on that log line: livekit/agents-js issue 2374, open since 29 August 2026, reports that a cancelled function tool leaves no output record, no span error or output attribute and no log entry. An empty log is not evidence that nothing ran. Query the booking system for records created in the window instead.
Work out your own exposure before you change anything
Four numbers give the size of the problem. None of the rates below is a measurement of ours; they come from your own logs.
- Resumed turns per 100 caller turns. On Deepgram, turns where
UserStartedSpeakingarrives after the agent began thinking; on Pipecat,EagerEndOfTurnCancelFrameevents; on LiveKit, discarded preemptive attempts, capped atmax_retries3 per turn. - Caller turns per call, from your transcripts, and calls per day.
- The share of resumed turns where a commit-list call had already been emitted. Almost nobody has this one, which is why the arithmetic is worth doing before the fix.
Worked through with illustrative inputs, so you can substitute yours: 400 calls a day at 8 caller turns each is 3,200 caller turns. At 6 resumed turns per 100, that is 192 resumed turns a day. If 1 in 25 of those had already emitted a commit-list call, roughly 8 irreversible actions a day fired on a turn the caller then continued – 54 a week on the unrounded 7.68. Then price one: a double booking costs a slot, a call and a rebooking, and an end_call at the wrong moment costs the enquiry outright.
Below about one a week, gate the commit list anyway – on Deepgram it is a one-line change – and leave the rest alone. Above roughly ten a week, step four is worth building properly.
What gating the commit list costs to run
The method is complete and a competent engineer can run it in a day. What it costs to keep running is the honest part.
| Your situation | Do it yourself | Hand it over |
|---|---|---|
| One platform, one agent, read-only tools only | Yes – the two lists take an hour and nothing needs gating | Not worth it |
| LiveKit Agents or Pipecat with bookings, payments or transfers | Workable, but you are building step four yourself and maintaining it | Reasonable |
| Two or more platforms, or a platform change mid-quarter | Costly – every default in the table above is per platform and moves without notice | Reasonable |
| Outbound booking at volume across several countries and languages | Ongoing – resumed-turn rates differ by language and pause habit, so the arithmetic is per route | Reasonable |
| Audio and caller data cannot leave your infrastructure | Only if you can self-host every layer | Only with private model deployment on your own infrastructure |
The arithmetic is yours. A single-platform agent with a short commit list is a day of work and then it is done. At the volume Zian AI reports – 50,769+ qualified sales appointments set – the commit list has to be re-checked against each vendor’s defaults every time a platform ships, which is the real cost the table measures. Zian AI runs booking agents of exactly this shape – the Outbound Appointment Setter and the Appointment Show-Specialist – and supports private model deployment on customer infrastructure where audio cannot leave. Zian publishes no turn-gating field of its own, so put step one and step five to us the same way you would put them to any vendor on your shortlist. If you would rather own it, the five steps are the whole method; our comparison of AI appointment setting software covers what to look for in a platform that already does it.
Where every figure on this page comes from
| Figure | Who published it | Link (their own page) | Date read |
|---|---|---|---|
defer_until_eot exists, and the default is false |
Deepgram | developers.deepgram.com changelog, 8 September 2026 | 20 September 2026 |
| Function calls are not gated on turn confirmation (No, by default); and a server-side function that reached your endpoint already ran, so defer rather than rely on cancellation | Deepgram | Speculative Replies and Turn Confirmation | 20 September 2026 |
| Out-of-order completion when one function defers and another does not | Deepgram | Configure the Voice Agent | 20 September 2026 |
FunctionCallCancelled covers only calls the client already received |
Deepgram | Function Call Cancelled | 20 September 2026 |
preemptive_generation.enabled True, preemptive_tts False, max_speech_duration 10.0 s, max_retries 3 |
LiveKit | TurnHandlingOptions reference | 20 September 2026 |
| Only the LLM runs preemptively; TTS waits for turn confirmation | LiveKit | Preemptive speech generation | 20 September 2026 |
Tool flags NONE, IGNORE_ON_ENTER, CANCELLABLE; disallow_interruptions() for mutating calls |
LiveKit | Function tools | 20 September 2026 |
enable_eager_end_of_turn defaults to False |
Pipecat | Deepgram Speech-to-Text service | 20 September 2026 |
| Nothing the speculation produces reaches the user or the context; the response is held until confirmation | Pipecat | User turn strategies | 20 September 2026 |
cancel_on_interruption True, cancellable_by_llm False |
Pipecat | Function Calling | 20 September 2026 |
With empty or omitted conditions the tool always executes; the first example rejects endCall unless the caller says goodbye |
Vapi | Tool rejection plan | 20 September 2026 |
| An interrupted custom function request is not cancelled and runs to completion; make such actions idempotent | Retell AI | Integrate any system with custom function | 20 September 2026 |
interruption_mode per tool, default allow; disable_interruptions deprecated |
ElevenLabs | Tool interruptions | 20 September 2026 |
conversation_config.turn.speculative_turn starts LLM generation before full turn confidence is reached, and defaults to false |
ElevenLabs | Create agent API reference | 20 September 2026 |
| Issue 5823, open, opened 17 September 2026 – a result arriving while the user is still speaking is dropped, no retry | pipecat-ai on GitHub | pipecat-ai/pipecat issue 5823 | 20 September 2026 |
| Issue 5779, open, opened 15 September 2026 – an interrupted tool call stuck in progress | pipecat-ai on GitHub | pipecat-ai/pipecat issue 5779 | 20 September 2026 |
| Issue 2374, open, opened 29 August 2026 – a cancelled tool leaves no output, no span attribute and no log | livekit on GitHub | livekit/agents-js issue 2374 | 20 September 2026 |
| Current releases: pipecat-ai 1.11.0 (18 September 2026), livekit-agents 1.8.2 (15 September 2026), @livekit/agents 1.9.0 (15 September 2026) | PyPI and npm | pypi.org/project/pipecat-ai, pypi.org/project/livekit-agents, registry.npmjs.org @livekit/agents | 20 September 2026 |
Frequently asked questions
Does my AI agent book the appointment if the caller keeps talking?
On most stacks, yes. Deepgram states in its own documentation that function calls are not gated on turn confirmation by default, so a booking call dispatches as soon as the model emits it. Setting defer_until_eot to true on that one function holds it until the turn is confirmed and discards it if the caller resumes, per the Deepgram changelog entry of 8 September 2026.
What causes AI voice agent double booking?
Two causes needing two fixes. The first is a retracted turn: the agent emits book_appointment, the caller carries on and amends the slot, and a second booking follows the first. The second is a tool call interrupted rather than stopped. Retell AI documents that an interrupted custom function request is not cancelled and runs to completion, which is why its own guidance is to make such actions idempotent.
Can I stop a tool call after it has already fired?
Not reliably, and the clearest statement of why comes from the vendor that built the gate. Deepgram notes that a server-side function which already reached your endpoint is a different matter, because Deepgram discards the response but your endpoint ran; see Speculative Replies and Turn Confirmation. Cancellation is an accounting entry, not a rollback.
Do LiveKit Agents or Pipecat have a defer-until-end-of-turn setting?
Neither documents one as at 20 September 2026. LiveKit offers disallow_interruptions, which protects a tool already running, and Pipecat offers cancel_on_interruption, which cancels one. Both act after dispatch, not before it. Open issues on both trackers describe the gap: pipecat 5823 and 5779, and livekit agents-js 2374.
Should read-only tools wait for turn confirmation too?
No, and holding them is the main cost of getting the split wrong. Deepgram notes that dispatching most functions early is where the latency advantage comes from, and that deferring one function does not delay the others. A lookup that only reads can run inside the speculative window and be thrown away for nothing.
A tool call is the only part of a voice agent that touches the real world, and it is the one part most platforms do not hold back. If you would rather have the commit list gated, tested and re-checked against each vendor’s defaults than own that yourself, Zian AI is in partnership-application beta. Apply For Partnership.