Skip to content

CheckContactAttribute Inline Branching ​

Overview ​

The CheckContactAttribute builder now supports inline branching using lambda expressions, providing a cleaner and more intuitive alternative to label-based flow control.

Why Inline Branching? ​

Traditional Label-Based Approach ​

Previously, you had to use labels to define different execution paths:

csharp
.CheckContactAttribute(check =>
{
    check
        .Attribute(Attributes.External("Authenticated"))
        .Equals(true, "AuthenticatedMenu")
        .Otherwise("AuthenticationFailed");
})
// Code continues linearly - you need to remember the labels
.PlayPrompt("Welcome! Authenticated.", "AuthenticatedMenu")
.PlayPrompt("Your balance is $100.")
.TransferToQueue("Support")
.Disconnect()
.PlayPrompt("Access denied.", "AuthenticationFailed")
.Disconnect()

Problems:

  • ❌ Labels are scattered throughout the flow
  • ❌ Hard to understand the flow structure at a glance
  • ❌ Easy to make mistakes with label names
  • ❌ Difficult to maintain as flows grow

New Inline Branching Approach ​

With inline branching, the flow structure matches your mental model:

csharp
.CheckContactAttribute(check =>
{
    check
        .Attribute(Attributes.External("Authenticated"))
        .Equals(true, authenticatedBranch =>
        {
            // This entire block executes ONLY when Authenticated == true
            authenticatedBranch
                .PlayPrompt("Welcome! Authenticated.")
                .PlayPrompt("Your balance is $100.")
                .TransferToQueue("Support")
                .Disconnect();
        })
        .Otherwise(notAuthenticatedBranch =>
        {
            // This block executes when authentication fails
            notAuthenticatedBranch
                .PlayPrompt("Access denied.")
                .Disconnect();
        });
})

Benefits:

  • ✅ Clear visual structure - nested code shows nested logic
  • ✅ Self-documenting - no need to track labels
  • ✅ Type-safe - compiler catches errors
  • ✅ Easier to refactor and maintain

Supported Overloads ​

All comparison methods now support inline branching:

Boolean Comparisons ​

csharp
.CheckContactAttribute(check =>
{
    check
        .Attribute(Attributes.External("IsVIP"))
        .Equals(true, vipPath =>
        {
            vipPath
                .PlayPrompt("Welcome, VIP!")
                .TransferToQueue("VIP-Support")
                .Disconnect();
        })
        .Otherwise(standardPath =>
        {
            standardPath
                .TransferToQueue("General-Support")
                .Disconnect();
        });
})

String Comparisons ​

csharp
.CheckContactAttribute(check =>
{
    check
        .Attribute(Attributes.External("Department"))
        .Equals("Sales", salesPath =>
        {
            salesPath.TransferToQueue("Sales").Disconnect();
        })
        .Equals("Support", supportPath =>
        {
            supportPath.TransferToQueue("Support").Disconnect();
        })
        .Otherwise(defaultPath =>
        {
            defaultPath.TransferToQueue("General").Disconnect();
        });
})

Numeric Comparisons ​

csharp
.CheckContactAttribute(check =>
{
    check
        .Attribute(Attributes.User("RetryCount"))
        .Equals(0, firstAttempt =>
        {
            firstAttempt.PlayPrompt("First try.").Disconnect();
        })
        .Equals(1, secondAttempt =>
        {
            secondAttempt.PlayPrompt("Second try.").Disconnect();
        })
        .Otherwise(tooMany =>
        {
            tooMany.PlayPrompt("Too many attempts.").Disconnect();
        });
})

Advanced Examples ​

Example 1: Multi-Tier Customer Service ​

