Skip to content

Customer Input Handling ​

The Switchboard framework provides powerful, flexible APIs for collecting customer input through both speech recognition (ASR) and DTMF (keypad) input.

Overview ​

The framework supports three main approaches to customer input:

  1. Simple DTMF Input - Basic keypad input collection
  2. Unified Input Routing - Route based on both speech intents and DTMF digits
  3. Sequential Input Mode - Attempt speech recognition first, with automatic fallback to DTMF

Simple DTMF Input ​

For basic keypad input collection, use the GetCustomerInput method:

csharp
var flow = new FlowBuilder()
    .SetName("BasicInput")
    .GetCustomerInput(
        promptText: "Press 1 for sales, 2 for support",
        branches: input => input
            .OnDigit("1", sales => sales
                .PlayPrompt("Connecting to sales...")
                .TransferToQueue("Sales")
                .Disconnect())
            .OnDigit("2", support => support
                .PlayPrompt("Connecting to support...")
                .TransferToQueue("Support")
                .Disconnect())
            .OnTimeout(timeout => timeout
                .PlayPrompt("We didn't receive your input")
                .Disconnect())
            .OnError(error => error
                .PlayPrompt("An error occurred")
                .Disconnect())
            .OnDefault(def => def
                .PlayPrompt("Invalid selection")
                .Disconnect()),
        configure: action =>
        {
            action.MaxDigits = 1;
            action.TimeoutSeconds = 5;
        },
        identifier: "basic-input")
    .Build();

Why use the branches: overload?

The branches: overload returns the parent IFlowBuilder, which makes it easier to compose flows where you want to keep chaining after you finish defining the input branches.

Unified Input Routing ​

The RouteByInput API allows you to handle both speech intents and DTMF digits with a single routing configuration:

Basic Routing ​

csharp
.RouteByInput(router => router
    .WhenIntent("sales_intent", sales => sales
        .PlayPrompt("Routing to sales team")
        .TransferToQueue("Sales"))
    .WhenDigits(["1", "2" ], sales => sales
        .PlayPrompt("Routing to sales via DTMF")
        .TransferToQueue("Sales"))
    .Otherwise(other => other
        .PlayPrompt("I didn't understand that")
        .Disconnect())
)

Unified Routes (Same Destination) ​

For cases where you want speech and DTMF to route to the same destination:

csharp
.RouteByInput(router => router
    // Unified route - both intent and digits go to same flow
    .When("sales_intent", ["1","2"], sales => sales
        .PlayPrompt("Connecting to sales")
        .TransferToQueue("Sales"))

    .When("support_intent", ["3", "4" ], support => support
        .PlayPrompt("Connecting to support")
        .TransferToQueue("Support"))

    // Fallback for unrecognized input
    .Otherwise(other => other
        .PlayPrompt("I didn't catch that, let me transfer you to an agent")
        .TransferToQueue("GeneralSupport"))
)

Complex Multi-Route Example ​

csharp
.RouteByInput(router => router
    // VIP customers
    .When("vip_intent", ["9"], vip => vip
        .PlayPrompt("Welcome VIP customer! Connecting to our premium support team")
        .TransferToQueue("VIPSupport"))

    // Sales department
    .WhenIntent("sales_intent", sales => sales
        .SetContactAttributes(attrs => attrs["Department"] = "Sales")
        .TransferToQueue("Sales"))

    .WhenDigits(["1", "2" ], sales => sales
        .SetContactAttributes(attrs => attrs["Department"] = "Sales")
        .PlayPrompt("You pressed for sales")
        .TransferToQueue("Sales"))

    // Technical support
    .When("tech_support_intent", new[] { "3" }, tech => tech
        .PlayPrompt("Connecting to technical support")
        .TransferToQueue("TechSupport"))

    // Billing
    .WhenDigits(["4", "5"], billing => billing
        .TransferToQueue("Billing"))

    // Default/unrecognized
    .Otherwise(other => other
        .PlayPrompt("I'm not sure what you need. Let me connect you with someone who can help")
        .TransferToQueue("GeneralSupport"))
)

