|
| 1 | +# Transfer & Handoff Troubleshooting |
| 2 | + |
| 3 | +A diagnostic guide for when `transferCall` or `handoff` tools don't work as expected. Walk through these steps in order to identify and fix the issue. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## Step 1: Confirm whether the tool call happened at all |
| 8 | + |
| 9 | +### Symptom |
| 10 | + |
| 11 | +Caller hears something like "Please hold while I transfer you" and then nothing happens — silence, call times out, or the original assistant continues talking. |
| 12 | + |
| 13 | +### What it usually means |
| 14 | + |
| 15 | +The model produced a text-only response instead of a tool call. This is common when the tool description is vague or the prompt doesn't explicitly require the tool call. |
| 16 | + |
| 17 | +### How to fix |
| 18 | + |
| 19 | +Make tool execution rules explicit in the system prompt. Put them at the **end** of the prompt so they're freshest in the model's context window. |
| 20 | + |
| 21 | +**Recommended prompt pattern** — add this at the end of your system prompt: |
| 22 | + |
| 23 | +``` |
| 24 | +CRITICAL TOOL-CALL RULES — these override any ambiguity above: |
| 25 | +
|
| 26 | +1. Whenever you decide to transfer, you MUST invoke the transferCall |
| 27 | + function in that same response. |
| 28 | +2. Your spoken acknowledgment and the transferCall tool call happen |
| 29 | + in the SAME response turn. |
| 30 | +3. If you already said "I'll connect you now" but the call is still |
| 31 | + active, immediately invoke transferCall again without saying |
| 32 | + anything else. |
| 33 | +``` |
| 34 | + |
| 35 | +--- |
| 36 | + |
| 37 | +## Step 2: Fix tool descriptions that make the model reluctant |
| 38 | + |
| 39 | +### Symptom |
| 40 | + |
| 41 | +The assistant "knows" it should transfer, but it frequently doesn't. |
| 42 | + |
| 43 | +### What it usually means |
| 44 | + |
| 45 | +Some tool configurations end up with language like "DO NOT call this function unless instructed" in the tool description the LLM sees. This biases the model toward not calling it. |
| 46 | + |
| 47 | +### How to fix |
| 48 | + |
| 49 | +- **Always set an explicit `function.description`** on your transferCall/handoff tools. Without a custom description, some setups inject overly cautious guardrails into the tool definition. |
| 50 | +- **Make destination `description` fields specific and use-case oriented.** The LLM uses these descriptions to select the right destination — they're effectively part of your routing policy. |
| 51 | + |
| 52 | +```yaml |
| 53 | +destinations: |
| 54 | + - type: number |
| 55 | + number: "+15551234567" |
| 56 | + description: "Transfer to the billing department for payment and invoice questions" |
| 57 | + message: "Let me connect you with our billing team." |
| 58 | +``` |
| 59 | +
|
| 60 | +--- |
| 61 | +
|
| 62 | +## Step 3: Use the right mechanism (transferCall vs handoff) |
| 63 | +
|
| 64 | +### Symptom |
| 65 | +
|
| 66 | +You're trying to transfer to another assistant in a squad, but it behaves like it's dialing a phone number, hallucinates a destination, or intermittently stalls. |
| 67 | +
|
| 68 | +### What it usually means |
| 69 | +
|
| 70 | +`transferCall` is a **telephony-forwarding** primitive — it dials phone numbers and SIP URIs. For transferring between assistants in a squad, use the `handoff` tool type instead. |
| 71 | + |
| 72 | +### When to use each |
| 73 | + |
| 74 | +| Mechanism | Use when | |
| 75 | +|-----------|----------| |
| 76 | +| `transferCall` | Transferring to an external phone number, SIP URI, or PBX | |
| 77 | +| `handoff` | Transferring between assistants within a squad | |
| 78 | +| `assistantDestinations` on squad members | Shorthand for handoff — Vapi converts these to handoff tools automatically | |
| 79 | + |
| 80 | +Using `transferCall` for assistant-to-assistant routing can cause the original assistant to continue with an error message when the transfer doesn't work as expected. |
| 81 | + |
| 82 | +--- |
| 83 | + |
| 84 | +## Step 4: Distinguish "platform executed transfer" from "telephony transfer succeeded" |
| 85 | + |
| 86 | +### Symptom |
| 87 | + |
| 88 | +The call's `endedReason` says the transfer happened (e.g., `assistant-forwarded-call`), but the destination never rang. |
| 89 | + |
| 90 | +### What it usually means |
| 91 | + |
| 92 | +`assistant-forwarded-call` means "Vapi initiated the transfer," not "the downstream provider successfully completed it." The telephony leg can still fail. |
| 93 | + |
| 94 | +### Common telephony failure modes |
| 95 | + |
| 96 | +**SIP REFER not supported by provider:** |
| 97 | +Some SIP trunks/providers don't support the REFER method. The call ends during transfer with no ring at the destination. Fix: remove explicit `sipVerb: "refer"` from your `transferPlan` and let Vapi use the default transfer mechanism. |
| 98 | + |
| 99 | +**Provider-specific auth issues:** |
| 100 | +Some providers (especially with SIP REFER) reject the transfer due to authentication header issues. The call looks "forwarded" on the Vapi side but drops at the provider level. Check your provider's transfer compatibility docs. |
| 101 | + |
| 102 | +### How to verify |
| 103 | + |
| 104 | +Check the call's `endedReason` and provider-level call logs: |
| 105 | +- `assistant-forwarded-call` = Vapi sent the transfer command |
| 106 | +- Actual ring/answer = check your telephony provider's logs/CDRs |
| 107 | + |
| 108 | +--- |
| 109 | + |
| 110 | +## Step 5: Watch for transient assistant foot-guns |
| 111 | + |
| 112 | +If you're using transient assistants (created per-call via API), two extra issues arise: |
| 113 | + |
| 114 | +### Tool descriptions drift between calls |
| 115 | + |
| 116 | +If your tool's `description` isn't explicitly set, the auto-generated description can vary slightly between calls. This inconsistency can reintroduce LLM reluctance to call the tool. |
| 117 | + |
| 118 | +**Fix:** Treat `function.description` as required configuration. Set it explicitly and keep it stable across calls. |
| 119 | + |
| 120 | +### One-shot transfer flows are fragile |
| 121 | + |
| 122 | +Transient assistants often appear in flows where you want immediate transfer behavior. Any prompt ambiguity increases the chance the model speaks first and stalls instead of calling the tool. |
| 123 | + |
| 124 | +**Fix:** Put transfer rules at the end of the system prompt. Prefer a single atomic response that both acknowledges and invokes the tool. |
| 125 | + |
| 126 | +--- |
| 127 | + |
| 128 | +## Quick Triage Checklist |
| 129 | + |
| 130 | +Use this checklist when debugging transfer issues: |
| 131 | + |
| 132 | +1. **Did a tool call appear?** Check the call transcript/messages. If the assistant only said "I'll transfer you" without a tool call, it's a prompt issue (Step 1). |
| 133 | +2. **Did the tool call error?** Check for validation or configuration errors in the tool call result. If so, it's a config issue (Step 2). |
| 134 | +3. **Is this a telephony failure?** Check if `endedReason` shows `assistant-forwarded-call` but the destination never rang. If so, it's a provider/SIP issue (Step 4). |
| 135 | +4. **Are you using the right tool type?** If transferring between squad assistants, use `handoff` not `transferCall` (Step 3). |
| 136 | +5. **Are you seeing intermittent "trouble accessing the system" messages?** This is often the LLM generating error text after a failed tool result — check the tool call result for errors. |
0 commit comments