Customer support escalation system

Build intelligent support routing using assistants that escalate calls based on customer tier, issue complexity, and agent expertise

Overview

Build an intelligent customer support escalation system that determines transfer destinations dynamically using customer tier analysis, issue complexity assessment, and real-time agent availability. This approach uses transfer tools with empty destinations and webhook servers for maximum escalation flexibility.

Agent Capabilities:

  • Customer tier-based prioritization and routing
  • Issue complexity analysis for specialist routing
  • Real-time agent availability and expertise matching
  • Intelligent escalation with context preservation

What You’ll Build:

  • Transfer tool with dynamic escalation logic
  • Assistant with intelligent support conversation flow
  • Webhook server for escalation destination logic
  • CRM integration for customer tier-based routing

Prerequisites

  • A Vapi account
  • Node.js or Python server environment
  • (Optional) CRM or customer database for tier lookup

Scenario

We will build a customer support escalation system for TechCorp that intelligently routes support calls based on customer tier, issue complexity, and agent expertise in real-time.


1. Create a Dynamic Escalation Tool

2

Create the escalation tool

  • Click Create Tool
  • Select Transfer Call as the tool type
  • Set tool name: Smart Support Escalation
  • Important: Leave the destinations array empty - this creates a dynamic transfer tool
  • Set function name: escalateToSupport
  • Add description: Escalate calls to appropriate support specialists based on customer tier and issue complexity
3

Configure tool parameters

Add these parameters to help the assistant provide context:

  • issue_category (string): Category of customer issue (technical, billing, account, product)
  • complexity_level (string): Issue complexity (basic, intermediate, advanced, critical)
  • customer_context (string): Relevant customer information for routing
  • escalation_reason (string): Why this needs escalation vs self-service

2. Create an Assistant with Smart Escalation

1

Create assistant

  • Navigate to Assistants in your dashboard
  • Click Create Assistant
  • Name: TechCorp Support Assistant
  • Add your dynamic escalation tool to the assistant’s tools
2

Configure system prompt

System Prompt
You are TechCorp's intelligent customer support assistant. Your job is to:
1. Help customers resolve issues when possible
2. Assess issue complexity and customer needs
3. Escalate to human specialists when appropriate using the escalateToSupport function
Try to resolve simple issues first. For complex issues or when customers request human help, escalate intelligently based on:
- Issue category (technical, billing, account, product)
- Complexity level (basic, intermediate, advanced, critical)
- Customer context and history
Always be professional and efficient in your support.
3

Enable server events

In assistant settings, enable the transfer-destination-request server event. This sends webhooks to your server when escalations are triggered.

4

Set server URL