Sequential Input Mode (ASR → DTMF Fallback) ​

Sequential mode attempts speech recognition first, then automatically falls back to DTMF input based on configurable triggers.

Basic Sequential Configuration ​

csharp
.GetCustomerInput(input =>
{
    // Primary mode: Try speech first
    input.Primary.Mode = InputMode.Speech;
    input.Primary.LexBot = "CustomerServiceBot";
    input.Primary.LexBotAlias = "Production";
    input.Primary.Prompt = "How can I help you today? Say sales, support, or billing.";
    input.Primary.ConfidenceThreshold = 0.7; // Only accept matches >= 70% confidence
    input.Primary.MaxRetries = 2;

    // Fallback mode: Use DTMF if speech fails
    input.Fallback.Mode = InputMode.DTMF;
    input.Fallback.PromptText = "I didn't catch that. Please use your keypad. Press 1 for sales, 2 for support, 3 for billing.";
    input.Fallback.MaxDigits = 1;

    // Control when to fallback
    input.FallbackTriggers = FallbackTrigger.NoMatch | FallbackTrigger.LowConfidence | FallbackTrigger.Timeout;
    input.EnableFallback = true;
})

Fallback Triggers ​

The FallbackTrigger enum controls when the system should fallback from speech to DTMF:

csharp
// Individual triggers
FallbackTrigger.Timeout           // Customer didn't say anything
FallbackTrigger.NoMatch           // Speech not recognized
FallbackTrigger.LowConfidence     // Confidence below threshold
FallbackTrigger.InvalidInput      // Input doesn't match expected format
FallbackTrigger.Error             // System error during recognition
FallbackTrigger.MaxRetriesExceeded // Customer exceeded retry attempts

// Combined triggers (use bitwise OR)
input.FallbackTriggers = FallbackTrigger.Timeout | FallbackTrigger.NoMatch;

// Predefined combinations
FallbackTrigger.AnyError          // All error conditions (excludes MaxRetriesExceeded)
FallbackTrigger.All               // Everything (including MaxRetriesExceeded)

Advanced Sequential Example ​

csharp
.GetCustomerInput(input =>
{
    // ===== PRIMARY: Speech Recognition =====
    input.Primary.Mode = InputMode.Speech;
    input.Primary.LexBot = "MultiLanguageBot";
    input.Primary.LexBotAlias = "Production";
    input.Primary.LexBotVersion = "V2";
    input.Primary.Locale = "en_US";

    // Initial prompt
    input.Primary.Prompt = "Welcome! How may I assist you today?";

    // Retry prompt (if customer not understood)
    input.Primary.RetryPrompt = "I'm sorry, I didn't quite catch that. Could you please repeat?";

    // Confidence and retry settings
    input.Primary.ConfidenceThreshold = 0.75; // 75% confidence required
    input.Primary.MaxRetries = 3;             // Try up to 3 times
    input.Primary.TimeoutSeconds = 5;         // 5 seconds to respond

    // Security: Encrypt sensitive input (PCI compliance)
    input.Primary.EncryptInput = false; // Set true for payment card data

    // ===== FALLBACK: DTMF =====
    input.Fallback.Mode = InputMode.DTMF;

    // Fallback prompt explains keypad options
    input.Fallback.PromptText = @"
        I'm having trouble understanding. Let's try the keypad instead.
        Press 1 for sales
        Press 2 for technical support
        Press 3 for billing
        Press 4 for general inquiries
    ";

    // Fallback retry prompt
    input.Fallback.RetryPrompt = "That wasn't a valid selection. Please press 1, 2, 3, or 4.";

    // DTMF settings
    input.Fallback.MaxDigits = 1;
    input.Fallback.MinDigits = 1;
    input.Fallback.TerminatingDigit = "#";  // Optional: press # to submit
    input.Fallback.TimeoutSeconds = 10;     // 10 seconds to enter digit
    input.Fallback.InterDigitTimeoutSeconds = 3; // 3 seconds between digits
    input.Fallback.MaxRetries = 2;

    // ===== FALLBACK TRIGGERS =====
    // Fallback on timeout, no match, or low confidence
    input.FallbackTriggers = FallbackTrigger.Timeout |
                            FallbackTrigger.NoMatch |
                            FallbackTrigger.LowConfidence;

    // Enable automatic fallback
    input.EnableFallback = true;
})
.OnDigit("1", sales => { /* Sales flow */ })
.OnDigit("2", support => { /* Support flow */ })
.OnDigit("3", billing => { /* Billing flow */ })
.OnDigit("4", general => { /* General flow */ })
.OnTimeout(timeout => timeout.Disconnect())

