diff --git a/fern/calls/assistant-based-warm-transfer.mdx b/fern/calls/assistant-based-warm-transfer.mdx
index 19910aa2a..19abf9e19 100644
--- a/fern/calls/assistant-based-warm-transfer.mdx
+++ b/fern/calls/assistant-based-warm-transfer.mdx
@@ -17,7 +17,7 @@ GPT-Live assistants cannot use assistant-based warm transfer. GPT-Live supports
Before you configure the transfer, prepare:
-- A Vapi assistant that handles phone calls
+- A Vapi assistant that handles phone calls on a Twilio number, a Vapi number, or a bring-your-own SIP trunk. On other phone providers, the call is transferred without the transfer assistant.
- A destination phone number in E.164 format, for example, `+14155550100`
- A [private Vapi API key](/security-and-privacy/api-keys) for the curl method
- A publicly accessible MP3 or WAV file if you want custom hold or completion audio
@@ -30,7 +30,7 @@ When the original assistant invokes the transfer call tool:
2. Vapi calls the destination and starts the transfer assistant.
3. The transfer assistant speaks with the operator and uses the previous conversation as context by default.
4. The transfer assistant calls `transferSuccessful` to connect the parties or `transferCancel` to return the customer to the original assistant.
-5. If the destination is busy, unreachable, or not human, the fallback plan determines what the customer hears and whether the call ends.
+5. With `transferAssistant` configured, a busy or unanswered destination rolls back to the configured fallback. The transfer assistant can also cancel when an operator declines or it reaches voicemail or an automated system. A transfer that doesn't finish within `transferAssistant.maxDurationSeconds` (120 seconds by default) rolls back.
The `transferSuccessful` and `transferCancel` tools are always available to the transfer assistant. You can customize them through the API, but you do not need to add them to a basic configuration.
@@ -159,7 +159,7 @@ Use these fields to control the transfer assistant and the two call legs. See th
| `transferAssistant.model` | Model and instructions used for the operator conversation | Required when you configure `transferAssistant` |
| `holdAudioUrl` | MP3 or WAV audio played to the customer while on hold | Default hold audio |
| `transferCompleteAudioUrl` | MP3 or WAV audio played to the destination after the introduction | No custom completion audio |
-| `fallbackPlan` | Customer message and end-call behavior when the transfer fails | Default fallback behavior when omitted |
+| `fallbackPlan` | Customer message and end-call behavior after a failed warm transfer | If omitted, the original assistant says "Could not transfer the call." and continues the conversation |
| `summaryPlan` | Operator summary content and generation timeout | Configure with `enabled`, `messages`, and `timeoutSeconds` |
| `contextEngineeringPlan` | Conversation context provided to the transfer assistant | All previous messages |
@@ -170,7 +170,7 @@ Use these fields to control the transfer assistant and the two call legs. See th
Keep the transfer assistant's system message focused on the operator conversation. Define when to use each built-in tool:
- Call `transferSuccessful` after a human operator confirms that they will take the call
-- Call `transferCancel` for voicemail, a busy signal, no answer, an IVR or automated hold queue, or an operator who declines
+- Call `transferCancel` when it hears voicemail, an interactive voice response (IVR) system, an automated hold queue, or an operator declining the call
- Answer questions about the transfer or give a brief customer summary
- Avoid unrelated conversation and complete the decision before `maxDurationSeconds`
@@ -180,7 +180,13 @@ For example:
Call transferCancel if the destination plays an IVR message, automated queue announcement, or hold message. Call transferSuccessful only after a live operator responds and accepts the call.
```
-The transfer assistant receives the previous conversation by default. Set `contextEngineeringPlan.type` to `none` when the operator must not receive that context, or use `lastNMessages` to limit it.
+Busy and no-answer call statuses can trigger rollback before the transfer assistant speaks. The transfer assistant receives the previous conversation by default. Set `contextEngineeringPlan.type` to `none` when the operator must not receive that context, or use `lastNMessages` to limit it.
+
+When you configure the transfer through the API, set `fallbackPlan.endCallEnabled`. With `false`, the original assistant says the fallback message and continues the conversation. With `true`, it says the message and ends the call. If omitted, the caller stays on the call with the original assistant.
+
+
+ Keep `transferPlan.transferAssistant` configured when you depend on no-answer fallback or a transfer timeout. In the Dashboard, turning off **Summary generation** can remove that configuration and prevent a no-answer event from starting the fallback. Keep **Summary generation** on or configure `transferAssistant` through the API, then verify the result with a controlled call.
+
## Verify the warm transfer
@@ -191,7 +197,7 @@ Place a test call and trigger the transfer. Confirm that:
3. Accepting the call connects the customer and operator.
4. Declining the call or reaching voicemail returns the customer to the original assistant or ends the call according to `fallbackPlan`.
-In [**Logs → Calls**](/observability/logs/call-logs), inspect the original call, destination call leg, transcript, and ended reason. To retrieve these details through the API, see the [Get Call API reference](/api-reference/calls/get). An `assistant-forwarded-call` ended reason confirms that the transfer was initiated; it does not confirm that the destination answered.
+In [**Logs → Calls**](/observability/logs/call-logs), inspect the original call, any available destination-leg record, transcript, and ended reason. To retrieve call details through the API, see the [Get Call API reference](/api-reference/calls/get). An `assistant-forwarded-call` ended reason alone does not confirm that the destination answered.
Assistant-based warm transfers to phone or SIP destinations do not send `transfer-update`, including when the call switches to the transfer assistant and back. A `status-update` event with `status: "forwarding"` signals that the transfer started, not that it succeeded. See [transfer event behavior](/server-url/events#transfer-update) for payloads and subscription requirements.
@@ -201,16 +207,16 @@ In [**Logs → Calls**](/observability/logs/call-logs), inspect the original cal
| Symptom | Likely cause | Resolution |
| --- | --- | --- |
-| The customer is transferred without an operator conversation | The destination uses a blind or non-assistant warm-transfer mode | Set `transferPlan.mode` to `warm-transfer-experimental`. |
+| The customer is transferred without an operator conversation | The destination uses a blind or non-assistant warm-transfer mode, the call uses an unsupported phone provider, or a Twilio call is transferring to a SIP address | Set `transferPlan.mode` to `warm-transfer-experimental`, and use a Twilio number, Vapi number, or bring-your-own SIP trunk. On Twilio, use a phone-number destination. |
| The transfer assistant never speaks | `firstMessage` is omitted or `firstMessageMode` waits for the operator | Set `firstMessage` and use `assistant-speaks-first`, or let the operator speak first. |
| The transfer assistant talks but never connects the parties | The prompt does not require `transferSuccessful` | Tell the transfer assistant to call `transferSuccessful` immediately after the operator accepts. |
-| Voicemail, an IVR, a hold queue, or a declined transfer connects anyway | The prompt does not define cancellation conditions | Require `transferCancel` for non-human destinations, busy signals, no answer, and declined transfers. |
+| Voicemail, an IVR, a hold queue, or a declined transfer connects anyway | The prompt does not define cancellation conditions | Require `transferCancel` for non-human destinations and declined transfers. Busy and no-answer call statuses can trigger rollback before the transfer assistant speaks. |
| The customer hears summary text instead of the fallback message | The transfer produced unexpected `warm-transfer-experimental` behavior | Record the affected call ID, the message the customer heard, whether `summaryPlan` was enabled, and the `endedReason`, then contact support. |
| The customer hears the wrong audio | Hold audio was configured as a tool message | Put customer hold audio in `transferPlan.holdAudioUrl`. Use `transferCompleteAudioUrl` for audio played to the destination after the introduction. |
| The customer cannot continue after a failed transfer | `fallbackPlan.endCallEnabled` is `true` | Set it to `false` so the original assistant remains on the call. |
| The transfer ends during a long operator interaction | A duration or silence timeout is too short | Increase `maxDurationSeconds` or `silenceTimeoutSeconds` within the supported ranges. |
-Check the affected call's **Logs** tab in [**Logs → Calls**](/observability/logs/call-logs) for validation and transfer errors. If Vapi initiated the transfer but the destination never rings, inspect the telephony provider's call detail records and follow [Troubleshoot call forwarding drops](/phone-calling/in-call-control/transfer-calls/debug-forwarding-drops).
+Check the affected call's **Logs** tab in [**Logs → Calls**](/observability/logs/call-logs) for validation and transfer errors. If Vapi initiated the transfer but the destination never rings, inspect the telephony provider's call detail records and follow [Troubleshoot call transfers](/phone-calling/in-call-control/transfer-calls/debug-forwarding-drops).
## API reference
@@ -228,7 +234,7 @@ The [Create Tool API reference](/api-reference/tools/create) documents transfer
Choose a transfer destination at runtime.
-
+
Diagnose failed or incomplete transfers.
diff --git a/fern/calls/call-ended-reason.mdx b/fern/calls/call-ended-reason.mdx
index ee41f1d36..c6f1ddd40 100644
--- a/fern/calls/call-ended-reason.mdx
+++ b/fern/calls/call-ended-reason.mdx
@@ -88,7 +88,7 @@ These indicate the assistant intentionally ended the call — not errors.
- `assistant-ended-call-after-message-spoken` — The assistant ended the call after speaking its final message.
- `assistant-ended-call-with-hangup-task` — The assistant ended the call using a hangup task.
- `assistant-said-end-call-phrase` — The assistant said a phrase configured to trigger call termination.
-- `assistant-forwarded-call` — The assistant transferred the call to another number or service.
+- `assistant-forwarded-call` — The assistant started a transfer to another number or service. This reason doesn't confirm that the destination answered.
- `assistant-join-timed-out` — The assistant failed to join the call within the expected timeframe.
## Customer actions
@@ -160,7 +160,7 @@ Common transcriber error patterns:
- `call.in-progress.error-warm-transfer-silence-timeout` — Silence timeout during a warm transfer.
- `call.in-progress.error-warm-transfer-microphone-timeout` — Microphone timeout during a warm transfer.
-For step-by-step transfer debugging, see [Debug forwarding drops](/phone-calling/in-call-control/transfer-calls/debug-forwarding-drops).
+For step-by-step transfer debugging, see [Troubleshoot call transfers](/phone-calling/in-call-control/transfer-calls/debug-forwarding-drops).
## Transport and connectivity
@@ -208,7 +208,8 @@ For SIP trunk setup issues, see [Troubleshoot SIP trunk credential errors](/adva
- `call.ringing.hook-executed-transfer` — A transfer hook executed during ringing ended the call.
- `call.ending.hook-executed-say` — A say hook executed during the ending phase.
- `call.ending.hook-executed-transfer` — A transfer hook executed during the ending phase.
-- `call.forwarding.operator-busy` — The operator was busy during call forwarding.
+- `call.forwarding.operator-busy` — The destination of a blind transfer was busy, or an assistant-based warm transfer failed and the fallback ended the call. For blind transfers, this reason appears only when transfer outcome detection is enabled.
+- `call.forwarding.no-answer` — The destination of a blind transfer didn't answer. This reason appears only when transfer outcome detection is enabled.
## Other reasons
diff --git a/fern/calls/troubleshoot-call-errors.mdx b/fern/calls/troubleshoot-call-errors.mdx
index a3918bea6..722a2deef 100644
--- a/fern/calls/troubleshoot-call-errors.mdx
+++ b/fern/calls/troubleshoot-call-errors.mdx
@@ -247,7 +247,7 @@ If a voice error ends the call, follow [Troubleshoot voice fallback](/voice-fall
For response-class guidance and checks across Vapi, your SIP provider, and your SIP infrastructure, see [Troubleshoot SIP response codes](/advanced/sip/troubleshoot-sip-response-codes).
-For a detailed transfer debugging walkthrough, see [Debug forwarding drops](/phone-calling/in-call-control/transfer-calls/debug-forwarding-drops).
+For a detailed transfer debugging walkthrough, see [Troubleshoot call transfers](/phone-calling/in-call-control/transfer-calls/debug-forwarding-drops).
## Call ended normally
@@ -271,7 +271,7 @@ These are not errors — they indicate the call ended as expected.
- **[Call end reasons](/calls/call-ended-reason):** Complete reference of every `endedReason` code.
- **[Debugging voice agents](/debugging):** General debugging workflow using dashboard tools, logs, Simulations, and Evals.
-- **[Debug forwarding drops](/phone-calling/in-call-control/transfer-calls/debug-forwarding-drops):** Deep dive into transfer failures.
+- **[Troubleshoot call transfers](/phone-calling/in-call-control/transfer-calls/debug-forwarding-drops):** Deep dive into transfer failures.
- **[Troubleshoot SIP trunk errors](/advanced/sip/troubleshoot-sip-trunk-credential-errors):** Resolve SIP credential validation failures.
- **[Troubleshoot SIP response codes](/advanced/sip/troubleshoot-sip-response-codes):** Identify where a SIP request failed and what to check next.
- **[How to report issues](/issue-reporting):** Include your `call_id` and account email when contacting support.
diff --git a/fern/calls/troubleshoot-call-forwarding-drops.mdx b/fern/calls/troubleshoot-call-forwarding-drops.mdx
index 4382d45d5..b6d062b23 100644
--- a/fern/calls/troubleshoot-call-forwarding-drops.mdx
+++ b/fern/calls/troubleshoot-call-forwarding-drops.mdx
@@ -1,40 +1,38 @@
---
-title: "Debug call forwarding drops"
-subtitle: "Learn to troubleshoot calls that drop immediately after initiating transfer"
-description: Troubleshoot Vapi calls that drop immediately after a transfer, finding why forwarding disconnects both parties and how to resolve the underlying cause.
+title: "Troubleshoot call transfers"
+subtitle: "Trace transfer attempts, failed connections, and caller recovery"
+description: Diagnose and troubleshoot transfer tool invocation, provider routing, busy and unanswered destinations, fallback behavior, and the evidence to send support.
---
-## Overview
+Use this page when a call transfer fails or doesn't behave as expected. A transfer has three separate stages: the assistant decides to transfer, Vapi starts the transfer, and the destination answers. Each stage leaves different evidence, so check both the Vapi call record and your phone provider's call logs to find the stage where the attempt stopped.
-When you initiate call forwarding, you expect the call to transfer to the destination. Instead, the call drops immediately after initiating the transfer, leaving both parties disconnected.
+## Quick diagnosis
-**In this guide, you'll learn to:**
+| What you observe | Check next | Likely cause |
+| --- | --- | --- |
+| The assistant says it will transfer, but the call continues | Look for a `transferCall` invocation in the call's **Messages** tab | Assistant prompt or model decision |
+| The `transferCall` tool runs, but the transfer does not start | Inspect the call's **Logs** tab for a Tool or Transfer error | Tool configuration or Vapi transfer initiation |
+| The call ends with `assistant-forwarded-call`, but the destination does not answer | Match `phoneCallProviderId` to the provider's call record | Provider route, destination, or delegated call control |
+| A warm transfer returns to the original assistant or ends after a fallback message | Check `transferPlan.mode`, `fallbackPlan.endCallEnabled`, and the transfer attempt | Configured fallback or transfer-assistant decision |
-- Identify why calls drop during forwarding
-- Check call logs and API responses systematically
-- Analyze telephony provider logs for transfer failures
-- Resolve configuration issues preventing successful transfers
-
-
- This guide focuses on the specific scenario where calls drop immediately after
- transfer initiation, not general call quality issues.
-
+The `assistant-forwarded-call` ended reason means Vapi started or handed off the transfer. It does not prove that the destination rang, answered, or spoke with the caller.
## Prerequisites
Before you start debugging, ensure you have:
-- **Call ID** from the dropped call
-- **API access** to make GET requests to Vapi
-- **Admin access** to your telephony provider dashboard (Twilio, Vonage, Telnyx)
-- **Wireshark** installed (for SIP-based calls only)
+- The call ID of the failed transfer
+- A [private Vapi API key](/security-and-privacy/api-keys) to retrieve the call through the API
+- Access to the call's **Messages** and **Logs** tabs, if logs were retained
+- Access to your phone provider's call record, if the transfer reached that provider
+- A SIP trace or packet capture, if you're debugging a SIP transfer
## Troubleshooting workflow
-First, verify whether Vapi successfully initiated the forwarding.
+First, check the call's `endedReason` and the [Messages tab](/observability/logs/call-logs#messages). A spoken promise to transfer is not a `transferCall` invocation.
```bash title="cURL"
@@ -47,164 +45,122 @@ curl -X GET "https://api.vapi.ai/call/{call_id}" \
import { VapiClient } from "@vapi-ai/server-sdk";
const client = new VapiClient({ token: process.env.VAPI_API_KEY });
-const call = await client.calls.get("ca123456xxxxx");
+const call = await client.calls.get("YOUR_CALL_ID");
console.log("End reason:", call.endedReason);
```
```python title="Python SDK"
+import os
from vapi import Vapi
client = Vapi(token=os.getenv("VAPI_API_KEY"))
-call = client.calls.get("ca123456xxxxx")
+call = client.calls.get("YOUR_CALL_ID")
print(f"End reason: {call.ended_reason}")
```
-**Check the response:**
-
-```json title="Expected response"
+```json title="Example response (truncated)"
{
- "id": "ca123456xxxxx",
- "endedReason": "assistant-forwarded-call",
+ "id": "550e8400-e29b-41d4-a716-446655440000",
+ "type": "inboundPhoneCall",
"status": "ended",
+ "endedReason": "assistant-forwarded-call",
+ "phoneCallProvider": "twilio",
+ "phoneCallTransport": "pstn",
+ "phoneCallProviderId": "CA0123456789abcdef0123456789abcdef",
"destination": {
"type": "number",
- "number": "+1234567890"
- },
- "phoneCallProviderId": "twilio_call_abc123"
+ "number": "+14155550100"
+ }
}
```
-**What the `endedReason` tells you:**
+For every field in the call object, see the [Get Call API reference](/api-reference/calls/get).
-- `"assistant-forwarded-call"` → Vapi forwarded successfully, continue to Step 2
-- Any other value → Forwarding wasn't initiated, check your assistant configuration
+**Interpret the result:**
-
+- No `transferCall` tool call: Check the assistant prompt and tool assignment. The model did not invoke the transfer tool.
+- A `transferCall` tool call followed by a Tool or Transfer error: Inspect the destination, transfer plan, and [call logs](/observability/logs/call-logs#logs).
+- `assistant-forwarded-call`: Vapi submitted the transfer to the phone provider or sent a `phone-call-control` request to your server. Check the downstream result before treating it as a connected call.
+- `call.in-progress.error-transfer-failed`, `call.forwarding.operator-busy`, or `call.forwarding.no-answer`: Inspect the transfer attempt and provider result. These reasons do not all mean the assistant failed to invoke the tool.
-
+
-If you're handling call control through server messages, this can prevent Vapi from managing transfers.
+
-Check your assistant's `serverMessages` configuration:
+When the assistant includes `phone-call-control` in `serverMessages`, Vapi sends a `phone-call-control` request to your server instead of transferring the call itself. Your server then decides whether and how to transfer. Check your server's response and your phone provider's call record before changing this setting.
-```json title="Problematic configuration"
+```json title="Vapi transfers the call"
{
- "assistant": {
- "serverMessages": ["phone-call-control", "status-update"]
- }
+ "serverMessages": ["status-update", "end-of-call-report", "tool-calls"]
}
```
-```json title="Correct configuration"
+```json title="Your server controls the transfer"
{
- "assistant": {
- "serverMessages": ["status-update"]
- }
+ "serverMessages": ["status-update", "end-of-call-report", "tool-calls", "phone-call-control"]
}
```
-
-
- If `"phone-call-control"` is present, your server is overriding Vapi's call
- control. Remove it unless you're implementing custom transfer logic.
-
-
-
-
-
-
-The `phoneCallProviderBypassEnabled` flag determines whether Vapi handles call control directly.
-
-**Recommended configuration:**
-
-```json title="Standard Vapi forwarding"
-{
- "phoneCallProviderBypassEnabled": false
-}
-```
+Remove `phone-call-control` unless your server handles transfers. Setting `serverMessages` replaces the default list, so include every message type your server needs. See [server events](/server-url/events) for the full list.
-
- Only set this to `true` if you're implementing custom call control through
- your telephony provider.
-
+Use [Webhook logs](/observability/logs/webhook-logs) to inspect a recorded server-message delivery. If your application intentionally controls the provider call, its logs and provider call record are the source of the downstream outcome.
-Not all transfer scenarios are supported. Verify your use case:
+Confirm that the call type supports the destination type:
-| From | To | Supported |
-| ---------- | ------------ | ------------------- |
-| Phone call | Phone number | ✅ Yes |
-| Web call | Phone number | ❌ No |
-| Phone call | SIP number | ❌ No (PSTN to SIP) |
-| SIP call | SIP number | ✅ Yes |
-| SIP call | Phone number | ✅ Yes |
+| Call type | To a phone number | To a SIP address |
+| --- | --- | --- |
+| Phone call on Twilio, Telnyx, a Vapi number, or a SIP trunk | Yes | Yes |
+| Phone call on Vonage | Yes | No |
+| Web call | No | No |
-
- Web-to-phone transfers are not supported. The call will always drop in this
- scenario.
-
+For a SIP destination, check the inbound provider, `transferPlan.sipVerb`, and the receiving private branch exchange (PBX). For a phone-number destination, verify E.164 format and call the number directly to confirm it is reachable.
-If the call shows `"assistant-forwarded-call"` but still drops, the issue is likely with your telephony provider.
-
-**Get your telephony call ID** from the Vapi call object:
-
-```json title="Extract telephony call ID"
-{
- "id": "ca123456xxxxx",
- "endedReason": "assistant-forwarded-call",
- "phoneCallProviderId": "CAabc123"
-}
-```
-
-**Check your provider's dashboard:**
+Use the call's `phoneCallProviderId` and timestamp to find the matching record in your phone provider's logs. Check whether the provider received the transfer command, dialed the destination, and recorded an answer, busy signal, no answer, rejection, or other failure.
-
-1. Go to [Twilio Console > Call Logs](https://console.twilio.com/us1/monitor/logs/calls)
-2. Search using `phoneCallProviderId` (e.g., `CAabc123`)
-3. Look for transfer-related errors in the call timeline
-4. Check TwiML execution logs for failed transfers
-
-
-
- 1. Access [Vonage API Dashboard](https://dashboard.nexmo.com/) 2. Navigate to
- Call Logs 3. Search using the telephony call ID and timestamp 4. Review call
- flow details for transfer failures
-
-
-
-1. Open [Telnyx Mission Control Portal](https://portal.telnyx.com/)
-2. Go to Call Detail Records
-3. Search using the telephony call ID
-4. Examine call records for forwarding errors
-
+
+ 1. Open [Twilio Console call logs](https://console.twilio.com/us1/monitor/logs/calls).
+ 2. Search for the call SID from `phoneCallProviderId`. It starts with `CA`.
+ 3. Check the call's status, its child calls, and any errors or warnings.
+
+
+ 1. Sign in to the [Vonage API Dashboard](https://dashboard.nexmo.com/).
+ 2. Find the call by the conversation UUID from `phoneCallProviderId` and the call's timestamp.
+ 3. Check the call's status and any transfer errors.
+
+
+ 1. Sign in to the [Telnyx Mission Control Portal](https://portal.telnyx.com/).
+ 2. Find the call's detail record by the call control ID from `phoneCallProviderId`.
+ 3. Check the call's status and any transfer errors.
+
+
+ There's no separate provider console for a Vapi number. Use the call's [Logs](/observability/logs/call-logs#logs) tab and, if available, the packet capture in `artifact.pcapUrl` (see the next step). For a bring-your-own SIP trunk, also check your SIP provider's call records.
+
+A successful API command to the provider is not proof that the downstream call connected. Compare the provider record with the Vapi call's `endedReason`, [Messages](/observability/logs/call-logs#messages), and [Logs](/observability/logs/call-logs#logs). A missing provider record can point to a problem starting the transfer or a configuration problem. A provider error identifies a downstream failure to investigate with that provider.
+
For SIP trunking or bring-your-own-number setups, analyze the SIP signaling.
-**Download the packet capture:**
+**Check whether a packet capture is available:** Look for `artifact.pcapUrl` in the call record you retrieved in the first step.
-```bash title="Get PCAP file"
-curl -X GET "https://api.vapi.ai/call/{call_id}" \
- -H "Authorization: Bearer YOUR_API_KEY"
-```
-
-The response includes a `pcapUrl` field. Download this file and open it in Wireshark.
+For an eligible Vapi or bring-your-own SIP phone call, the completed call can contain `artifact.pcapUrl` when packet capture was enabled and retained. If present, download that URL and open the capture in a SIP analysis tool. For example, Wireshark supports SIP. A missing URL does not prove that no transfer command was sent.
**Filter for transfer packets:**
@@ -214,93 +170,88 @@ sip.Method == "REFER"
**What to look for:**
-- **REFER packet present** → Vapi sent the transfer request to your SIP provider
-- **REFER packet missing** → Transfer wasn't initiated by Vapi
-- **202 Accepted response** → SIP provider accepted the transfer
-- **Error responses (4xx, 5xx)** → SIP provider rejected the transfer
+- **REFER request present:** The transfer request reached the SIP signaling path captured in this file.
+- **202 Accepted response:** The SIP provider accepted the transfer request. Check any later NOTIFY or referred-call status, because acceptance alone doesn't prove that the destination answered.
+- **Error response (4xx or 5xx):** The SIP provider rejected the transfer request.
+- **No REFER request:** Check whether the transfer used `sipVerb: "dial"` or `sipVerb: "bye"`, whether capture started before the transfer, and whether the tool was invoked. Vapi numbers always dial the destination, so look for the dial leg instead.
- **Important:** SIP transfers are handled by your telephony provider, not Vapi.
- Once Vapi sends the REFER packet, your SIP provider manages the actual
- transfer process.
+ With `sipVerb: "refer"`, your SIP provider completes the transfer after it accepts the REFER request, so its logs show the outcome. With `sipVerb: "dial"`, Vapi calls the destination itself, so inspect the outbound dial leg instead of looking for a REFER. Vapi numbers always transfer this way. With `sipVerb: "bye"`, Vapi ends its leg without dialing the destination.
-For more details on SIP configuration, see our [SIP trunk documentation](https://docs.vapi.ai/advanced/sip/sip-trunk#inbound-call-test).
+For SIP configuration details, see the [SIP trunk inbound call test](/advanced/sip/sip-trunk#inbound-call-test).
-## Common solutions
-
-Based on your findings, here are the most frequent fixes:
+## Diagnose busy, no answer, rejection, voicemail, and timeout
-### Call shows successful forwarding but still drops
+Recovery depends on the transfer mode. Check `transferPlan.mode` before changing a fallback setting.
-**Root cause:** Telephony provider couldn't complete the transfer
+| Mode or path | What to check | Caller recovery |
+| --- | --- | --- |
+| Blind transfer or non-assistant warm transfer | Provider call record for busy, no answer, rejection, voicemail, or a failed destination leg | **Twilio:** Vapi hands the call to Twilio and ends with `assistant-forwarded-call`, even if the destination is busy or doesn't answer. The caller's call ends, and voicemail counts as answered.
**Vapi numbers and SIP trunks:** Busy and no-answer can have their own ended reasons when transfer outcome detection is enabled. Otherwise the call ends with `assistant-forwarded-call`. |
+| `warm-transfer-experimental` | Operator-leg status, `transferAssistant` configuration, transfer-assistant tool call, and timeout | With `transferAssistant` configured, busy and no-answer statuses roll back. `transferCancel` can roll back a declined call or detected voicemail. After rollback, the original assistant says the configured fallback message. |
+| Blind SIP transfer with SIP transfer fallback | Transfer outcome, `fallbackPlan`, and provider record | After a busy, unanswered, or failed transfer, the original assistant says the fallback message, then continues or ends the call. |
-**Solution:** Check destination number format and availability
+For assistant-based warm transfer, set `fallbackPlan.endCallEnabled` explicitly. Set it to `false` to keep the original assistant on the call after the fallback message, or to `true` to end the call after the message.
-- Verify the destination number includes country code
-- Test calling the destination directly outside of Vapi
-- Check if destination has call blocking enabled
+When the destination is voicemail or an automated queue, the transfer assistant must cancel the transfer, or configured voicemail detection must identify the non-human destination. Do not assume that a standard message, summary, or TwiML introduction detects voicemail.
-### endedReason is not 'assistant-forwarded-call'
+Read the call's messages and logs with the ended reason rather than using that field alone to decide where the failure occurred.
-**Root cause:** Transfer wasn't initiated due to configuration
+A failed transfer command can end with `call.in-progress.error-transfer-failed`. On a blind SIP transfer with SIP transfer fallback enabled, a configured `fallbackPlan` can also handle a recoverable transfer-request failure while the caller is still connected to Vapi. It cannot recover a call that has already ended.
-**Solution:** Review assistant settings
+An operator-leg busy or no-answer outcome can produce a different reason or return the caller to the original assistant, depending on the mode and recovery configuration.
-- Remove `"phone-call-control"` from `serverMessages`
-- Set `phoneCallProviderBypassEnabled` to `false`
-- Verify your transfer function is properly configured
+## Common solutions
-### SIP REFER packets missing in PCAP
+### The transfer started but the destination never answered
-**Root cause:** Vapi didn't send transfer request to SIP provider
+- Use E.164 format with the country code, for example `+14155550100`.
+- Call the destination directly from another phone to confirm it's reachable.
+- Check whether the destination blocks or screens incoming calls, for example with call blocking or spam filtering.
+- Find the call in your phone provider's logs to see why it didn't connect.
-**Solution:** Check Vapi configuration
+### The assistant didn't transfer the call
-- Verify SIP endpoint configuration
-- Ensure destination format matches SIP addressing
-- Check for conflicting call control settings
+- Confirm the transfer tool is attached to the assistant and has a destination.
+- Describe when to transfer in both the tool description and the assistant prompt. The assistant saying it will transfer doesn't start a transfer.
+- Check that `phone-call-control` isn't in `serverMessages` unless your server handles transfers.
+- Confirm the call is a phone call. Web calls can't transfer to a phone number or SIP address.
-## Limitations
+### The packet capture has no REFER request
-### Transfer scenario limitations
+- Check `transferPlan.sipVerb`. With `dial`, look for the outbound call to the destination instead. Vapi numbers always use `dial`.
+- Confirm a SIP destination is a full SIP URI, for example `sip:support@example.com`.
+- Check that `phone-call-control` isn't sending the transfer to your server.
-- **Web calls** cannot be transferred to phone numbers
-- **PSTN-to-SIP** transfers are not supported
-- Cross-provider transfers may have compatibility issues
-
-### Configuration dependencies
+## Limitations
-- `phoneCallProviderBypassEnabled` must be `false` for Vapi-managed transfers
-- `serverMessages` with `"phone-call-control"` overrides default behavior
-- SIP configuration requires proper endpoint setup
+- **Web calls** can't transfer to a phone number or SIP address. The call ends with `call.in-progress.error-transfer-failed`.
+- **Vonage calls** can't transfer to a SIP address.
+- **Warm modes that speak a message or summary, or run TwiML,** work only on Twilio. Other providers transfer the call without the introduction.
+- **Assistant-based warm transfer on Twilio** needs a phone-number destination. A SIP destination is transferred without the transfer assistant.
+- **Transfer outcome detection and SIP transfer fallback** are available on Vapi numbers and SIP trunks.
+- **Your server completes transfers** when `serverMessages` includes `phone-call-control`. Vapi sends transfers to your server instead of transferring the call itself.
## When to contact support
-Escalate to support when you've completed all troubleshooting steps and:
-
-- Provider logs show successful transfers but calls consistently fail
-- SIP packet analysis indicates Vapi-side transfer issues
-- Multiple destinations fail with the same configuration
-- Configuration changes don't resolve recurring failures
+Contact support when the tool was invoked with a supported configuration but the Vapi and provider records disagree, a transfer error has no clear cause, or the same failure persists across controlled calls.
**Include in your support request:**
-- Call ID and timestamp
-- Complete troubleshooting results from this guide
-- Telephony provider error codes and logs
-- Screenshots of configuration settings
+- Vapi call ID, timestamp, call type, and `endedReason`
+- Transfer tool ID, mode, destination type, and a redacted destination
+- Expected caller and operator outcome, plus what each party heard
+- Relevant **Messages** and **Logs** entries, including the transfer tool call and fallback behavior
+- `phoneCallProviderId`, provider call record, error code, and SIP trace or `artifact.pcapUrl` when available
+- Whether `phone-call-control` in `serverMessages` sends transfers to your server
+- Screenshots of the transfer tool and transfer plan settings in the Dashboard
## Next steps
-Now that you can debug call forwarding drops:
-
-- **Monitor call patterns:** Set up alerts for calls with unexpected `endedReason` values
-- **Test systematically:** Verify transfers work across different destination types before production
-- **Review SIP setup:** Ensure your SIP configuration follows our [advanced SIP guide](https://docs.vapi.ai/advanced/sip/sip-trunk)
+Test the chosen transfer mode with a reachable operator, a busy destination, and an unanswered call before using it for production escalation. For SIP routing, compare the result with your [SIP trunk configuration](/advanced/sip/sip-trunk). To catch transfer failures early, set up [monitoring](/observability/monitoring-quickstart) with an Insight that tracks transfer-related ended reasons, such as `call.in-progress.error-transfer-failed` and `call.forwarding.no-answer`.
diff --git a/fern/docs.yml b/fern/docs.yml
index a63911bab..c6df19bc6 100644
--- a/fern/docs.yml
+++ b/fern/docs.yml
@@ -515,7 +515,8 @@ navigation:
path: calls/transfer-calls-with-api-lookup.mdx
- page: On-hold specialist transfer
path: calls/call-handling-with-vapi-and-twilio.mdx
- - page: Debug forwarding drops
+ - page: Troubleshoot call transfers
+ slug: debug-forwarding-drops
path: calls/troubleshoot-call-forwarding-drops.mdx
icon: fa-light fa-bug
- page: Call queue management
diff --git a/fern/tools/transfer-call.mdx b/fern/tools/transfer-call.mdx
index fe59b9d52..ce1b41b7d 100644
--- a/fern/tools/transfer-call.mdx
+++ b/fern/tools/transfer-call.mdx
@@ -183,21 +183,49 @@ The transfer mode controls what happens on the destination call leg before the c
| Dashboard mode | API value | What it does |
| --- | --- | --- |
| **Blind Transfer** | `blind-transfer` | Connects the customer without speaking to the destination. This is the default. See [Blind transfer](/tools/transfer-call/blind-transfer). |
-| **Blind Transfer with Summary in SIP Header** | `blind-transfer-add-summary-to-sip-header` | Adds a generated summary to the `X-Transfer-Summary` SIP header, then transfers immediately. See [Blind transfer](/tools/transfer-call/blind-transfer). |
-| **Warm Transfer with Message** | `warm-transfer-say-message` | Speaks a fixed message to the destination before connecting the customer. See [Warm transfer](/tools/transfer-call/warm-transfer). |
-| **Warm Transfer with Summary** | `warm-transfer-say-summary` | Speaks a generated conversation summary to the destination before connecting. See [Warm transfer](/tools/transfer-call/warm-transfer). |
-| **Warm Transfer with TwiML** | `warm-transfer-twiml` | Executes TwiML on the destination call leg before connecting. See [Warm transfer](/tools/transfer-call/warm-transfer). |
-| **Warm Transfer - Wait for Operator, Then Say Message** | `warm-transfer-wait-for-operator-to-speak-first-and-then-say-message` | Waits for destination speech, then delivers a fixed message. See [Warm transfer](/tools/transfer-call/warm-transfer). |
-| **Warm Transfer - Wait for Operator, Then Say Summary** | `warm-transfer-wait-for-operator-to-speak-first-and-then-say-summary` | Waits for destination speech, then delivers a generated summary. See [Warm transfer](/tools/transfer-call/warm-transfer). |
-| **Warm Transfer - Experimental** | `warm-transfer-experimental` | Places the customer on hold and supports fallback, summaries, custom audio, or a transfer-assistant conversation. See [Assistant-based warm transfer](/calls/assistant-based-warm-transfer). |
+| **Blind Transfer with Summary in SIP Header** | `blind-transfer-add-summary-to-sip-header` | Adds a generated summary to the `X-Transfer-Summary` SIP header, then transfers immediately. See [Blind transfer](/tools/transfer-call/blind-transfer). Vapi numbers and bring-your-own SIP trunks only. Other providers transfer without the header. |
+| **Warm Transfer with Message** | `warm-transfer-say-message` | Speaks a fixed message to the destination before connecting the customer. See [Warm transfer](/tools/transfer-call/warm-transfer). Twilio only. Other providers transfer without the message. |
+| **Warm Transfer with Summary** | `warm-transfer-say-summary` | Speaks a generated conversation summary to the destination before connecting. See [Warm transfer](/tools/transfer-call/warm-transfer). Twilio only. Other providers transfer without the summary. |
+| **Warm Transfer with TwiML** | `warm-transfer-twiml` | Executes TwiML on the destination call leg before connecting. See [Warm transfer](/tools/transfer-call/warm-transfer). Twilio only. |
+| **Warm Transfer - Wait for Operator, Then Say Message** | `warm-transfer-wait-for-operator-to-speak-first-and-then-say-message` | Waits for destination speech, then delivers a fixed message. See [Warm transfer](/tools/transfer-call/warm-transfer). Twilio only. Other providers transfer without the message. |
+| **Warm Transfer - Wait for Operator, Then Say Summary** | `warm-transfer-wait-for-operator-to-speak-first-and-then-say-summary` | Waits for destination speech, then delivers a generated summary. See [Warm transfer](/tools/transfer-call/warm-transfer). Twilio only. Other providers transfer without the summary. |
+| **Warm Transfer - Experimental** | `warm-transfer-experimental` | Places the customer on hold and supports fallback, summaries, custom audio, or a transfer-assistant conversation. See [Assistant-based warm transfer](/calls/assistant-based-warm-transfer). Works on Twilio (phone-number destinations), Vapi numbers, and bring-your-own SIP trunks. |
Use [Dynamic call transfers](/calls/call-dynamic-transfers) when the destination is selected at runtime. Dynamic destination selection is separate from the transfer mode.
+## Understand transfer outcomes and fallback
+
+The `assistant-forwarded-call` ended reason means Vapi initiated a transfer or delegated call control to your server. By itself, it does not confirm that the destination answered or that the caller and destination spoke. Check the provider's call record to confirm the downstream result.
+
+| Transfer mode | Caller state before connection | If the transfer fails |
+| --- | --- | --- |
+| `blind-transfer` | The transfer starts without an operator introduction. | Doesn't wait for an answer. On Twilio, a busy or unanswered call ends. On Vapi numbers and bring-your-own SIP trunks, the caller can return to the assistant only if SIP transfer fallback is enabled and `fallbackPlan` is configured. |
+| `blind-transfer-add-summary-to-sip-header` | The transfer starts with a generated summary in the `X-Transfer-Summary` SIP header. | The summary is not spoken. The transfer call tool does not wait for operator acceptance. |
+| `warm-transfer-say-message` | The destination hears a fixed message before the caller is connected. | This mode does not run an assistant that can accept or cancel the transfer. |
+| `warm-transfer-say-summary` | The destination hears a generated summary before the caller is connected. | This mode does not run an assistant that can accept or cancel the transfer. |
+| `warm-transfer-twiml` | The destination call leg executes the configured TwiML before the caller is connected. | The TwiML controls the introduction; this mode does not run a transfer assistant. |
+| `warm-transfer-wait-for-operator-to-speak-first-and-then-say-message` | The destination speaks before hearing the fixed message. | `transferPlan.timeout` limits the wait for destination speech. It is not a failed-transfer fallback. |
+| `warm-transfer-wait-for-operator-to-speak-first-and-then-say-summary` | The destination speaks before hearing the generated summary. | `transferPlan.timeout` limits the wait for destination speech. It is not a failed-transfer fallback. |
+| `warm-transfer-experimental` | The caller waits on hold while the destination call leg runs. | The transfer assistant can connect or cancel. After a failed attempt, the original assistant says the fallback message, then continues or ends the call. See [Assistant-based warm transfer](/calls/assistant-based-warm-transfer). |
+
+Test the selected mode with the same phone provider and destination used in production. For a transfer that requires an operator to accept the caller, configure [assistant-based warm transfer](/calls/assistant-based-warm-transfer) and an explicit `fallbackPlan`.
+
+## Common transfer scenarios
+
+Each scenario pairs a common reason for handing a caller to a person with the destination, transfer mode, and fallback settings that fit it. The scenarios differ in how much control you keep. A blind transfer connects the caller fastest but can't bring them back if no one answers. Assistant-based warm transfer confirms that a person accepted before connecting the caller. Start from the scenario that matches why the caller needs a person, then check that your phone provider [supports the transfer mode](#choose-a-transfer-mode) it uses.
+
+| Need | Configure | Expected result and recovery |
+| --- | --- | --- |
+| Escalate when the caller asks or the assistant reaches a capability boundary | Add a transfer destination and describe the trigger in the tool description and assistant prompt. Require caller agreement when appropriate. | The model must invoke `transferCall`; speech alone does not start a transfer. Use [call logs](/observability/logs/call-logs) to confirm invocation. |
+| Route to a specialist | Give each stored destination a distinct description, or use a [lookup-based transfer](/calls/transfer-calls-with-api-lookup). | The selected destination receives the transfer attempt. Confirm the selected route in the tool call and provider record. |
+| Give an operator context before connecting | Use a supported [warm transfer mode](/tools/transfer-call/warm-transfer) with a message or summary. | The destination hears the introduction before connection. These modes do not ask an assistant to confirm acceptance. |
+| Require a person to accept a high-stakes escalation | Use [assistant-based warm transfer](/calls/assistant-based-warm-transfer). Prompt the transfer assistant to call `transferSuccessful` only after acceptance and `transferCancel` when it should decline. Set `fallbackPlan.endCallEnabled` explicitly. | The caller waits on hold during the operator conversation. A canceled attempt or handled operator-leg failure follows the configured fallback; test busy, no-answer, and voicemail outcomes with the production provider. |
+
## Verify the transfer
Place a test call and ask for the destination described in the tool. Confirm that the call log contains a `transferCall` tool invocation and that the destination rings or answers.
-The `assistant-forwarded-call` ended reason confirms that Vapi initiated the transfer. It does not confirm that the downstream telephony provider completed it, so check the provider's call logs when the destination does not receive the call.
+The `assistant-forwarded-call` ended reason confirms that Vapi initiated or delegated the transfer. It does not confirm that the downstream telephony provider completed it, so check the provider's call logs when the destination does not receive the call.
## Troubleshooting
@@ -210,7 +238,7 @@ The `assistant-forwarded-call` ended reason confirms that Vapi initiated the tra
| The assistant transfers when it should continue helping | The prompt permits broad invocation | Narrow the prompt conditions. For tools configured through the API, you can also add a [tool rejection plan](/tools/tool-rejection-plan). |
| An assistant-to-assistant transfer stalls or behaves like an external call | The transfer call tool is being used for assistant routing | Use the [handoff tool](/squads/handoff) for transfers between standalone assistants or assistants in a Squad. |
-For dropped calls, provider diagnostics, and SIP packet checks, follow [Troubleshoot call forwarding drops](/phone-calling/in-call-control/transfer-calls/debug-forwarding-drops). The [call ended reasons reference](/calls/call-ended-reason) lists transfer-specific outcomes such as `call.in-progress.error-transfer-failed`.
+For dropped calls, provider diagnostics, and SIP packet checks, follow [Troubleshoot call transfers](/phone-calling/in-call-control/transfer-calls/debug-forwarding-drops). The [call ended reasons reference](/calls/call-ended-reason) lists transfer-specific outcomes such as `call.in-progress.error-transfer-failed`.
## API reference
@@ -237,7 +265,7 @@ The [Create Tool API reference](/api-reference/tools/create) documents the publi
Move the conversation to another assistant or Squad.
-
+
Diagnose provider, SIP, and call-routing failures.
diff --git a/fern/tools/transfer-call/blind-transfer.mdx b/fern/tools/transfer-call/blind-transfer.mdx
index d5e03d87a..730a4b7dc 100644
--- a/fern/tools/transfer-call/blind-transfer.mdx
+++ b/fern/tools/transfer-call/blind-transfer.mdx
@@ -20,6 +20,10 @@ Both blind modes transfer the caller immediately. The summary variant adds conte
Blind transfer is the default. If `transferPlan` is omitted, Vapi uses `blind-transfer`.
+Neither blind mode waits for an operator to accept the call. The `assistant-forwarded-call` ended reason means the transfer started, not that the destination answered.
+
+A busy line, no answer, or voicemail does not automatically return the caller to the original assistant through the transfer call tool. If SIP transfer fallback is enabled and the transfer has a `fallbackPlan`, the caller can return to the assistant after a busy, unanswered, or failed transfer. See [Understand transfer outcomes and fallback](/tools/transfer-call#understand-transfer-outcomes-and-fallback).
+
## Configure blind transfer
Update the transfer plan on each destination that should use blind transfer.
@@ -144,7 +148,7 @@ An `assistant-forwarded-call` ended reason confirms that Vapi initiated the tran
| The summary contains the wrong information | The summary prompt is broad or uses the wrong template variable | Give `summaryPlan.messages` a focused instruction and include `{{transcript}}` in the user message. |
| Vapi shows `assistant-forwarded-call`, but the transfer failed | Vapi initiated the downstream transfer but did not observe its final result | Use the telephony provider's call detail record and SIP signaling as the source of truth for downstream completion. |
-For additional provider diagnostics and packet capture guidance, follow [Troubleshoot call forwarding drops](/phone-calling/in-call-control/transfer-calls/debug-forwarding-drops).
+For additional provider diagnostics and packet capture guidance, follow [Troubleshoot call transfers](/phone-calling/in-call-control/transfer-calls/debug-forwarding-drops).
## API reference
@@ -162,7 +166,7 @@ The [Update Tool API reference](/api-reference/tools/update) documents the trans
Let a transfer assistant speak with the destination.
-
+
Diagnose incomplete transfers and SIP routing failures.
diff --git a/fern/tools/transfer-call/warm-transfer.mdx b/fern/tools/transfer-call/warm-transfer.mdx
index ba530473d..1f6148b1c 100644
--- a/fern/tools/transfer-call/warm-transfer.mdx
+++ b/fern/tools/transfer-call/warm-transfer.mdx
@@ -214,7 +214,7 @@ In [**Logs → Calls**](/observability/logs/call-logs), inspect the transfer his
| The API rejects the TwiML | The value is invalid XML, too long, or uses unsupported verbs | Use valid XML under 4,096 characters and limit the instructions to the supported verbs. |
| The TwiML behaves differently from the test | The destination interactive voice response system handles timing or input differently | Test the same TwiML against the production destination and simplify `Gather`, `Pause`, or audio timing. |
-If the transfer is initiated but the destination never rings or the call drops, follow [Troubleshoot call forwarding drops](/phone-calling/in-call-control/transfer-calls/debug-forwarding-drops).
+If the transfer is initiated but the destination never rings or the call drops, follow [Troubleshoot call transfers](/phone-calling/in-call-control/transfer-calls/debug-forwarding-drops).
## API reference
@@ -232,7 +232,7 @@ The [Update Tool API reference](/api-reference/tools/update) documents transfer-
Let a transfer assistant converse with the destination.
-
+
Diagnose incomplete transfers and provider routing failures.