csharp
var flow = new FlowBuilder()
    .SetName("Tiered Support")
    .SetType(FlowType.ContactFlow)

    .CheckContactAttribute(check =>
    {
        check
            .Attribute(Attributes.External("CustomerTier"))
            .Equals("VIP", vip =>
            {
                vip
                    .PlayPrompt("Welcome, VIP customer!")
                    .SetContactAttributes(attrs =>
                    {
                        attrs["Priority"] = "High";
                        attrs["MaxWaitTime"] = "30";
                    })
                    .TransferToQueue("VIP-Support")
                    .Disconnect();
            })
            .Equals("Premium", premium =>
            {
                premium
                    .PlayPrompt("Hello, Premium customer!")
                    .SetContactAttributes(attrs =>
                    {
                        attrs["Priority"] = "Medium";
                        attrs["MaxWaitTime"] = "120";
                    })
                    .TransferToQueue("Premium-Support")
                    .Disconnect();
            })
            .Otherwise(standard =>
            {
                standard
                    .PlayPrompt("Thank you for calling.")
                    .TransferToQueue("General-Support")
                    .Disconnect();
            });
    })
    .Build();

Example 2: Nested Branching with Input ​

csharp
var flow = new FlowBuilder()
    .SetName("Authenticated Menu")
    .SetType(FlowType.ContactFlow)

    // Invoke Lambda to check authentication
    .InvokeLambda("CheckAuthFunction", lambda =>
    {
        lambda.InputParameters["UserId"] = "$.Attributes.UserId";
    })

    // Branch based on authentication result
    .OnSuccess(lambdaSuccess =>
    {
        lambdaSuccess
            .CheckContactAttribute(check =>
            {
                check
                    .Attribute(Attributes.External("Authenticated"))
                    .Equals(true, authenticated =>
                    {
                        // Show authenticated menu
                        authenticated
                            .PlayPrompt("Welcome, $.External.CustomerName!")
                            .GetCustomerInput("Press 1 for orders, 2 for billing.", input =>
                            {
                                input.TimeoutSeconds = 5;
                            })
                            .OnDigit("1", orders =>
                            {
                                orders
                                    .PlayPrompt("Checking your orders...")
                                    .InvokeLambda("GetOrdersFunction")
                                    .OnSuccess(ordersSuccess =>
                                    {
                                        ordersSuccess
                                            .PlayPrompt("You have $.External.OrderCount orders.")
                                            .TransferToQueue("Orders")
                                            .Disconnect();
                                    })
                                    .OnError(ordersError =>
                                    {
                                        ordersError
                                            .PlayPrompt("Unable to retrieve orders.")
                                            .Disconnect();
                                    });
                            })
                            .OnDigit("2", billing =>
                            {
                                billing
                                    .PlayPrompt("Your balance is $.External.Balance")
                                    .TransferToQueue("Billing")
                                    .Disconnect();
                            })
                            .OnDefault(invalid => invalid.Disconnect())
                            .OnTimeout(timeout => timeout.Disconnect())
                            .OnError(error => error.Disconnect());
                    })
                    .Otherwise(notAuthenticated =>
                    {
                        // Authentication failed
                        notAuthenticated
                            .PlayPrompt("Authentication failed.")
                            .PlayPrompt("Please contact customer support.")
                            .TransferToQueue("Support")
                            .Disconnect();
                    });
            });
    })
    .OnError(lambdaError =>
    {
        lambdaError
            .PlayPrompt("System error. Please try again.")
            .Disconnect();
    })
    .Build();

Example 3: Retry Logic with Counter ​

csharp
var flow = new FlowBuilder()
    .SetName("PIN Verification with Retries")
    .SetType(FlowType.ContactFlow)

    .StoreCustomerInput("Enter your PIN:", input =>
    {
        input.MaxDigits = 4;
    })
    .OnSuccess(pinEntered =>
    {
        pinEntered
            .InvokeLambda("VerifyPIN", lambda =>
            {
                lambda.InputParameters["PIN"] = "$.StoredCustomerInput";
            })
            .OnSuccess(verified =>
            {
                verified
                    .CheckContactAttribute(check =>
                    {
                        check
                            .Attribute(Attributes.External("PINValid"))
                            .Equals(true, validPin =>
                            {
                                validPin
                                    .PlayPrompt("PIN accepted. Welcome!")
                                    .TransferToQueue("Authenticated")
                                    .Disconnect();
                            })
                            .Otherwise(invalidPin =>
                            {
                                // Check retry count
                                invalidPin
                                    .CheckContactAttribute(retryCheck =>
                                    {
                                        retryCheck
                                            .Attribute(Attributes.User("RetryCount"))
                                            .Equals(0, firstFail =>
                                            {
                                                firstFail
                                                    .PlayPrompt("Incorrect PIN. Please try again.")
                                                    .SetContactAttributes(attrs =>
                                                    {
                                                        attrs["RetryCount"] = "1";
                                                    })
                                                    .Disconnect(); // Loop back
                                            })
                                            .Equals(1, secondFail =>
                                            {
                                                secondFail
                                                    .PlayPrompt("Incorrect PIN. One more try.")
                                                    .SetContactAttributes(attrs =>
                                                    {
                                                        attrs["RetryCount"] = "2";
                                                    })
                                                    .Disconnect();
                                            })
                                            .Otherwise(tooManyFails =>
                                            {
                                                tooManyFails
                                                    .PlayPrompt("Too many failed attempts.")
                                                    .PlayPrompt("Your account has been locked.")
                                                    .Disconnect();
                                            });
                                    });
                            });
                    });
            });
    })
    .Build();