Multi-Language Sequential Input ​

csharp
.GetCustomerInput(input =>
{
    // Spanish language bot
    input.Primary.LexBot = "CustomerServiceBot_Spanish";
    input.Primary.Locale = "es_US";
    input.Primary.Prompt = "¿Cómo puedo ayudarte hoy?";
    input.Primary.RetryPrompt = "Lo siento, no entendí. ¿Podrías repetir?";

    // Spanish DTMF fallback
    input.Fallback.PromptText = @"
        Usemos el teclado.
        Presiona 1 para ventas
        Presiona 2 para soporte técnico
        Presiona 3 para facturación
    ";
})

Best Practices ​

When to Use Each Approach ​

Simple DTMF Input:

  • Simple menu systems (1-5 options)
  • Sensitive data collection (SSN, account numbers)
  • Environments with poor audio quality
  • When speech recognition is not required

Unified Input Routing:

  • Modern IVR systems with speech and DTMF options
  • User preference flexibility
  • Accessibility requirements
  • Multi-channel support

Sequential Mode:

  • Best user experience (try speech first)
  • Fallback safety net for speech failures
  • Compliance requirements (must support DTMF for accessibility)
  • High-quality speech recognition with DTMF backup

Security Considerations ​

csharp
// For PCI compliance: Encrypt sensitive input
input.Primary.EncryptInput = true;  // Speech
input.Fallback.EncryptInput = true; // DTMF

// Use appropriate timeout for sensitive data
input.Primary.TimeoutSeconds = 30; // Give users time to find their card
input.Fallback.TimeoutSeconds = 30;

Confidence Thresholds ​

csharp
// High confidence for critical operations
input.Primary.ConfidenceThreshold = 0.85; // 85% for payments, transfers

// Medium confidence for routing
input.Primary.ConfidenceThreshold = 0.70; // 70% for menu navigation

// Lower confidence for informational queries
input.Primary.ConfidenceThreshold = 0.60; // 60% for FAQ, hours

Error Handling ​

csharp
.RouteByInput(router => router
    .When("cancel_intent", new[] { "*" }, cancel => cancel
        .PlayPrompt("Cancelling. Goodbye!")
        .Disconnect())

    .When("agent_intent", new[] { "0" }, agent => agent
        .PlayPrompt("Connecting to an agent")
        .TransferToQueue("GeneralSupport"))

    // Always provide an "Otherwise" for unrecognized input
    .Otherwise(other => other
        .PlayPrompt("I'm sorry, I didn't understand that")
        .TransferToQueue("GeneralSupport"))
)

Testing Input Flows ​

csharp
[Test]
public void SequentialInput_WithFallback_ShouldGenerateCorrectFlow()
{
    // Arrange
    var builder = new FlowBuilder();

    // Act
    var flow = builder
        .SetName("TestSequentialInput")
        .GetCustomerInput(input =>
        {
            input.Primary.Mode = InputMode.Speech;
            input.Primary.LexBot = "TestBot";
            input.Fallback.Mode = InputMode.DTMF;
            input.FallbackTriggers = FallbackTrigger.AnyError;
        })
        .OnDigit("1", sales => sales.TransferToQueue("Sales"))
        .Build();

    // Assert
    flow.Should().NotBeNull();
    var inputConfig = /* extract from flow */;
    inputConfig.IsSequential.Should().BeTrue();
}

Preview release - Licensing terms TBD before 1.0