Call Transfers
Call transfers enable your AI agents to route calls to human agents, external phone numbers, or other systems when needed. This critical capability allows you to build hybrid workflows where AI handles routine interactions and humans address complex or sensitive issues.
What Are Call Transfers?
Call transfers hand off an active conversation from your AI agent to another destination—typically a human agent, a different phone number, or an external system. The transfer process can be immediate (cold transfer), consultative (warm transfer), or programmatically determined (HTTP transfer).
Common Use Cases:
- Escalating complex technical issues from AI to human support
- Routing sales inquiries to qualified sales representatives
- Transferring billing questions to payment specialists
- After-hours routing to on-call emergency services
- Multi-level support workflows (tier 1 AI, tier 2 human)
- Geographic routing to regional offices
- VIP customer routing to dedicated account managers
Best Practice: Use AI agents to handle 70-80% of routine inquiries, then transfer the remaining 20-30% that require human expertise, empathy, or decision-making authority.
Transfer Types Comparison
Dasha BlackBox supports three distinct transfer methods, each optimized for different scenarios:
| Feature | Cold Transfer | Warm Transfer | HTTP Transfer |
|---|
| Speed | Fastest | Slower (consultation required) | Variable (webhook latency) |
| Context Sharing | None | Full conversation context | Configurable |
| AI Agent Role | Disconnects immediately | Briefs human, then disconnects | Routes based on logic |
| Destination | Fixed phone/SIP | Fixed phone/SIP | Dynamic (determined by webhook) |
| Best For | Simple routing, IVR-style | Complex issues, VIP customers | Business rule routing, load balancing |
| User Experience | Fast but impersonal | Personal, smooth handoff | Depends on implementation |
| Configuration Complexity | Low | Medium | High |
| Fallback Support | No | No | Yes (via fallback transfer) |
| Caller ID Mode | N/A | Configurable (user or agent phone) | Configurable via warm fallback |
Transfer Type Decision Tree
Do you need dynamic routing based on business logic?
├─ Yes → Use HTTP Transfer
└─ No → Does the human agent need context before talking to caller?
├─ Yes → Use Warm Transfer
└─ No → Use Cold Transfer
Cold Transfer (Blind Transfer)
Cold transfer immediately routes the call to a destination phone number or SIP endpoint. The AI agent disconnects as soon as the transfer begins, and the caller connects directly to the new destination without any introduction or context sharing.
How Cold Transfer Works
When to Use Cold Transfer
Ideal Scenarios:
- IVR-Style Routing: “Press 1 for sales, 2 for support” equivalents
- Department Routing: Simple redirection to known departments
- After-Hours Forwarding: Route to voicemail or answering service
- Emergency Escalation: Immediate connection to emergency contacts
- High-Volume Routing: Minimize latency for simple redirects
Not Recommended For:
- Complex issues requiring context transfer
- VIP customers expecting personalized service
- Situations where availability confirmation is needed
- Cases where the caller’s issue needs explanation
Configuring Cold Transfer
Via Dashboard
Via API
System Prompt
Step-by-Step Configuration:
- Navigate to your agent’s Features tab
- Locate the Call Transfer section
- Toggle Enable Call Transfer to ON
- Select Cold Transfer from the transfer type dropdown
- Enter the destination endpoint:
- Phone number in E.164 format:
+1-555-123-4567
- SIP URI:
sip:support@example.com
- (Optional) Add transfer routes for multiple destinations
- Click Save Agent
Configure cold transfer with destination endpoint Basic Cold Transfer Configuration:const response = await fetch('https://your-api-url.com/api/v1/agents', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: "Support Router Agent",
isEnabled: true,
config: {
primaryLanguage: "en-US",
llmConfig: {
vendor: "openai",
model: "gpt-4.1-mini",
prompt: `You are a support routing agent. When users request billing help,
say "Let me transfer you to our billing department" then transfer them.
When they need technical support, transfer to technical support.`,
options: { temperature: 0.7 }
},
ttsConfig: {
provider: "Dasha",
providerVoiceId: "default-voice",
model: "common"
},
sttConfig: { vendor: "Auto" },
transfer: {
type: "cold",
endpointDestination: "+1-555-0100",
isEnabled: true
}
}
})
});
const agent = await response.json();
console.log('Agent created with cold transfer:', agent.agentId);
Multiple Transfer Routes:{
config: {
transfer: {
type: "cold",
isEnabled: true,
endpointDestination: "+1-555-0100", // Default destination
transferRoutes: [
{
name: "billing_department",
destination: "+1-555-0100",
description: "Billing and payment inquiries"
},
{
name: "technical_support",
destination: "+1-555-0101",
description: "Technical product support"
},
{
name: "sales_team",
destination: "sip:sales@company.com",
description: "New sales and product demos"
},
{
name: "emergency_line",
destination: "+1-555-0911",
description: "Urgent issues requiring immediate attention"
}
]
}
}
}
Example Cold Transfer Prompt:You are a customer service routing agent for Acme Corp.
# Your Role
Route callers to the appropriate department quickly and accurately.
# Transfer Instructions
When transferring calls, follow these steps:
1. Confirm you understand the caller's need
2. Tell them which department you're transferring to
3. Set expectations for the transfer
4. Execute the transfer
# Transfer Routes
- Billing questions → transfer to billing_department
- Technical problems → transfer to technical_support
- Sales inquiries → transfer to sales_team
- Urgent emergencies → transfer to emergency_line immediately
# Example Conversation
Caller: "I have a question about my bill"
You: "I can help connect you to our billing department. They'll be
able to assist with your billing question. Please hold while I
transfer you."
[Transfer to billing_department]
All phone numbers must use E.164 format for transfers to work correctly:
E.164 Format Rules:
- Start with
+ (plus sign)
- Include country code (1-3 digits)
- Include area code and local number
- No spaces, dashes, or parentheses
- Maximum 15 digits total
Valid Examples:
+1-555-123-4567 ✓ (US number with hyphens allowed)
+14155551234 ✓ (US number, compact)
+44-20-7123-4567 ✓ (UK number)
+86-10-1234-5678 ✓ (China number)
Invalid Examples:
555-123-4567 ✗ (Missing country code and +)
1-555-123-4567 ✗ (Missing + prefix)
(555) 123-4567 ✗ (Invalid formatting)
+1 555 123 4567 ✗ (Spaces not allowed in some systems)
Invalid Phone Numbers: Using incorrect phone number formats will cause transfer failures. Always validate phone numbers against E.164 format before deployment.
For SIP endpoints, use standard SIP URI format:
sip:username@domain
sip:support@company.com
sip:+15551234567@sip.provider.com
Warm Transfer (Attended Transfer)
Warm transfer allows the AI agent to consult with the destination (typically a human agent) before completing the transfer. The AI can brief the human on the caller’s situation, confirm availability, and ensure a smooth handoff.
How Warm Transfer Works
When to Use Warm Transfer
Ideal Scenarios:
- Complex Issues: Technical problems requiring detailed context
- VIP Customers: High-value customers deserving personalized service
- Sensitive Situations: Billing disputes, complaints, legal matters
- Availability Confirmation: Ensure human agent is available before transfer
- Quality Assurance: Human can decline transfer if not qualified
- Context Preservation: Caller doesn’t need to repeat information
Not Recommended For:
- High-volume, simple routing (use cold transfer)
- After-hours scenarios (no human available)
- Time-sensitive emergencies (cold transfer is faster)
- Situations where consultation adds no value
Configuring Warm Transfer
Via Dashboard
Via API
System Prompt
Step-by-Step Configuration:
- Navigate to your agent’s Features tab
- Toggle Enable Call Transfer to ON
- Select Warm Transfer from the transfer type dropdown
- Enter the destination endpoint (phone or SIP)
- Configure warm transfer behavior:
- Caller ID Mode: Select which phone number the human agent sees (Agent Phone Number or User Phone Number)
- Consultation Script: What AI says to human during consultation
- Customer Hold Message: What caller hears during consultation
- Continue After Operator Disconnected: Resume AI if human declines
- Continue Recording: Record the transferred conversation
- Click Save Agent
Configure warm transfer with consultation settings Basic Warm Transfer Configuration:const response = await fetch('https://your-api-url.com/api/v1/agents', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: "Premium Support Agent",
isEnabled: true,
config: {
primaryLanguage: "en-US",
llmConfig: {
vendor: "openai",
model: "gpt-4.1",
prompt: `You are a premium customer support agent. For complex issues,
transfer to a human specialist with full context. During consultation,
brief the specialist on: customer name, issue description, steps already
tried, and urgency level.`,
options: { temperature: 0.7 }
},
ttsConfig: {
provider: "ElevenLabs",
providerVoiceId: "21m00Tcm4TlvDq8ikWAM",
model: "eleven_turbo_v2_5"
},
sttConfig: { vendor: "Auto" },
transfer: {
type: "warm",
isEnabled: true,
endpointDestination: "+1-555-0200",
sayPhraseToCustomer: "Please hold while I connect you with a specialist who can help.",
continueAfterOperatorDisconnected: true,
continueRecordingInTransfer: true,
callerIdMode: "userPhoneNumber", // Human agent sees the customer's phone number
interactionWithOperator: {
type: "smart_v1",
briefing: "Brief the specialist on caller's name, issue, and urgency"
}
}
}
})
});
const agent = await response.json();
console.log('Agent created with warm transfer:', agent.agentId);
Advanced Warm Transfer with Static Briefing:{
config: {
transfer: {
type: "warm",
isEnabled: true,
endpointDestination: "sip:specialist@company.com",
sayPhraseToCustomer: "One moment please, I'm getting a specialist for you.",
continueAfterOperatorDisconnected: true,
continueRecordingInTransfer: false,
callerIdMode: "agentPhoneNumber", // Human agent sees the company's internal number
interactionWithOperator: {
type: "static",
script: "Hello, this is the AI agent. I have a customer on the line who needs advanced technical support. The issue involves network connectivity problems. Are you able to assist?"
}
}
}
}
Example Warm Transfer Prompt:You are a technical support agent for TechCorp.
# Your Role
Handle basic technical questions and escalate complex issues to human specialists.
# When to Transfer
Transfer to a human specialist when:
- Issue requires advanced troubleshooting (above tier 1)
- Customer has tried basic steps without success
- Customer explicitly requests human assistance
- Issue involves account security or sensitive data
- Problem affects critical business operations
# Warm Transfer Process
1. Gather Context:
- Customer name and account ID
- Detailed issue description
- Steps already attempted
- Urgency level (low, medium, high, critical)
- Impact on customer's business
2. Set Expectations:
Say: "I'm going to connect you with a specialist who can help.
Please hold while I brief them on your situation."
3. Brief the Specialist:
During consultation, say: "Hi, this is the AI assistant. I have
[customer name] on the line with [issue description]. They've tried
[steps taken]. This is [urgency level]. Are you able to assist?"
4. Complete Transfer:
After specialist confirms, complete the transfer.
# Example Conversation
Customer: "I've reset my router 3 times and still can't connect"
You: "I understand you've already tried basic troubleshooting. Let me
connect you with one of our technical specialists who can dive deeper
into this. Please hold while I brief them."
[Consult with specialist]
You (to specialist): "I have John Smith with persistent connectivity
issues. He's reset the router multiple times without success. Are you
able to help?"
Specialist: "Yes, transfer them over."
[Complete transfer]
Warm Transfer Behavior Options
Interaction with Operator:
| Option | Description | Use Case |
|---|
smart_v1 | AI dynamically briefs human based on conversation context | Complex, variable issues |
static | AI uses pre-defined script every time | Consistent, simple briefings |
Additional Settings:
-
sayPhraseToCustomer: What the caller hears while AI consults with human
- Example:
"Please hold while I connect you with a specialist"
-
continueAfterOperatorDisconnected: If
true, AI resumes if human declines or disconnects
- Example: Human says “I can’t take this call” → AI continues with caller
-
continueRecordingInTransfer: If
true, continues recording after transfer
- Required for compliance in some industries
Advanced Warm Transfer Parameters
This section documents additional warm transfer parameters for advanced configurations.
Continue Recognition In Transfer
The continueRecognitionInTransfer parameter controls whether speech recognition continues during the warm transfer phase. This is useful when the AI agent needs to listen to the conversation between the caller and the human operator during the transfer.
| Parameter | Type | Default | Description |
|---|
continueRecognitionInTransfer | boolean | false | Continue speech recognition during the transfer |
When to Enable:
- Supervisor Monitoring: When AI needs to hear the transfer conversation for quality assurance
- Context Extraction: When you want to capture additional information from the human-to-human handoff
- Training Data: When collecting conversation data for AI model improvement
- Compliance: When regulations require full conversation capture
API Example:
{
config: {
features: {
transfer: {
type: "warm",
isEnabled: true,
endpointDestination: "+1-555-0200",
continueRecognitionInTransfer: true, // AI continues listening during transfer
continueRecordingInTransfer: true,
interactionWithOperator: {
type: "smartV1"
}
}
}
}
}
Enabling continueRecognitionInTransfer may have privacy and compliance implications. Ensure you have appropriate consent and disclosures in place before enabling this feature.
Caller ID Mode
The Caller ID Mode setting determines which phone number is displayed to the transfer destination (human agent) when the AI initiates a warm transfer call. This setting is crucial for helping human agents identify and prepare for incoming transferred calls.
Available Modes:
| Mode | Value | Description |
|---|
| Agent Phone Number | agentPhoneNumber | Uses the AI agent’s configured outbound SIP fromUser as the caller ID. The human agent sees the company’s number. This is the default. |
| User Phone Number | userPhoneNumber | Uses the original caller’s phone number as the caller ID. The human agent sees the customer’s number on their phone. |
When callerIdMode is not specified or set to null, the system defaults to agentPhoneNumber behavior.
When to Use Each Mode
Use agentPhoneNumber (default) when:
- You want a consistent, recognizable internal number for all transfers
- Human agents work in a call center that routes by internal extension
- Privacy requirements prevent sharing customer phone numbers with agents
- You need to distinguish AI-transferred calls from direct customer calls
- Your phone system filters or routes calls based on caller ID
Use userPhoneNumber when:
- Human agents need to identify the caller before answering
- Agents use CRM screen pops that match on caller ID
- You want agents to see the customer’s callback number
- Agents use caller ID to pull up customer records
- Internal routing systems rely on caller ID for prioritization
Configuring Caller ID Mode
Step-by-Step Configuration:
- Navigate to your agent’s Features tab
- Enable Call Transfer and select Warm Transfer
- Find the Caller ID Mode dropdown
- Select your preferred mode:
- Agent Phone Number (default): AI agent’s configured phone number appears as caller ID
- User Phone Number: Customer’s phone number appears as caller ID
- Click Save Agent
Configure which phone number the human agent sees during warm transfer Caller ID Mode with Warm Transfer:const response = await fetch('https://your-api-url.com/api/v1/agents', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: "Support Agent with Caller ID",
isEnabled: true,
config: {
primaryLanguage: "en-US",
llmConfig: {
vendor: "openai",
model: "gpt-4.1",
prompt: "You are a customer support agent...",
options: { temperature: 0.7 }
},
ttsConfig: {
provider: "Dasha",
providerVoiceId: "default-voice",
model: "common"
},
sttConfig: { vendor: "Auto" },
transfer: {
type: "warm",
isEnabled: true,
endpointDestination: "+1-555-0200",
// Use agent's phone number as caller ID
callerIdMode: "agentPhoneNumber",
sayPhraseToCustomer: "Please hold while I connect you with a specialist.",
continueAfterOperatorDisconnected: true,
interactionWithOperator: {
type: "smart_v1",
briefing: "Brief the specialist on the customer's issue"
}
}
}
})
});
const agent = await response.json();
console.log('Agent created with caller ID mode:', agent.agentId);
Using User Phone Number:{
config: {
transfer: {
type: "warm",
isEnabled: true,
endpointDestination: "+1-555-0200",
// Use user's phone number instead of default agent phone number
callerIdMode: "userPhoneNumber",
sayPhraseToCustomer: "One moment please.",
interactionWithOperator: {
type: "static",
script: "Incoming transfer from AI assistant."
}
}
}
}
Caller ID Mode in HTTP Transfer Warm Fallback:{
config: {
transfer: {
type: "http",
isEnabled: true,
webhook: {
url: "https://api.yourcompany.com/webhooks/transfer-routing"
},
fallback: {
type: "warm",
endpointDestination: "+1-555-0999",
// Configure caller ID mode for the fallback transfer
callerIdMode: "agentPhoneNumber",
sayPhraseToCustomer: "Please hold while I find someone to help.",
continueAfterOperatorDisconnected: true
}
}
}
}
Caller ID Mode API Reference
| Field | Type | Required | Values | Description |
|---|
callerIdMode | string | No | "userPhoneNumber", "agentPhoneNumber" | Determines the caller ID shown to the transfer destination |
SIP Configuration Required: When using agentPhoneNumber mode, ensure your agent has a properly configured outbound SIP fromUser. Without this configuration, the transfer may fail or show an unexpected caller ID.
Operator Name
The operatorName parameter allows you to provide a name reference for the human operator in the conversation. This name is used by the AI when generating contextual phrases, particularly when using threeway messaging with smartV1 behavior.
| Parameter | Type | Default | Description |
|---|
operatorName | string | null | Name reference for the operator in generated phrases |
Use Cases:
- Personalized Handoffs: GPT can include the operator’s name or role in generated handoff phrases
- Context for Threeway Messaging: When using
smartV1 threeway messaging, the operator name provides context for generating appropriate introduction phrases
- Conversation Context: The AI can reference the operator by name during the transfer process
API Example:
{
config: {
features: {
transfer: {
type: "warm",
isEnabled: true,
endpointDestination: "+1-555-0200",
operatorName: "Technical Support Specialist",
threewayMessaging: {
type: "smartV1"
}
}
}
}
}
Threeway Messaging
Threeway messaging enables a unified phrase to be spoken to BOTH the customer and the human operator simultaneously right before the bridge channel is executed. This feature creates a cohesive handoff experience where both parties hear the same announcement.
How It Works: The threeway messaging phrase is delivered via TTS to both the parent conversation (customer) and the warm transfer child block (operator) for simultaneous delivery. This occurs right before the call is bridged.
Threeway Messaging vs. Other Messaging:
| Feature | Who Hears | When | Purpose |
|---|
sayPhraseToCustomer | Customer only | Before consultation | Set customer expectations |
sayPhraseToOperator (in interactionWithOperator.static) | Operator only | During consultation | Brief operator privately |
threewayMessaging | Both simultaneously | Before bridge | Unified handoff announcement |
ThreewaySmartV1Behavior
Smart V1 behavior uses GPT to generate a contextual handoff phrase based on the conversation history and optional additional instructions. The AI crafts an appropriate announcement that makes sense for both parties.
| Field | Type | Required | Description |
|---|
type | "smartV1" | Yes | Type discriminator |
additionalInstructions | string | No | Optional instructions to guide GPT in generating the phrase |
When to Use:
- Variable Contexts: When handoff situations vary and need contextual messaging
- Dynamic Conversations: When the phrase should reflect the specific conversation
- Personalized Experience: When you want GPT to craft relevant, natural-sounding announcements
API Example:
{
config: {
features: {
transfer: {
type: "warm",
isEnabled: true,
endpointDestination: "+1-555-0200",
operatorName: "Billing Specialist",
threewayMessaging: {
type: "smartV1",
additionalInstructions: "Keep it professional and brief. Include a summary of the customer's billing issue."
},
interactionWithOperator: {
type: "smartV1"
}
}
}
}
}
Example Generated Phrases:
Based on conversation context, GPT might generate phrases like:
- “I’m now connecting you with our billing specialist to help resolve your invoice discrepancy.”
- “You’ll be speaking with a technical support representative who can assist with your network configuration issue.”
- “Connecting you both now. Sarah needs help with her premium account upgrade.”
ThreewayStaticBehavior
Static behavior uses a predefined phrase that is spoken unchanged to both parties. This provides consistent, predictable messaging for standardized handoffs.
| Field | Type | Required | Description |
|---|
type | "static" | Yes | Type discriminator |
phrase | string | No | The exact phrase to speak to both parties |
When to Use:
- Consistent Messaging: When you need the same phrase every time
- Compliance Requirements: When exact wording is mandated
- Simple Handoffs: When context-specific phrases aren’t needed
- Predictable Workflows: When standardization is preferred
API Example:
{
config: {
features: {
transfer: {
type: "warm",
isEnabled: true,
endpointDestination: "+1-555-0200",
threewayMessaging: {
type: "static",
phrase: "Connecting you now. Please hold while the line is bridged."
},
interactionWithOperator: {
type: "smartV1"
}
}
}
}
}
Example Static Phrases:
- “Connecting you now”
- “Please hold while I connect you”
- “You are now being connected to a representative”
- “I’m bridging this call now. One moment please.”
Threeway Messaging API Reference
| Field | Type | Required | Default | Description |
|---|
threewayMessaging | object | No | null | Threeway messaging configuration |
threewayMessaging.type | "smartV1" | "static" | Yes | - | Behavior type discriminator |
threewayMessaging.additionalInstructions | string | No | null | Additional GPT instructions (smartV1 only) |
threewayMessaging.phrase | string | No | null | Static phrase to speak (static only) |
Say Phrase To Operator (Static Behavior)
When using interactionWithOperator with type: "static", you can configure a phrase to say to the human operator before bridging the call. This is part of the static interaction behavior and is separate from threeway messaging—it’s heard by the operator only.
| Field | Type | Required | Description |
|---|
sayPhraseToOperator | object | No | Phrase configuration for operator-only messaging |
sayPhraseToOperator.phrase | string | Yes | The phrase to say to the operator |
sayPhraseToOperator.beforeOperator | boolean | No | If true, says phrase before waiting for operator to answer. Default: false |
API Example:
{
config: {
features: {
transfer: {
type: "warm",
isEnabled: true,
endpointDestination: "+1-555-0200",
sayPhraseToCustomer: "Please hold while I connect you with a specialist.",
interactionWithOperator: {
type: "static",
waitFirstAnswerFromOperator: true,
waitChecksTimeoutInS: 4,
sayPhraseToOperator: {
phrase: "Hello, this is the AI assistant. I have a customer with a billing dispute who needs your help.",
beforeOperator: false
}
}
}
}
}
}
Combining Messaging Features: You can use sayPhraseToCustomer (customer only), sayPhraseToOperator in static behavior (operator only), and threewayMessaging (both parties) together for complete control over what each party hears at each stage of the transfer.
Complete Warm Transfer Parameter Reference
| Parameter | Type | Default | Description |
|---|
type | "warm" | - | Transfer type discriminator |
endpointDestination | string | - | Phone number or SIP URI to transfer to |
description | string | - | Human-readable description of the transfer |
isEnabled | boolean | true | Whether this transfer is enabled |
sayPhraseToCustomer | string | null | Phrase spoken to customer before consultation |
continueAfterOperatorDisconnected | boolean | false | Resume AI if operator disconnects/declines |
continueRecordingInTransfer | boolean | true | Continue recording during transfer |
continueRecognitionInTransfer | boolean | false | Continue speech recognition during transfer |
callerIdMode | "userPhoneNumber" | "agentPhoneNumber" | "agentPhoneNumber" | Caller ID shown to operator |
operatorName | string | null | Name reference for operator in generated phrases |
threewayMessaging | object | null | Unified messaging to both parties before bridge |
interactionWithOperator | object | null | Operator consultation behavior (smartV1 or static) |
transferRoutes | array | null | Named routes for LLM-driven destination selection |
failoverBehavior | object | null | Behavior when transfer fails |
sip | object | null | Optional SIP configuration override for the transfer |
Transfer Success and Failure Handling
Successful Transfer Flow:
1. AI initiates warm transfer
2. AI calls human agent
3. Human agent answers
4. AI briefs human on situation
5. Human confirms ability to help
6. AI completes transfer
7. Caller and human are connected
8. AI disconnects
Failure Scenarios:
Human Doesn’t Answer:
// If continueAfterOperatorDisconnected: true
AI: "I apologize, but I couldn't reach a specialist right now. Let me see
what else I can do to help you."
[AI continues conversation]
// If continueAfterOperatorDisconnected: false
AI: "I'm sorry, but no specialist is available at the moment. Would you
like me to take a message or schedule a callback?"
[Call continues with AI for message/callback scheduling]
Human Declines Transfer:
// Human says "I can't take this call"
AI (to customer): "I apologize, but our specialist isn't available to help
with this specific issue right now. Let me see if I can connect you with
someone else, or we can schedule a callback."
HTTP Transfer (Programmatic Routing)
HTTP transfer uses a webhook to determine the transfer destination dynamically based on business logic, caller data, CRM integrations, or real-time availability checks. This enables sophisticated routing strategies beyond simple fixed endpoints.
How HTTP Transfer Works
When to Use HTTP Transfer
Ideal Scenarios:
- CRM Integration: Route to assigned account manager or sales rep
- Load Balancing: Distribute calls across available agents
- Business Hours Routing: Route to on-call vs regular support based on time
- Geographic Routing: Route based on caller’s location or area code
- Priority Routing: VIP customers to dedicated agents
- Skill-Based Routing: Route to agent with specific expertise
- Availability Checking: Verify agent availability before transfer
- Multi-Tenant Systems: Route to customer-specific support teams
Not Recommended For:
- Simple, fixed routing (use cold transfer)
- When webhook latency is unacceptable
- When fallback complexity isn’t justified
Configuring HTTP Transfer
Via Dashboard
Via API
System Prompt
Step-by-Step Configuration:
- Navigate to your agent’s Features tab
- Toggle Enable Call Transfer to ON
- Select HTTP Transfer from the transfer type dropdown
- Enter webhook configuration:
- Webhook URL: Your routing endpoint
- Headers: Authentication tokens, API keys
- Timeout: Max wait time for webhook response (default: 5s)
- Configure fallback transfer (optional):
- Select fallback type (cold or warm)
- Enter fallback destination
- Click Save Agent
Configure HTTP transfer with webhook endpoint Basic HTTP Transfer Configuration:const response = await fetch('https://your-api-url.com/api/v1/agents', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: "Smart Routing Agent",
isEnabled: true,
config: {
primaryLanguage: "en-US",
llmConfig: {
vendor: "openai",
model: "gpt-4.1-mini",
prompt: `You are a smart routing agent. Gather customer information,
then transfer to the appropriate specialist based on their needs.`,
options: { temperature: 0.7 }
},
ttsConfig: {
provider: "Cartesia",
providerVoiceId: "default-voice",
model: "sonic"
},
sttConfig: { vendor: "Auto" },
transfer: {
type: "http",
isEnabled: true,
webhook: {
url: "https://api.yourcompany.com/webhooks/transfer-routing",
headers: {
"Authorization": "Bearer YOUR_WEBHOOK_API_KEY",
"Content-Type": "application/json"
}
},
fallback: {
type: "cold",
endpointDestination: "+1-555-0100" // General support line
}
}
}
})
});
const agent = await response.json();
console.log('Agent created with HTTP transfer:', agent.agentId);
Advanced HTTP Transfer with Warm Fallback:{
config: {
transfer: {
type: "http",
isEnabled: true,
webhook: {
url: "https://api.yourcompany.com/webhooks/intelligent-routing",
headers: {
"Authorization": "Bearer sk-abc123xyz",
"X-API-Version": "v2"
},
customSettings: {
timeout: 5000,
retryAttempts: 2
}
},
fallback: {
type: "warm",
endpointDestination: "+1-555-0999",
sayPhraseToCustomer: "Please hold while I find someone to help you.",
continueAfterOperatorDisconnected: true
}
}
}
}
Example HTTP Transfer Prompt:You are an intelligent routing agent for GlobalCorp.
# Your Role
Gather customer information and route to the appropriate specialist
using our intelligent routing system.
# Information to Collect
Before transferring, gather:
- Customer name
- Account number or email
- Nature of inquiry (sales, support, billing)
- Urgency level
- Preferred language (if not English)
# Transfer Process
1. Greet customer and identify their need
2. Collect required information
3. Say: "Let me connect you with the best person to help. One moment."
4. Initiate transfer (webhook will determine destination)
5. Transfer completes automatically
# Example Conversation
Customer: "I need help with my premium account"
You: "I can help you with that. Can I get your account email?"
Customer: "john@example.com"
You: "Thank you. What can we help you with today?"
Customer: "I want to upgrade my plan"
You: "Perfect. Let me connect you with our account specialist who can
help with upgrades. One moment please."
[Webhook routes to appropriate sales rep based on account data]
When a transfer is triggered, Dasha BlackBox sends a POST request to your webhook URL:
Request Headers:
POST /webhooks/transfer-routing HTTP/1.1
Host: api.yourcompany.com
Content-Type: application/json
Authorization: Bearer YOUR_WEBHOOK_API_KEY
X-Dasha BlackBox-Event: transfer.requested
X-Dasha BlackBox-Call-Id: 660e8400-e29b-41d4-a716-446655440001
X-Dasha BlackBox-Agent-Id: 550e8400-e29b-41d4-a716-446655440000
Request Body:
{
"callId": "660e8400-e29b-41d4-a716-446655440001",
"agentId": "550e8400-e29b-41d4-a716-446655440000",
"agentName": "Smart Routing Agent",
"callerNumber": "+1-555-987-6543",
"callDirection": "inbound",
"timestamp": "2025-10-20T15:30:00Z",
"conversationContext": {
"customerName": "John Doe",
"accountEmail": "john@example.com",
"inquiryType": "upgrade",
"urgency": "medium"
},
"additionalData": {
"customerId": "cust_789",
"accountTier": "premium"
}
}
Field Descriptions:
callId: Unique identifier for the call
agentId: ID of the AI agent handling the call
agentName: Name of the AI agent
callerNumber: Phone number of the caller (E.164 format)
callDirection: "inbound" or "outbound"
timestamp: ISO 8601 timestamp when transfer was requested
conversationContext: Information extracted from conversation
additionalData: Custom metadata from call creation
Your webhook must return a JSON response within the timeout period (default: 5 seconds):
Successful Response (200 OK):
{
"transferTo": "+1-555-0201",
"transferType": "warm",
"context": {
"assignedRep": "Jane Smith",
"repId": "rep_456",
"customerTier": "premium",
"accountValue": "$50,000"
},
"briefing": "Customer John Doe wants to upgrade premium account. Account value: $50k. Assigned rep: Jane Smith."
}
Response Fields:
| Field | Type | Required | Description |
|---|
transferTo | string | Yes | Destination phone number (E.164) or SIP URI |
transferType | string | No | "cold" or "warm" (default: "cold") |
context | object | No | Additional data to log with transfer |
briefing | string | No | For warm transfers, what AI says to human during consultation |
Error Response (4xx or 5xx):
If your webhook returns an error or times out, the fallback transfer (if configured) will be used:
{
"error": "No available agents for this account",
"errorCode": "NO_AGENTS_AVAILABLE",
"useFallback": true
}
Webhook Timeouts: Ensure your webhook responds within the timeout period (default: 5 seconds). Slow responses will trigger fallback transfers and degrade user experience.
Webhook Implementation Examples
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhooks/transfer-routing', async (req, res) => {
try {
const {
callId,
callerNumber,
conversationContext,
additionalData
} = req.body;
// Example: Route based on account tier
const accountTier = additionalData?.accountTier || 'standard';
const inquiryType = conversationContext?.inquiryType;
let transferDestination;
let transferType = 'cold';
// Routing logic
if (accountTier === 'premium') {
// Premium customers get warm transfer to dedicated rep
const assignedRep = await getAssignedRep(additionalData.customerId);
transferDestination = assignedRep.phoneNumber;
transferType = 'warm';
} else if (inquiryType === 'billing') {
// Billing goes to billing department
transferDestination = '+1-555-0100';
} else if (inquiryType === 'technical') {
// Technical to support queue
transferDestination = await getNextAvailableAgent('technical');
} else {
// Default to general support
transferDestination = '+1-555-0200';
}
// Return routing decision
res.json({
transferTo: transferDestination,
transferType: transferType,
context: {
routingReason: `${accountTier} tier - ${inquiryType} inquiry`,
assignedAt: new Date().toISOString()
},
briefing: `Customer ${conversationContext.customerName} from ${accountTier} tier needs help with ${inquiryType}.`
});
} catch (error) {
console.error('Routing error:', error);
// Return error to trigger fallback
res.status(500).json({
error: 'Routing failed',
errorCode: 'INTERNAL_ERROR',
useFallback: true
});
}
});
app.listen(3000, () => {
console.log('Transfer routing webhook listening on port 3000');
});
from flask import Flask, request, jsonify
from datetime import datetime
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
@app.route('/webhooks/transfer-routing', methods=['POST'])
def transfer_routing():
try:
data = request.get_json()
call_id = data.get('callId')
caller_number = data.get('callerNumber')
context = data.get('conversationContext', {})
additional_data = data.get('additionalData', {})
# Routing logic
account_tier = additional_data.get('accountTier', 'standard')
inquiry_type = context.get('inquiryType')
# Determine destination
if account_tier == 'premium':
# Premium customers to dedicated rep
transfer_to = get_assigned_rep(additional_data.get('customerId'))
transfer_type = 'warm'
briefing = f"Premium customer {context.get('customerName')} needs help with {inquiry_type}"
elif inquiry_type == 'billing':
transfer_to = '+1-555-0100'
transfer_type = 'cold'
briefing = None
elif inquiry_type == 'technical':
transfer_to = get_next_available_agent('technical')
transfer_type = 'warm'
briefing = f"Technical support needed for {context.get('customerName')}"
else:
transfer_to = '+1-555-0200'
transfer_type = 'cold'
briefing = None
response = {
'transferTo': transfer_to,
'transferType': transfer_type,
'context': {
'routingReason': f"{account_tier} tier - {inquiry_type} inquiry",
'assignedAt': datetime.utcnow().isoformat()
}
}
if briefing:
response['briefing'] = briefing
logging.info(f"Routed call {call_id} to {transfer_to}")
return jsonify(response), 200
except Exception as e:
logging.error(f"Routing error: {str(e)}")
return jsonify({
'error': 'Routing failed',
'errorCode': 'INTERNAL_ERROR',
'useFallback': True
}), 500
def get_assigned_rep(customer_id):
# Example: Query CRM for assigned representative
# In production, this would hit your CRM API
return '+1-555-0301'
def get_next_available_agent(skill):
# Example: Get next available agent from queue
# In production, this would check agent availability
return '+1-555-0401'
if __name__ == '__main__':
app.run(port=3000)
Custom Integration Example: This demonstrates how you can build your own CRM integration using Dasha BlackBox webhooks. This is NOT a built-in integration and requires custom development on your end.
// Example: Generic webhook integration pattern for connecting to any CRM
// Flow: Dasha BlackBox webhook → Your backend → Your CRM API
const express = require('express');
const app = express();
app.use(express.json());
// Connect to your CRM system (example using generic HTTP client)
const fetchCustomerData = async (customerEmail) => {
// Replace with your CRM's API endpoint
const response = await fetch(`${process.env.CRM_API_URL}/customers?email=${customerEmail}`, {
headers: {
'Authorization': `Bearer ${process.env.CRM_API_KEY}`
}
});
return response.json();
};
app.post('/webhooks/transfer-routing', async (req, res) => {
try {
const { conversationContext, additionalData } = req.body;
const customerEmail = conversationContext?.accountEmail;
// Query your CRM for customer and account owner details
const customerData = await fetchCustomerData(customerEmail);
if (customerData) {
const ownerPhone = customerData.accountOwner?.phone;
const ownerName = customerData.accountOwner?.name;
const accountType = customerData.accountType || 'Standard';
// Route to account owner for enterprise, general support for others
if (accountType === 'Enterprise') {
return res.json({
transferTo: ownerPhone,
transferType: 'warm',
context: {
accountOwner: ownerName,
accountType: accountType,
source: 'CRM'
},
briefing: `Enterprise customer ${conversationContext.customerName}. Account owner: ${ownerName}.`
});
} else {
return res.json({
transferTo: '+1-555-0200', // General support
transferType: 'cold'
});
}
} else {
// Customer not found, use fallback
return res.status(404).json({
error: 'Customer not found in CRM',
useFallback: true
});
}
} catch (error) {
console.error('CRM routing error:', error);
res.status(500).json({
error: 'CRM lookup failed',
useFallback: true
});
}
});
app.listen(3000);
For more control over when transfers occur, you can define transfer as a tool that the LLM explicitly calls:
{
"name": "transfer_to_human",
"description": "Transfer the call to a human agent when the AI cannot resolve the issue or when explicitly requested by the caller",
"schema": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"description": "Reason for transfer (e.g., 'complex technical issue', 'billing dispute', 'customer request')"
},
"department": {
"type": "string",
"enum": ["billing", "technical", "sales", "general"],
"description": "Department to transfer to"
},
"urgency": {
"type": "string",
"enum": ["low", "medium", "high", "critical"],
"description": "Urgency level of the issue"
},
"context": {
"type": "string",
"description": "Brief summary of conversation for the receiving agent"
}
},
"required": ["reason", "department"]
},
"webhook": {
"url": "https://api.yourcompany.com/webhooks/transfer-handler"
}
}
app.post('/webhooks/transfer-handler', async (req, res) => {
const { toolArguments, callId } = req.body;
const { reason, department, urgency, context } = toolArguments;
// Determine transfer destination based on department
const destinations = {
'billing': '+1-555-0100',
'technical': '+1-555-0101',
'sales': '+1-555-0102',
'general': '+1-555-0103'
};
const transferTo = destinations[department] || destinations['general'];
// Log transfer for analytics
await logTransfer(callId, {
reason,
department,
urgency,
context,
destination: transferTo
});
// Return transfer instruction
res.json({
success: true,
message: `Transferring to ${department} department`,
transferTo: transferTo,
transferType: urgency === 'critical' ? 'cold' : 'warm',
briefing: context || `Transfer reason: ${reason}`
});
});
Monitoring Transfers in Call Inspector
The Call Inspector provides detailed visibility into transfer execution and outcomes:
Transfer Event Details
When you open a call with transfers in the Call Inspector, you’ll see:
Transfer Initiation:
- Timestamp when transfer was triggered
- Transfer type (cold, warm, HTTP)
- Destination endpoint (phone number or SIP URI)
- Reason for transfer (from conversation context)
Webhook Execution (HTTP Transfers):
- Request payload sent to webhook
- Response received from webhook
- Latency (time to receive response)
- Routing decision made
Transfer Outcome:
- Success or failure status
- Connection time to destination
- Duration of consultation (warm transfers)
- Fallback usage (if primary transfer failed)
View complete transfer execution details and outcomes
Transfer Metrics to Track
Success Metrics:
- Transfer completion rate (percentage of successful transfers)
- Average transfer connection time
- Warm transfer consultation duration
- Webhook response latency (HTTP transfers)
Failure Metrics:
- Transfer failure rate
- Reasons for failure (no answer, busy, invalid number)
- Fallback usage rate
- Webhook timeout rate
Quality Metrics:
- Post-transfer call duration (indicates if human resolved issue)
- Customer satisfaction after transfer
- Number of re-transfers (bouncing between departments)
- Time to resolution after transfer
Use Cases and Examples
Sales Qualification to Human
Scenario: AI qualifies leads, then transfers hot leads to sales reps
// Agent configuration
{
config: {
llmConfig: {
prompt: `You are a sales qualification assistant. Ask about:
- Company size
- Budget
- Timeline
- Decision maker status
If qualified (company size above 50 employees, budget above $10k, timeline under 3 months),
transfer to sales team. Otherwise, offer to send materials via email.`
},
transfer: {
type: "http",
webhook: {
url: "https://api.yourcompany.com/webhooks/sales-routing"
}
}
}
}
// Webhook determines best sales rep
app.post('/webhooks/sales-routing', async (req, res) => {
const { conversationContext } = req.body;
const { companySize, budget, industry } = conversationContext;
// Route to specialized sales rep
let salesRep;
if (industry === 'healthcare') {
salesRep = await getAvailableRep('healthcare');
} else if (companySize above 500) {
salesRep = await getAvailableRep('enterprise');
} else {
salesRep = await getAvailableRep('smb');
}
res.json({
transferTo: salesRep.phone,
transferType: 'warm',
briefing: `Qualified lead: ${companySize} employees, $${budget} budget, ${industry} industry`
});
});
Support Escalation Path
Scenario: Tier 1 AI support, escalate to human for complex issues
// Agent with escalation tool
{
config: {
llmConfig: {
prompt: `You are tier 1 technical support. Try to resolve:
- Password resets
- Basic connectivity issues
- Account activation
Escalate to human if:
- Customer tried your suggestions without success
- Issue requires backend access
- Customer is frustrated or angry
- Issue is business-critical`
},
tools: [
{
name: "escalate_to_tier2",
description: "Escalate to tier 2 human support for complex issues",
schema: {
type: "object",
properties: {
issue_summary: { type: "string" },
steps_tried: {
type: "array",
items: { type: "string" }
},
customer_sentiment: {
type: "string",
enum: ["calm", "frustrated", "angry"]
}
},
required: ["issue_summary", "steps_tried"]
},
webhook: {
url: "https://api.yourcompany.com/webhooks/tier2-escalation"
}
}
]
}
}
After-Hours Routing
Scenario: Different transfer destinations based on business hours
app.post('/webhooks/transfer-routing', async (req, res) => {
const now = new Date();
const hour = now.getHours();
const dayOfWeek = now.getDay();
// Business hours: Mon-Fri 9am-5pm
const isBusinessHours = (
dayOfWeek >= 1 && dayOfWeek <= 5 &&
hour >= 9 && hour < 17
);
if (isBusinessHours) {
// Transfer to regular support team
res.json({
transferTo: '+1-555-0100',
transferType: 'warm'
});
} else {
// Transfer to on-call emergency line
res.json({
transferTo: '+1-555-0911',
transferType: 'cold',
context: {
afterHours: true,
routedAt: now.toISOString()
}
});
}
});
Geographic Routing
Scenario: Route to regional support based on area code
app.post('/webhooks/transfer-routing', async (req, res) => {
const { callerNumber } = req.body;
// Extract area code from caller number
const areaCode = callerNumber.substring(2, 5); // Extract from +1-XXX-...
// Map area codes to regions
const regionMapping = {
// East Coast (NYC, Boston, DC area codes)
'212': '+1-555-0201', '646': '+1-555-0201', '617': '+1-555-0201',
// West Coast (LA, SF, Seattle area codes)
'213': '+1-555-0202', '415': '+1-555-0202', '206': '+1-555-0202',
// Central (Chicago, Dallas area codes)
'312': '+1-555-0203', '214': '+1-555-0203'
};
const destination = regionMapping[areaCode] || '+1-555-0200'; // Default
res.json({
transferTo: destination,
transferType: 'cold',
context: {
region: getRegionName(destination),
areaCode: areaCode
}
});
});
Best Practices
Clear Transfer Messaging
Do:
- Explain why you’re transferring:
"This requires specialist expertise, so I'm connecting you with our technical team"
- Set expectations:
"This should take just a moment" or "The specialist will pick up in about 30 seconds"
- Confirm before transferring:
"Would you like me to connect you with a specialist?"
Don’t:
- Transfer without explanation:
"Please hold" [transfer]
- Make promises you can’t keep:
"They'll definitely fix this right away"
- Use negative language:
"I can't help you, let me find someone who can"
Fallback Handling
Always configure fallback transfers for HTTP transfers:
{
transfer: {
type: "http",
webhook: { url: "..." },
fallback: {
type: "cold",
endpointDestination: "+1-555-0100" // Always-available general support
}
}
}
Fallback Triggers:
- Webhook timeout (above 5 seconds)
- Webhook returns error status (4xx, 5xx)
- Webhook returns
useFallback: true
- Network errors preventing webhook call
Context Preservation
For warm transfers, ensure the AI provides adequate context:
Good Briefing:
"Hi, this is the AI assistant. I have Sarah Johnson on the line. She's having
trouble accessing her account after a password reset. She's tried resetting
twice without success. This is a high-priority issue as she needs access for
a client meeting in 30 minutes. Are you able to assist?"
Poor Briefing:
"I have a customer with an account issue."
Phone Number Validation
Validate all transfer destinations before deployment:
function isValidE164(phoneNumber) {
const e164Regex = /^\+[1-9]\d{1,14}$/;
return e164Regex.test(phoneNumber);
}
// Validate before configuring transfer
if (!isValidE164(transferDestination)) {
throw new Error(`Invalid phone number: ${transferDestination}`);
}
Testing Strategy
Pre-Production Testing:
- Test all transfer routes with real phone numbers
- Verify fallback handling (disconnect webhook to test)
- Test transfers during different business hours
- Measure transfer connection times
- Review transfer conversation transcripts for quality
Production Monitoring:
- Track transfer success rate (target: above 95%)
- Monitor webhook response times (target: under 2 seconds)
- Alert on high transfer failure rates
- Review transferred call recordings periodically
- Collect feedback from receiving human agents
Troubleshooting Transfer Issues
Transfer Fails to Connect
Symptoms:
- Call drops after transfer attempt
- Caller hears busy signal or “number not reachable”
- Transfer shows as “Failed” in call inspector
Debugging Steps:
-
Verify phone number format (E.164)
// Correct
+1-555-123-4567 ✓
// Incorrect
555-123-4567 ✗ (missing country code and +)
1-555-123-4567 ✗ (missing +)
-
Test destination number externally
- Can you call the number from your phone?
- Is the number active and accepting calls?
- Check for call forwarding or blocking
-
Review SIP configuration (for SIP URIs)
- Verify SIP server is reachable
- Check SIP credentials
- Test SIP endpoint with SIP testing tools
-
Check transfer limits
- Some carriers block transfers to certain number types
- Verify no rate limiting on destination
Webhook Timeouts (HTTP Transfer)
Symptoms:
- HTTP transfers fall back to default destination
- Call inspector shows “Webhook timeout”
- Transfers take longer than expected
Debugging Steps:
-
Measure webhook response time
app.post('/webhooks/transfer-routing', async (req, res) => {
const startTime = Date.now();
// Your routing logic
const result = await determineDestination(req.body);
const duration = Date.now() - startTime;
console.log(`Webhook responded in ${duration}ms`);
res.json(result);
});
-
Optimize slow queries
- Cache CRM lookups
- Use indexed database queries
- Implement connection pooling
- Consider async processing with immediate response
-
Increase timeout (if justified)
{
transfer: {
webhook: {
url: "...",
customSettings: {
timeout: 8000 // Increase to 8 seconds
}
}
}
}
Warm Transfer Consultation Fails
Symptoms:
- AI calls human, but human doesn’t answer
- Transfer completes but without consultation
- Caller complains they had to repeat information
Debugging Steps:
-
Verify human agent availability
- Is the phone number monitored during operating hours?
- Are agents trained to answer transfers?
- Check for call forwarding issues
-
Adjust timeout for consultation phase
- Some agents need more time to answer
- Consider using ring group/queue instead of direct number
-
Configure proper fallback behavior
{
transfer: {
type: "warm",
continueAfterOperatorDisconnected: true, // AI resumes if no answer
sayPhraseToCustomer: "I'm connecting you now. If no one answers, I'll continue helping you."
}
}
Context Not Preserved
Symptoms:
- Caller has to repeat information to human
- Human agent unaware of conversation history
- Poor user experience after transfer
Solutions:
- Use warm transfers instead of cold
- Configure detailed briefing in
interactionWithOperator
- Send call metadata to human agent’s system
- Use CRM integration to display caller history
High Transfer Rates
Symptoms:
- More than 40-50% of calls transfer to humans
- AI transfers for issues it should handle
- Transfer rate increasing over time
Debugging Steps:
-
Review transfer reasons in call inspector
- Are transfers justified?
- Could AI handle these with better prompts?
- Are customers explicitly requesting humans?
-
Improve AI capabilities
- Add tools for common transfer reasons
- Enhance system prompt with more examples
- Train on actual transferred conversations
-
Adjust transfer triggers
- Increase threshold for escalation
- Add clarifying questions before transfer
- Offer alternatives to transfer
Next Steps
API Cross-Refs