Configure your server URL to handle escalation requests (e.g., https://your-app.com/webhook/escalation)


3. Build Escalation Logic Server

import express from 'express';
import crypto from 'crypto';
const app = express();
app.use(express.json());
// Webhook secret verification
function verifyWebhookSignature(payload: string, signature: string) {
const expectedSignature = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET!)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Support escalation logic
function determineSupportDestination(request: any) {
const { functionCall, call, customer } = request;
const { issue_category, complexity_level, customer_context, escalation_reason } = functionCall.parameters;
// Simulate customer tier lookup
const customerData = lookupCustomerTier(customer.number);
// Enterprise customer escalation
if (customerData?.tier === 'enterprise' || complexity_level === 'critical') {
return {
type: "number",
number: "+1-555-ENTERPRISE-SUPPORT",
message: "Connecting you to our enterprise support specialist.",
transferPlan: {
mode: "warm-transfer-say-summary",
summaryPlan: {
enabled: true,
messages: [
{
role: "system",
content: "Provide a summary for the enterprise support specialist."
},
{
role: "user",
content: `Enterprise customer with ${issue_category} issue. Complexity: ${complexity_level}. Reason: ${escalation_reason}. Context: ${customer_context}`
}
]
}
}
};
}
// Advanced technical issues
if (issue_category === 'technical' && (complexity_level === 'advanced' || complexity_level === 'intermediate')) {
return {
type: "number",
number: "+1-555-TECH-SPECIALISTS",
message: "Transferring you to our technical support specialists.",
transferPlan: {
mode: "warm-transfer-say-message",
message: `Technical ${complexity_level} issue. Customer context: ${customer_context}. Escalation reason: ${escalation_reason}`
}
};
}
// Billing and account specialists
if (issue_category === 'billing' || issue_category === 'account') {
return {
type: "number",
number: "+1-555-BILLING-TEAM",
message: "Connecting you with our billing and account specialists.",
transferPlan: {
mode: "warm-transfer-say-message",
message: `${issue_category} issue, complexity ${complexity_level}. Context: ${customer_context}`
}
};
}
// Product and feature questions
if (issue_category === 'product') {
return {
type: "number",
number: "+1-555-PRODUCT-SUPPORT",
message: "Transferring you to our product specialists.",
transferPlan: {
mode: "warm-transfer-say-message",
message: `Product ${complexity_level} inquiry. Context: ${customer_context}`
}
};
}
// Default to general support
return {
type: "number",
number: "+1-555-GENERAL-SUPPORT",
message: "Connecting you with our support team.",
transferPlan: {
mode: "warm-transfer-say-message",
message: `General ${issue_category} support needed. Level: ${complexity_level}`
}
};
}
// Simulate customer tier lookup
function lookupCustomerTier(phoneNumber: string) {
// In production, integrate with your actual CRM
const mockCustomerData = {
"+1234567890": { tier: "enterprise", account: "TechCorp Enterprise" },
"+0987654321": { tier: "standard", account: "Basic Plan" },
"+1111111111": { tier: "premium", account: "Premium Support" }
};
return mockCustomerData[phoneNumber];
}
// Support escalation webhook
app.post('/webhook/escalation', (req, res) => {
try {
const signature = req.headers['x-vapi-signature'] as string;
const payload = JSON.stringify(req.body);
// Verify webhook signature
if (!verifyWebhookSignature(payload, signature)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const request = req.body;
// Only handle transfer destination requests
if (request.type !== 'transfer-destination-request') {
return res.status(200).json({ received: true });
}
// Determine destination based on escalation context
const destination = determineSupportDestination(request);
res.json({ destination });
} catch (error) {
console.error('Escalation webhook error:', error);
res.status(500).json({
error: 'Unable to determine escalation destination. Please try again.'
});
}
});
app.listen(3000, () => {
console.log('Support escalation server running on port 3000');
});

4. Test Your Support Escalation System

Use dedicated test accounts and transfer destinations staffed by your team. The scripts below create calls for manual testing; they don’t simulate callers or assert that escalation worked.

CheckCoverage
EvalsEscalate when policy requires it, avoid unnecessary escalation, and request the correct destination with the required context.
SimulationsFollow an unresolved issue through escalation, including a failed or unavailable destination and a safe next step.
Controlled callsVerify the real transfer connects, the recipient receives the needed context, and the audio remains usable.

Repeat critical checks and turn routing failures into regression tests. See test decisions with Evals and test outcomes with Simulations.

1

Create a phone number

  • Navigate to Phone Numbers in your dashboard
  • Click Create Phone Number
  • Assign your support assistant to the number
  • Configure any additional settings
2

Test different escalation scenarios

Call your number and test various scenarios:

  • Basic technical questions (should try to resolve first)
  • Complex billing issues from enterprise customers
  • Advanced technical problems requiring specialists
  • Critical issues requiring immediate escalation
3

Monitor escalation patterns

Check your server logs to see:

  • Escalation requests received
  • Customer tier classifications
  • Destination routing decisions
  • Any errors or routing issues

Advanced Integration Examples

CRM Integration (Salesforce)

// Example: Salesforce CRM integration for customer tier lookup
async function lookupCustomerInSalesforce(phoneNumber: string) {
const salesforce = new SalesforceAPI({
clientId: process.env.SALESFORCE_CLIENT_ID,
clientSecret: process.env.SALESFORCE_CLIENT_SECRET,
redirectUri: process.env.SALESFORCE_REDIRECT_URI
});
try {
const customer = await salesforce.query(`
SELECT Id, Account.Type, Support_Tier__c, Case_Count__c, Contract_Level__c
FROM Contact
WHERE Phone = '${phoneNumber}'
`);
return customer.records[0];
} catch (error) {
console.error('Salesforce lookup failed:', error);
return null;
}
}

Issue Complexity Assessment

function assessIssueComplexity(issueDescription: string, customerHistory: any) {
const complexKeywords = ['api', 'integration', 'custom', 'enterprise', 'migration'];
const criticalKeywords = ['down', 'outage', 'critical', 'urgent', 'emergency'];
const hasComplexKeywords = complexKeywords.some(keyword =>
issueDescription.toLowerCase().includes(keyword)
);
const hasCriticalKeywords = criticalKeywords.some(keyword =>
issueDescription.toLowerCase().includes(keyword)
);
if (hasCriticalKeywords || customerHistory.previousEscalations > 2) {
return 'critical';
}
if (hasComplexKeywords || customerHistory.tier === 'enterprise') {
return 'advanced';
}
return 'basic';
}

Agent Availability Checking

function getAvailableSpecialist(category: string, complexity: string) {
const specialists = getSpecialistsByCategory(category);
const qualifiedAgents = specialists.filter(agent =>
agent.complexityLevel >= complexity && agent.isAvailable
);
if (qualifiedAgents.length === 0) {
return {
type: "number",
number: "+1-555-QUEUE-CALLBACK",
message: "All specialists are busy. You'll be added to our priority queue.",
transferPlan: {
mode: "warm-transfer-say-message",
message: `${category} ${complexity} issue - customer needs callback when specialist available`
}
};
}
// Return least busy qualified agent
const bestAgent = qualifiedAgents.sort(
(a, b) => a.activeCallCount - b.activeCallCount
)[0];
return {
type: "number",
number: bestAgent.phoneNumber,
message: `Connecting you to ${bestAgent.name}, our ${category} specialist.`,
transferPlan: {
mode: "warm-transfer-say-summary",
summaryPlan: {
enabled: true,
messages: [
{
role: "system",
content: `Provide a summary for ${bestAgent.name}`
}
]
}
}
};
}

Error Handling Best Practices

Comprehensive Error Handling

function handleEscalationError(error: any, context: any) {
console.error('Support escalation error:', error);
// Log escalation details for debugging
console.error('Escalation context:', {
phoneNumber: context.customer?.number,
issueCategory: context.functionCall?.parameters?.issue_category,
complexityLevel: context.functionCall?.parameters?.complexity_level,
timestamp: new Date().toISOString()
});
// Return fallback destination
return {
type: "number",
number: process.env.FALLBACK_SUPPORT_NUMBER,
message: "I'll connect you with our general support team who can help you.",
transferPlan: {
mode: "warm-transfer-say-message",
message: "Escalation routing error - connecting to general support team"
}
};
}

Queue Management

async function getEscalationWithQueueManagement(context: any) {
try {
const queueStatus = await checkSupportQueueStatus();
const destination = await determineEscalationDestination(context);
// Add queue time estimate if available
if (queueStatus.estimatedWaitTime > 5) {
destination.message += ` Current wait time is approximately ${queueStatus.estimatedWaitTime} minutes.`;
}
return destination;
} catch (error) {
return handleEscalationError(error, context);
}
}

Next Steps

You’ve built a sophisticated customer support escalation system using assistants! Consider these enhancements:

  • Call Analysis - Analyze escalation patterns and optimize routing
  • Function tools - Build additional tools for advanced support logic
  • Webhooks - Learn more about webhook security and advanced event handling