Speech Recognition (ASR)
Amazon Connect supports speech recognition through integration with Amazon Lex, allowing callers to interact with your contact flows using natural language instead of pressing buttons.
Overview
Speech recognition (also called Automatic Speech Recognition or ASR) enables:
- Natural conversation - Callers can speak instead of navigating DTMF menus
- Intent recognition - Understand what callers want, not just what they say
- Multi-language support - Over 20 languages and dialects
- Fallback to DTMF - Automatic fallback when speech recognition fails
- Better caller experience - Faster, more intuitive interactions
Amazon Lex Integration
The Switchboard framework integrates with Amazon Lex V2 (recommended) and V1 bots.
Prerequisites
- Amazon Lex Bot - Create and train a Lex bot with intents
- Bot Permissions - Grant Amazon Connect access to invoke the bot
- Bot Alias - Deploy bot to an alias (e.g., "Production", "Development")
Lex Bot Setup Example
// Example Lex V2 Bot Configuration
{
"botName": "CustomerServiceBot",
"intents": [
{
"name": "Sales",
"sampleUtterances": [
"I want to talk to sales",
"Sales department",
"I need to buy something"
]
},
{
"name": "Support",
"sampleUtterances": [
"I need help",
"Technical support",
"Something is broken"
]
},
{
"name": "Billing",
"sampleUtterances": [
"I have a question about my bill",
"Billing department",
"Invoice question"
]
}
]
}Basic ASR Input
Using Fluent API
using Switchboard;
using Switchboard.Configuration;
using Switchboard.Enums;
public class SmartIVRFlow : FlowBuilderBase
{
protected override void Build(FlowBuilder builder)
{
builder.ThenGetInput(input =>
{
// Configure ASR
input.Primary.Mode = InputMode.ASR;
input.Primary.LexBot = "CustomerServiceBot";
input.Primary.LexBotAlias = "Production";
input.Primary.LexBotVersion = "V2";
input.Primary.Locale = "en_US";
input.Primary.Prompt = "How can I help you today?";
input.Primary.ConfidenceThreshold = 0.6;
// Disable fallback for pure ASR
input.EnableFallback = false;
})
.RouteByInput(router =>
{
router.OnIntent("Sales", flow =>
{
flow.PlayPrompt("Transferring you to sales...")
.ThenTransferToQueue("SalesQueue");
});
router.OnIntent("Support", flow =>
{
flow.PlayPrompt("Connecting you to technical support...")
.ThenTransferToQueue("SupportQueue");
});
router.OnIntent("Billing", flow =>
{
flow.PlayPrompt("Let me get you to our billing team...")
.ThenTransferToQueue("BillingQueue");
});
router.OnNoMatch(flow =>
{
flow.PlayPrompt("I didn't understand. Let me connect you to an agent.")
.ThenTransferToQueue("GeneralQueue");
});
router.OnError(flow =>
{
flow.PlayPrompt("Sorry, we're experiencing technical difficulties.")
.ThenDisconnect();
});
});
}
}Using Attributes
using Switchboard.Attributes;
using Switchboard.Actions;
[ContactFlow("SmartIVR")]
public partial class SmartIVRFlow : FlowDefinitionBase
{
[Action(Order = 1)]
[GetCustomerInput(
InputType = CustomerInputType.ASR,
Text = "How can I help you today?",
LexBot = "CustomerServiceBot",
LexBotAlias = "Production",
Locale = "en-US",
ConfidenceThreshold = 0.6)]
public partial void GetIntent();
[Action(Order = 2)]
[Branch(Input = "$.CustomerInput")]
public partial void RouteByIntent()
{
[Case("Sales")]
void Sales()
{
[Message("Transferring to sales...")]
void NotifySales();
[TransferToQueue("SalesQueue")]
void TransferSales();
}
[Case("Support")]
void Support()
{
[Message("Connecting to support...")]
void NotifySupport();
[TransferToQueue("SupportQueue")]
void TransferSupport();
}
[Case("Billing")]
void Billing()
{
[Message("Routing to billing...")]
void NotifyBilling();
[TransferToQueue("BillingQueue")]
void TransferBilling();
}
[Default]
void NoMatch()
{
[Message("I didn't understand.")]
void NotifyNoMatch();
[TransferToQueue("GeneralQueue")]
void TransferGeneral();
}
}
}Sequential Mode (ASR → DTMF Fallback)
Recommended approach - Try speech recognition first, automatically fall back to DTMF if it fails.
Fluent API
builder.ThenGetInput(input =>
{
// Primary input: ASR
input.Primary.Mode = InputMode.ASR;
input.Primary.LexBot = "MenuBot";
input.Primary.LexBotAlias = "Production";
input.Primary.Prompt = "You can say sales, support, or billing";
input.Primary.ConfidenceThreshold = 0.6;
input.Primary.MaxRetries = 2;
// Fallback input: DTMF
input.Fallback.Mode = InputMode.DTMF;
input.Fallback.PromptText = "Or press 1 for sales, 2 for support, 3 for billing";
input.Fallback.MaxDigits = 1;
input.Fallback.TimeoutSeconds = 5;
// Enable fallback
input.EnableFallback = true;
input.FallbackTriggers = FallbackTrigger.AnyError; // Timeout, NoMatch, LowConfidence, etc.
})
.RouteByInput(router =>
{
// Route by intent name (ASR) OR digit (DTMF)
router.OnIntent("Sales", flow => /* ... */)
.OnDigit("1", flow => /* ... */); // Same queue
router.OnIntent("Support", flow => /* ... */)
.OnDigit("2", flow => /* ... */);
router.OnIntent("Billing", flow => /* ... */)
.OnDigit("3", flow => /* ... */);
});Attributes
[Action(Order = 1)]
[GetCustomerInput(
InputType = CustomerInputType.Sequential, // ASR → DTMF
Text = "You can say sales, support, or billing",
LexBot = "MenuBot",
LexBotAlias = "Production",
ConfidenceThreshold = 0.6,
FallbackText = "Or press 1 for sales, 2 for support, 3 for billing")]
[MaxDigits(1)]
[Timeout(5)]
public partial void GetDepartmentChoice();Fallback Triggers
Control when fallback to DTMF occurs:
using Switchboard.Enums;
input.FallbackTriggers = FallbackTrigger.Timeout | FallbackTrigger.NoMatch;
// Available triggers:
// - Timeout: Customer didn't respond in time
// - InvalidInput: Input format invalid
// - NoMatch: No intent matched
// - Error: Lex bot error
// - LowConfidence: Below confidence threshold
// - MaxRetriesExceeded: Exceeded retry attempts
// - AnyError: Timeout | InvalidInput | NoMatch | Error | LowConfidence
// - All: All conditions (including MaxRetriesExceeded)Routing by Intent
Use RouteByInput() to route based on Lex intent names:
.RouteByInput(router =>
{
// Route by Lex intent name
router.OnIntent("Sales", flow =>
{
flow.PlayPrompt("Great! Let me connect you to our sales team.")
.ThenTransferToQueue("SalesQueue");
});
router.OnIntent("Support", flow =>
{
flow.PlayPrompt("I'll get you to technical support right away.")
.ThenTransferToQueue("SupportQueue");
});
router.OnIntent("CancelOrder", flow =>
{
// More complex routing
flow.ThenInvokeLambda(lambda =>
{
lambda.FunctionArn = "arn:aws:lambda:...";
lambda.InputParameters["orderId"] = "$.Attributes.OrderId";
})
.RouteByLambda(result =>
{
result.OnSuccess(s => s.PlayPrompt("Order cancelled successfully."));
result.OnError(e => e.PlayPrompt("Unable to cancel order."));
});
});
// Handle failures
router.OnNoMatch(flow =>
{
flow.PlayPrompt("I'm not sure I understood that.")
.ThenGetInput(/* retry with clearer prompt */);
});
router.OnTimeout(flow =>
{
flow.PlayPrompt("I didn't hear anything. Let me transfer you to an agent.")
.ThenTransferToQueue("GeneralQueue");
});
router.OnError(flow =>
{
flow.PlayPrompt("We're having technical issues.")
.ThenDisconnect();
});
});Confidence Threshold Tuning
The confidence threshold determines how certain Lex must be before accepting a match.
input.Primary.ConfidenceThreshold = 0.6; // 60% confidence required
// Threshold guidelines:
// 0.4 (40%): Low threshold - accept most matches (more false positives)
// 0.6 (60%): Medium threshold - balanced (recommended for most use cases)
// 0.8 (80%): High threshold - only high-confidence matches (more fallbacks)Confidence Threshold Strategy
// Start with medium confidence
input.Primary.ConfidenceThreshold = 0.6;
input.Primary.MaxRetries = 2;
// For critical transactions, use higher confidence
[GetCustomerInput(
Text = "Say the account number you want to transfer funds to",
LexBot = "AccountTransferBot",
ConfidenceThreshold = 0.8)] // Higher confidence for financial actions
public partial void GetTargetAccount();
// For simple menus, use lower confidence
[GetCustomerInput(
Text = "Are you calling about an existing order or a new order?",
LexBot = "OrderTypeBot",
ConfidenceThreshold = 0.5)] // Lower confidence for binary choice
public partial void GetOrderType();Multi-Language ASR
Support multiple languages by configuring the locale:
// English (US)
input.Primary.Locale = "en_US";
// Spanish (US)
input.Primary.Locale = "es_US";
// French (Canada)
input.Primary.Locale = "fr_CA";
// Supported locales (Amazon Lex V2):
// - en_US, en_GB, en_AU, en_IN
// - es_ES, es_US, es_419
// - fr_FR, fr_CA
// - de_DE
// - it_IT
// - ja_JP
// - ko_KR
// - pt_BR
// - zh_CN
// And more...Dynamic Language Selection
builder.ThenGetInput(input =>
{
input.Primary.Mode = InputMode.DTMF;
input.Primary.Prompt = "For English press 1. Para Español oprima 2.";
input.Primary.MaxDigits = 1;
})
.RouteByInput(router =>
{
router.OnDigit("1", flow =>
{
flow.ThenSetAttribute("Language", "en_US")
.ThenGetInput(input =>
{
input.Primary.Mode = InputMode.ASR;
input.Primary.Locale = "en_US";
input.Primary.LexBot = "CustomerServiceBot_EN";
input.Primary.Prompt = "How can I help you today?";
});
});
router.OnDigit("2", flow =>
{
flow.ThenSetAttribute("Language", "es_US")
.ThenGetInput(input =>
{
input.Primary.Mode = InputMode.ASR;
input.Primary.Locale = "es_US";
input.Primary.LexBot = "CustomerServiceBot_ES";
input.Primary.Prompt = "¿Cómo puedo ayudarte hoy?";
});
});
});Complete Example: Smart IVR with ASR
A production-ready example combining all ASR features:
using Switchboard;
using Switchboard.Configuration;
using Switchboard.Enums;
public class SmartIVRFlowBuilder : FlowBuilderBase
{
protected override void Build(FlowBuilder builder)
{
builder
// Welcome message
.PlayPrompt("Welcome to Acme Corporation.")
// Language selection
.ThenGetInput(input =>
{
input.Primary.Mode = InputMode.DTMF;
input.Primary.Prompt = "For English, press 1. Para Español, oprima 2.";
input.Primary.MaxDigits = 1;
input.Primary.TimeoutSeconds = 5;
})
.RouteByInput(router =>
{
router.OnDigit("1", flow => flow.ThenContinue("EnglishIVR"));
router.OnDigit("2", flow => flow.ThenContinue("SpanishIVR"));
router.OnTimeout(flow => flow.ThenContinue("EnglishIVR")); // Default to English
})
// English ASR Menu
.ThenJoinPoint("EnglishIVR")
.ThenSetAttribute("Language", "en_US")
.ThenGetInput(input =>
{
// Primary: ASR
input.Primary.Mode = InputMode.ASR;
input.Primary.LexBot = "AcmeIVR_EN";
input.Primary.LexBotAlias = "Production";
input.Primary.LexBotVersion = "V2";
input.Primary.Locale = "en_US";
input.Primary.Prompt = "I can help you with sales, support, or billing. What can I do for you?";
input.Primary.ConfidenceThreshold = 0.6;
input.Primary.MaxRetries = 2;
// Fallback: DTMF
input.Fallback.Mode = InputMode.DTMF;
input.Fallback.PromptText = "Or press 1 for sales, 2 for support, 3 for billing.";
input.Fallback.MaxDigits = 1;
input.Fallback.TimeoutSeconds = 5;
// Enable sequential fallback
input.EnableFallback = true;
input.FallbackTriggers = FallbackTrigger.AnyError;
})
.RouteByInput(router =>
{
router.OnIntent("Sales", flow =>
{
flow.PlayPrompt("Let me connect you to our sales team.")
.ThenTransferToQueue("SalesQueue");
})
.OnDigit("1", flow => // DTMF fallback for Sales
{
flow.PlayPrompt("Transferring you to sales.")
.ThenTransferToQueue("SalesQueue");
});
router.OnIntent("Support", flow =>
{
flow.PlayPrompt("I'll get you to technical support.")
.ThenTransferToQueue("SupportQueue");
})
.OnDigit("2", flow =>
{
flow.PlayPrompt("Connecting to support.")
.ThenTransferToQueue("SupportQueue");
});
router.OnIntent("Billing", flow =>
{
flow.PlayPrompt("Routing you to our billing department.")
.ThenTransferToQueue("BillingQueue");
})
.OnDigit("3", flow =>
{
flow.PlayPrompt("Transferring to billing.")
.ThenTransferToQueue("BillingQueue");
});
router.OnNoMatch(flow =>
{
flow.PlayPrompt("I'm sorry, I didn't understand. Let me transfer you to an agent.")
.ThenTransferToQueue("GeneralQueue");
});
router.OnError(flow =>
{
flow.PlayPrompt("We're experiencing technical difficulties.")
.ThenTransferToQueue("GeneralQueue");
});
})
// Spanish ASR Menu (similar structure)
.ThenJoinPoint("SpanishIVR")
.ThenSetAttribute("Language", "es_US")
.ThenGetInput(input =>
{
input.Primary.Mode = InputMode.ASR;
input.Primary.LexBot = "AcmeIVR_ES";
input.Primary.Locale = "es_US";
input.Primary.Prompt = "Puedo ayudarte con ventas, soporte técnico o facturación. ¿En qué puedo ayudarte?";
// ... similar configuration
});
// ... Spanish routing
}
}Best Practices
1. Always Provide DTMF Fallback
Use Sequential mode for better caller experience:
input.Primary.Mode = InputMode.ASR;
input.Fallback.Mode = InputMode.DTMF;
input.EnableFallback = true;2. Set Appropriate Confidence Thresholds
- Simple menus: 0.5-0.6 confidence
- General purpose: 0.6-0.7 confidence
- Critical transactions: 0.7-0.8 confidence
3. Provide Clear Prompts
// Good: Specific, actionable
"I can help you with sales, support, or billing. What do you need?"
// Bad: Vague, open-ended
"How can I help you?"4. Handle All Error Cases
router.OnNoMatch(flow => /* ... */)
.OnTimeout(flow => /* ... */)
.OnError(flow => /* ... */);5. Test with Real Users
- Test with different accents and speaking styles
- Monitor confidence scores in CloudWatch
- Adjust thresholds based on real data
Troubleshooting
Low Recognition Accuracy
Problem: Lex isn't recognizing caller intent correctly
Solutions:
- Add more training utterances to your Lex bot
- Lower confidence threshold (start at 0.5, increase gradually)
- Provide clearer prompts with examples
- Test with diverse voices and accents
Frequent Fallback to DTMF
Problem: System always falls back to DTMF
Solutions:
- Check Lex bot permissions - Ensure Connect has invoke permissions
- Verify bot alias - Must match deployment alias exactly
- Check locale setting - Must match bot language
- Review CloudWatch logs - Check for Lex errors
Intent Not Matched
Problem: Lex returns NoMatch even with valid input
Solutions:
- Add more sample utterances covering variations
- Use slot elicitation for complex intents
- Enable conversation logs in Lex for debugging
- Lower confidence threshold temporarily to see what's being recognized
Timeout Issues
Problem: Callers not responding in time
Solutions:
- Increase timeout to 8-10 seconds for ASR
- Add retry prompt with clearer instructions
- Increase MaxRetries to 3 for important decisions
See Also
- Flow Basics - General flow building concepts
- Routing - Advanced routing patterns
- Dynamic Attributes - Runtime configuration
- Multi-Language Support - Internationalization
- Amazon Lex Documentation - Official Lex docs