Backward Compatibility ​

The original label-based approach is still fully supported. You can mix and match:

csharp
.CheckContactAttribute(check =>
{
    check
        .Attribute(Attributes.External("Status"))
        .Equals("Active", activeFlow =>
        {
            // Use inline branching for active status
            activeFlow
                .PlayPrompt("Account is active")
                .TransferToQueue("Active")
                .Disconnect();
        })
        .Equals("Inactive", "InactiveLabel")  // Still use labels if you prefer
        .Otherwise("DefaultLabel");
})
.PlayPrompt("Account inactive", "InactiveLabel")
.Disconnect()
.PlayPrompt("Unknown status", "DefaultLabel")
.Disconnect()

When to Use Each Approach ​

Use Inline Branching When: ​

  • ✅ You want clear, readable code
  • ✅ Each branch has distinct logic
  • ✅ You're building new flows
  • ✅ You want to avoid label management

Use Labels When: ​

  • ✅ Multiple conditions need to jump to the same destination
  • ✅ You need to jump to a label defined elsewhere in the flow
  • ✅ You're maintaining existing flows with labels
  • ✅ You need to create reusable flow sections

Implementation Details ​

How It Works ​

When you use inline branching:

  1. The framework creates a new FlowBuilder instance for each nested branch
  2. The nested builder shares the parent's action counter (for unique IDs)
  3. The nested builder has access to the parent's join point scope
  4. Each branch is stored in the BranchAction.Branches dictionary
  5. During flow generation, nested flows are flattened into the main flow

Performance Considerations ​

  • No runtime performance difference between inline and label-based approaches
  • Both compile to the same Amazon Connect flow JSON
  • Inline branching may generate slightly larger code (more lambdas), but this is negligible

Migration Guide ​

Before (Label-Based) ​

csharp
.CheckContactAttribute(check =>
{
    check
        .Attribute(Attributes.External("Type"))
        .Equals("A", "TypeA")
        .Equals("B", "TypeB")
        .Otherwise("TypeDefault");
})
.PlayPrompt("Type A flow", "TypeA")
.Disconnect()
.PlayPrompt("Type B flow", "TypeB")
.Disconnect()
.PlayPrompt("Default flow", "TypeDefault")
.Disconnect()

After (Inline Branching) ​

csharp
.CheckContactAttribute(check =>
{
    check
        .Attribute(Attributes.External("Type"))
        .Equals("A", typeA =>
        {
            typeA.PlayPrompt("Type A flow").Disconnect();
        })
        .Equals("B", typeB =>
        {
            typeB.PlayPrompt("Type B flow").Disconnect();
        })
        .Otherwise(typeDefault =>
        {
            typeDefault.PlayPrompt("Default flow").Disconnect();
        });
})

Best Practices ​

  1. Keep branches focused: Each branch should have a clear purpose
  2. Avoid deep nesting: If you have >3 levels of nesting, consider extracting to separate flows
  3. Use meaningful parameter names: vipBranch, authenticatedPath, etc.
  4. Be consistent: Pick one style (inline or labels) per flow for clarity
  5. Comment complex branching: Explain the business logic, not the syntax

See Also ​

Preview release - Licensing terms TBD before 1.0