diff --git a/fern/assistants/examples/inbound-support.mdx b/fern/assistants/examples/inbound-support.mdx index ab8fbac3..26c8d9a2 100644 --- a/fern/assistants/examples/inbound-support.mdx +++ b/fern/assistants/examples/inbound-support.mdx @@ -159,7 +159,7 @@ We will be creating a customer support agent for VapiBank, a bank that wants to }, voice: { provider: "11labs", - voice_id: "burt" + voice_id: "rachel" } }); @@ -193,7 +193,7 @@ We will be creating a customer support agent for VapiBank, a bank that wants to }, "voice": { "provider": "11labs", - "voice_id": "burt" + "voice_id": "rachel" } } @@ -222,7 +222,7 @@ We will be creating a customer support agent for VapiBank, a bank that wants to }, "voice": { "provider": "11labs", - "voice_id": "burt" + "voice_id": "rachel" } }' ``` diff --git a/fern/assistants/examples/support-escalation.mdx b/fern/assistants/examples/support-escalation.mdx index 0e25524c..3f666603 100644 --- a/fern/assistants/examples/support-escalation.mdx +++ b/fern/assistants/examples/support-escalation.mdx @@ -21,6 +21,10 @@ Build an intelligent customer support escalation system that determines transfer * Webhook server for escalation destination logic * CRM integration for customer tier-based routing + +**Important:** Dynamic transfer tools do not support function parameters. When creating a transfer tool with an empty destinations array, you cannot define or pass parameters. The webhook will analyze the conversation transcript and call context instead to determine routing logic. + + ## Prerequisites * A [Vapi account](https://dashboard.vapi.ai/) @@ -49,13 +53,6 @@ We will build a customer support escalation system for TechCorp that intelligent - Set function name: `escalateToSupport` - Add description: `Escalate calls to appropriate support specialists based on customer tier and issue complexity` - - 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 - @@ -72,31 +69,7 @@ We will build a customer support escalation system for TechCorp that intelligent destinations: [], function: { name: "escalateToSupport", - description: "Escalate calls to appropriate support specialists based on customer tier and issue complexity", - parameters: { - type: "object", - properties: { - issue_category: { - type: "string", - description: "Category of customer issue", - enum: ["technical", "billing", "account", "product"] - }, - complexity_level: { - type: "string", - description: "Issue complexity level", - enum: ["basic", "intermediate", "advanced", "critical"] - }, - customer_context: { - type: "string", - description: "Relevant customer information for routing" - }, - escalation_reason: { - type: "string", - description: "Why this needs escalation vs self-service" - } - }, - required: ["issue_category", "complexity_level"] - } + description: "Escalate calls to appropriate support specialists based on customer tier and issue complexity" } }); @@ -131,31 +104,7 @@ We will build a customer support escalation system for TechCorp that intelligent "destinations": [], "function": { "name": "escalateToSupport", - "description": "Escalate calls to appropriate support specialists based on customer tier and issue complexity", - "parameters": { - "type": "object", - "properties": { - "issue_category": { - "type": "string", - "description": "Category of customer issue", - "enum": ["technical", "billing", "account", "product"] - }, - "complexity_level": { - "type": "string", - "description": "Issue complexity level", - "enum": ["basic", "intermediate", "advanced", "critical"] - }, - "customer_context": { - "type": "string", - "description": "Relevant customer information for routing" - }, - "escalation_reason": { - "type": "string", - "description": "Why this needs escalation vs self-service" - } - }, - "required": ["issue_category", "complexity_level"] - } + "description": "Escalate calls to appropriate support specialists based on customer tier and issue complexity" } } @@ -183,31 +132,7 @@ We will build a customer support escalation system for TechCorp that intelligent "destinations": [], "function": { "name": "escalateToSupport", - "description": "Escalate calls to appropriate support specialists based on customer tier and issue complexity", - "parameters": { - "type": "object", - "properties": { - "issue_category": { - "type": "string", - "description": "Category of customer issue", - "enum": ["technical", "billing", "account", "product"] - }, - "complexity_level": { - "type": "string", - "description": "Issue complexity level", - "enum": ["basic", "intermediate", "advanced", "critical"] - }, - "customer_context": { - "type": "string", - "description": "Relevant customer information for routing" - }, - "escalation_reason": { - "type": "string", - "description": "Why this needs escalation vs self-service" - } - }, - "required": ["issue_category", "complexity_level"] - } + "description": "Escalate calls to appropriate support specialists based on customer tier and issue complexity" } }' ``` @@ -235,10 +160,11 @@ We will build a customer support escalation system for TechCorp that intelligent 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 + When you need to escalate, clearly state the issue type and complexity in the conversation before calling the escalation function. For example: + - "I see you're having a technical issue that seems quite complex. Let me connect you with a specialist." + - "This billing matter is critical. I'll transfer you to our billing team right away." + + The system will analyze the conversation to route you to the right specialist. Always be professional and efficient in your support. ``` @@ -284,10 +210,6 @@ Always be professional and efficient in your support.` ], toolIds: [escalationToolId] }, - voice: { - provider: "11labs", - voiceId: "burt" - }, serverUrl: "https://your-app.com/webhook/escalation", serverUrlSecret: process.env.WEBHOOK_SECRET }); @@ -342,10 +264,6 @@ Always be professional and efficient in your support.""" ], "toolIds": [escalation_tool_id] }, - "voice": { - "provider": "11labs", - "voiceId": "burt" - }, "serverUrl": "https://your-app.com/webhook/escalation", "serverUrlSecret": os.getenv("WEBHOOK_SECRET") } @@ -385,7 +303,7 @@ Always be professional and efficient in your support.""" }, "voice": { "provider": "11labs", - "voiceId": "burt" + "voiceId": "rachel" }, "serverUrl": "https://your-app.com/webhook/escalation", "serverUrlSecret": "your-webhook-secret" @@ -421,8 +339,32 @@ Always be professional and efficient in your support.""" // Support escalation logic function determineSupportDestination(request: any) { - const { functionCall, call, customer } = request; - const { issue_category, complexity_level, customer_context, escalation_reason } = functionCall.parameters; + const { call, customer } = request; + + // Analyze the transcript to determine issue type and complexity + // Since dynamic transfers don't support parameters, we must analyze the conversation + const transcript = call.transcript || ''; + const lowerTranscript = transcript.toLowerCase(); + + // Determine issue category from conversation + let issue_category = 'general'; + if (lowerTranscript.includes('technical') || lowerTranscript.includes('error') || lowerTranscript.includes('bug')) { + issue_category = 'technical'; + } else if (lowerTranscript.includes('billing') || lowerTranscript.includes('payment') || lowerTranscript.includes('invoice')) { + issue_category = 'billing'; + } else if (lowerTranscript.includes('account') || lowerTranscript.includes('password') || lowerTranscript.includes('login')) { + issue_category = 'account'; + } else if (lowerTranscript.includes('product') || lowerTranscript.includes('feature')) { + issue_category = 'product'; + } + + // Determine complexity level from keywords + let complexity_level = 'basic'; + if (lowerTranscript.includes('urgent') || lowerTranscript.includes('critical') || lowerTranscript.includes('emergency')) { + complexity_level = 'critical'; + } else if (lowerTranscript.includes('complex') || lowerTranscript.includes('advanced') || lowerTranscript.includes('integration')) { + complexity_level = 'advanced'; + } // Simulate customer tier lookup const customerData = lookupCustomerTier(customer.number); @@ -444,7 +386,7 @@ Always be professional and efficient in your support.""" }, { role: "user", - content: `Enterprise customer with ${issue_category} issue. Complexity: ${complexity_level}. Reason: ${escalation_reason}. Context: ${customer_context}` + content: `Enterprise customer with ${issue_category} issue. Complexity: ${complexity_level}.` } ] } @@ -460,7 +402,7 @@ Always be professional and efficient in your support.""" 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}` + message: `Technical ${complexity_level} issue requiring specialist support.` } }; } @@ -473,7 +415,7 @@ Always be professional and efficient in your support.""" 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}` + message: `${issue_category} issue, complexity ${complexity_level}.` } }; } @@ -486,7 +428,7 @@ Always be professional and efficient in your support.""" message: "Transferring you to our product specialists.", transferPlan: { mode: "warm-transfer-say-message", - message: `Product ${complexity_level} inquiry. Context: ${customer_context}` + message: `Product ${complexity_level} inquiry.` } }; } @@ -581,14 +523,31 @@ Always be professional and efficient in your support.""" def determine_support_destination(request_data: Dict[str, Any]) -> Dict[str, Any]: """Determine support escalation destination based on request context""" - function_call = request_data.get('functionCall', {}) - parameters = function_call.get('parameters', {}) + call = request_data.get('call', {}) customer = request_data.get('customer', {}) - issue_category = parameters.get('issue_category', 'general') - complexity_level = parameters.get('complexity_level', 'basic') - customer_context = parameters.get('customer_context', '') - escalation_reason = parameters.get('escalation_reason', '') + # Analyze the transcript to determine issue type and complexity + # Since dynamic transfers don't support parameters, we must analyze the conversation + transcript = call.get('transcript', '') + lower_transcript = transcript.lower() + + # Determine issue category from conversation + issue_category = 'general' + if 'technical' in lower_transcript or 'error' in lower_transcript or 'bug' in lower_transcript: + issue_category = 'technical' + elif 'billing' in lower_transcript or 'payment' in lower_transcript or 'invoice' in lower_transcript: + issue_category = 'billing' + elif 'account' in lower_transcript or 'password' in lower_transcript or 'login' in lower_transcript: + issue_category = 'account' + elif 'product' in lower_transcript or 'feature' in lower_transcript: + issue_category = 'product' + + # Determine complexity level from keywords + complexity_level = 'basic' + if 'urgent' in lower_transcript or 'critical' in lower_transcript or 'emergency' in lower_transcript: + complexity_level = 'critical' + elif 'complex' in lower_transcript or 'advanced' in lower_transcript or 'integration' in lower_transcript: + complexity_level = 'advanced' # Simulate customer tier lookup customer_data = lookup_customer_tier(customer.get('number', '')) @@ -610,7 +569,7 @@ Always be professional and efficient in your support.""" }, { "role": "user", - "content": f"Enterprise customer with {issue_category} issue. Complexity: {complexity_level}. Reason: {escalation_reason}. Context: {customer_context}" + "content": f"Enterprise customer with {issue_category} issue. Complexity: {complexity_level}." } ] } @@ -625,7 +584,7 @@ Always be professional and efficient in your support.""" "message": "Transferring you to our technical support specialists.", "transferPlan": { "mode": "warm-transfer-say-message", - "message": f"Technical {complexity_level} issue. Customer context: {customer_context}. Escalation reason: {escalation_reason}" + "message": f"Technical {complexity_level} issue requiring specialist support." } } @@ -637,7 +596,7 @@ Always be professional and efficient in your support.""" "message": "Connecting you with our billing and account specialists.", "transferPlan": { "mode": "warm-transfer-say-message", - "message": f"{issue_category} issue, complexity {complexity_level}. Context: {customer_context}" + "message": f"{issue_category} issue, complexity {complexity_level}." } } @@ -649,7 +608,7 @@ Always be professional and efficient in your support.""" "message": "Transferring you to our product specialists.", "transferPlan": { "mode": "warm-transfer-say-message", - "message": f"Product {complexity_level} inquiry. Context: {customer_context}" + "message": f"Product {complexity_level} inquiry." } } @@ -939,8 +898,7 @@ function handleEscalationError(error: any, context: any) { // 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, + callId: context.call?.id, timestamp: new Date().toISOString() }); diff --git a/fern/calls/call-dynamic-transfers.mdx b/fern/calls/call-dynamic-transfers.mdx index 81c256ac..2f9277df 100644 --- a/fern/calls/call-dynamic-transfers.mdx +++ b/fern/calls/call-dynamic-transfers.mdx @@ -33,7 +33,11 @@ Dynamic transfers operate by leaving the destination unspecified initially, then 4. **Response** - Server returns destination details and transfer configuration 5. **Transfer** - Vapi executes the transfer to the determined destination -**Available context:** Your webhook receives conversation transcript, extracted variables, customer information, function parameters, and call metadata. +**Available context:** Your webhook receives conversation transcript, extracted variables, customer information, and call metadata. + + +**Important limitation:** Dynamic transfer tools do not support function parameters. When creating a transfer tool with an empty destinations array (for dynamic routing), you cannot define or use parameters. Your webhook must determine routing logic by analyzing the conversation transcript and other context provided in the webhook request. + --- @@ -62,20 +66,7 @@ Dynamic transfers operate by leaving the destination unspecified initially, then destinations: [], function: { name: "dynamicTransfer", - description: "Transfer call to appropriate destination based on customer needs", - parameters: { - type: "object", - properties: { - reason: { - type: "string", - description: "Reason for transfer" - }, - urgency: { - type: "string", - enum: ["low", "medium", "high", "critical"] - } - } - } + description: "Transfer call to appropriate destination based on customer needs" } }); @@ -99,20 +90,7 @@ Dynamic transfers operate by leaving the destination unspecified initially, then "destinations": [], # Empty for dynamic routing "function": { "name": "dynamicTransfer", - "description": "Transfer call to appropriate destination based on customer needs", - "parameters": { - "type": "object", - "properties": { - "reason": { - "type": "string", - "description": "Reason for transfer" - }, - "urgency": { - "type": "string", - "enum": ["low", "medium", "high", "critical"] - } - } - } + "description": "Transfer call to appropriate destination based on customer needs" } } @@ -133,14 +111,7 @@ Dynamic transfers operate by leaving the destination unspecified initially, then "destinations": [], "function": { "name": "dynamicTransfer", - "description": "Transfer call to appropriate destination based on customer needs", - "parameters": { - "type": "object", - "properties": { - "reason": {"type": "string", "description": "Reason for transfer"}, - "urgency": {"type": "string", "enum": ["low", "medium", "high", "critical"]} - } - } + "description": "Transfer call to appropriate destination based on customer needs" } }' ``` @@ -168,14 +139,14 @@ Dynamic transfers operate by leaving the destination unspecified initially, then messages: [ { role: "system", - content: "You help customers and transfer them when needed using the dynamicTransfer tool. Assess the customer's needs and transfer to the appropriate department." + content: "You help customers and transfer them when needed using the dynamicTransfer tool. When transferring, clearly state the reason and urgency in the conversation before calling the tool (e.g., 'This seems urgent, let me transfer you to emergency support'). The system will analyze the conversation to route appropriately." } ], toolIds: ["YOUR_DYNAMIC_TOOL_ID"] }, voice: { provider: "11labs", - voiceId: "burt" + voiceId: "rachel" }, serverUrl: "https://your-server.com/webhook", serverUrlSecret: process.env.WEBHOOK_SECRET @@ -199,11 +170,11 @@ Dynamic transfers operate by leaving the destination unspecified initially, then "model": "gpt-4o", "messages": [{ "role": "system", - "content": "You help customers and transfer them when needed using the dynamicTransfer tool. Assess the customer's needs and transfer to the appropriate department." + "content": "You help customers and transfer them when needed using the dynamicTransfer tool. When transferring, clearly state the reason and urgency in the conversation before calling the tool (e.g., 'This seems urgent, let me transfer you to emergency support'). The system will analyze the conversation to route appropriately." }], "toolIds": [tool_id] }, - "voice": {"provider": "11labs", "voiceId": "burt"}, + "voice": {"provider": "11labs", "voiceId": "rachel"}, "serverUrl": "https://your-server.com/webhook", "serverUrlSecret": os.getenv("WEBHOOK_SECRET") } @@ -225,7 +196,7 @@ Dynamic transfers operate by leaving the destination unspecified initially, then "model": "gpt-4o", "messages": [{ "role": "system", - "content": "You help customers and transfer them when needed using the dynamicTransfer tool." + "content": "You help customers and transfer them when needed using the dynamicTransfer tool. When transferring, clearly state the reason and urgency in the conversation before calling the tool." }], "toolIds": ["YOUR_DYNAMIC_TOOL_ID"] }, @@ -273,11 +244,16 @@ Dynamic transfers operate by leaving the destination unspecified initially, then } // Simple routing logic - customize for your needs - const { functionCall, customer } = request; - const urgency = functionCall.parameters?.urgency || 'medium'; + const { call, customer } = request; + + // Analyze transcript to determine urgency since parameters are not supported + const transcript = call.transcript || ''; + const isUrgent = transcript.toLowerCase().includes('urgent') || + transcript.toLowerCase().includes('emergency') || + transcript.toLowerCase().includes('critical'); let destination; - if (urgency === 'critical') { + if (isUrgent) { destination = { type: "number", number: "+1-555-EMERGENCY", @@ -336,10 +312,13 @@ Dynamic transfers operate by leaving the destination unspecified initially, then return {"received": True} # Simple routing logic - customize for your needs - function_call = request_data.get('functionCall', {}) - urgency = function_call.get('parameters', {}).get('urgency', 'medium') + call = request_data.get('call', {}) + + # Analyze transcript to determine urgency since parameters are not supported + transcript = call.get('transcript', '').lower() + is_urgent = 'urgent' in transcript or 'emergency' in transcript or 'critical' in transcript - if urgency == 'critical': + if is_urgent: destination = { "type": "number", "number": "+1-555-EMERGENCY",