Skip to content

Loop

Repeat a section of the flow a specified number of times.

Signatures

csharp
// Static loop count
ILoopBuilder Loop(
    int loopCount,
    Action<ILoopBuilder> configure,
    string? identifier = null)

// Dynamic loop count from attribute
ILoopBuilder Loop(
    AttributeReference loopCountAttribute,
    Action<ILoopBuilder> configure,
    string? identifier = null)

Parameters

ParameterTypeRequiredDescription
loopCountintYesNumber of iterations
loopCountAttributeAttributeReferenceYesAttribute containing loop count
configureAction<ILoopBuilder>YesConfigure loop behavior
identifierstring?NoOptional identifier (use with ContinueAt)

ILoopBuilder Methods

MethodDescription
.WhileLooping(Action<IFlowBuilder>)Configure inline actions for each iteration
.WhileLooping(string targetLabel)Jump to a labeled action on each iteration
.WhenDone(Action<IFlowBuilder>)Configure inline actions after loop completes
.WhenDone(string targetLabel)Jump to a labeled action after loop completes
.LoopIdentifierIdentifier for ContinueAt to restart loop

Examples

Basic Retry Pattern

csharp
Flow.Create("PIN Entry with Retry")
    .Loop(3, loop => loop
        .WhileLooping(attempt => attempt
            .GetCustomerInput("Enter your PIN.", input => input.MaxDigits = 4)
                .OnDigits(d => d
                    .InvokeLambda("ValidatePIN")
                        .OnSuccess(valid => valid
                            .PlayPrompt("PIN accepted.")
                            .TransferToQueue("Authenticated")
                            .Disconnect())
                        .OnError(invalid => invalid
                            .PlayPrompt("Invalid PIN. Please try again.")))
                .OnTimeout(t => t.PlayPrompt("No input received."))
            .ThenContinue())  // Continue to next iteration
        .WhenDone(maxAttempts => maxAttempts
            .PlayPrompt("Maximum attempts reached. Goodbye.")
            .Disconnect()));
csharp
Flow.Create("Main Menu")
    .PlayPrompt("Welcome to Nick Software.", "MainMenu")

    .Loop(3, mainMenuLoop => mainMenuLoop
        .WhileLooping(menuAttempt => menuAttempt
            .GetCustomerInput("Press 1 for Sales, 2 for Support.")
                .OnDigit("1", sales => sales
                    .TransferToQueue("Sales")
                    .Disconnect())
                .OnDigit("2", support => support
                    .TransferToQueue("Support")
                    .Disconnect())
                .OnDefault(def => def
                    .PlayPrompt("Invalid selection."))
                .OnTimeout(t => t
                    .PlayPrompt("We didn't receive your selection."))
            .ThenContinue())
        .WhenDone(maxMenuAttempts => maxMenuAttempts
            .PlayPrompt("Goodbye.")
            .Disconnect()))

    .Disconnect();

Account Lookup with Retry

csharp
Flow.Create("Account Entry")
    .Loop(3, accountLoop => accountLoop
        .WhileLooping(attempt => attempt
            .StoreCustomerInput("Enter your account number.", input =>
            {
                input.MaxDigits = 10;
                input.CustomTerminatingKeypress = "#";
            })
            .OnSuccess(entered => entered
                .SetContactAttributes(attrs =>
                {
                    attrs["AccountNumber"] = Attributes.System(SystemAttributes.StoredCustomerInput);
                })
                .InvokeLambda("LookupAccount")
                    .OnSuccess(found => found
                        .PlayPrompt("Account found.")
                        .TransferToQueue("AccountServices")
                        .Disconnect())
                    .OnError(notFound => notFound
                        .PlayPrompt("Account not found. Please try again.")))
            .OnError(inputError => inputError
                .PlayPrompt("Invalid entry."))
            .ThenContinue())
        .WhenDone(maxAttempts => maxAttempts
            .PlayPrompt("Maximum attempts. Transferring to support.")
            .TransferToQueue("Support")
            .Disconnect()));

Nested Loops (Authentication Flow)

csharp
Flow.Create("Full Authentication")
    // Outer loop: Account entry
    .Loop(3, accountLoop => accountLoop
        .WhileLooping(accountAttempt => accountAttempt
            .StoreCustomerInput("Enter your account number.")
                .OnSuccess(accountEntered => accountEntered
                    .InvokeLambda("LookupAccount")
                        .OnSuccess(accountFound => accountFound
                            // Inner loop: PIN entry
                            .Loop(3, pinLoop => pinLoop
                                .WhileLooping(pinAttempt => pinAttempt
                                    .StoreCustomerInput("Enter your PIN.", p => p.MaxDigits = 4)
                                        .OnSuccess(pinEntered => pinEntered
                                            .InvokeLambda("ValidatePIN")
                                                .OnSuccess(valid => valid
                                                    .PlayPrompt("Authenticated!")
                                                    .TransferToQueue("Authenticated")
                                                    .Disconnect())
                                                .OnError(invalid => invalid
                                                    .PlayPrompt("Invalid PIN.")))
                                        .OnError(e => e.PlayPrompt("Invalid entry."))
                                    .ThenContinue())
                                .WhenDone(pinMax => pinMax
                                    .PlayPrompt("Maximum PIN attempts."))))
                        .OnError(notFound => notFound
                            .PlayPrompt("Account not found.")))
                .OnError(e => e.PlayPrompt("Invalid entry."))
            .ThenContinue())
        .WhenDone(accountMax => accountMax
            .PlayPrompt("Maximum account attempts. Goodbye.")
            .Disconnect()));

Dynamic Loop Count

csharp
Flow.Create("Dynamic Retry")
    // Get retry count from Lambda
    .InvokeLambda("GetConfiguration")
        .OnSuccess(config => config
            .SetContactAttributes(attrs =>
            {
                attrs["MaxRetries"] = Attributes.External("MaxRetries");
            }))
        .OnError(e => e
            .SetContactAttributes(attrs =>
            {
                attrs["MaxRetries"] = "3";  // Default
            }))

    // Use dynamic loop count
    .Loop(Attributes.Contact("MaxRetries"), loop => loop
        .WhileLooping(attempt => attempt
            .GetCustomerInput("Enter your selection.")
                .OnDigits(d => d.TransferToQueue("Support").Disconnect())
                .OnDefault(d => d.PlayPrompt("Invalid selection."))
            .ThenContinue())
        .WhenDone(done => done.Disconnect()));

Label-Based Targeting (Flat Flow Structure)

Use label-based targeting when you prefer a flat flow structure or need to reference actions defined elsewhere:

csharp
Flow.Create("Menu with Retry")
    .PlayPrompt("Welcome to support.")

    // Loop that targets labeled actions
    .Loop(3, loop => loop
        .WhileLooping("main-menu")      // Target: labeled GetCustomerInput
        .WhenDone("max-attempts"))      // Target: labeled goodbye prompt

    // Main menu input (target of WhileLooping)
    .GetCustomerInput("Press 1 for Sales, 2 for Support.", "main-menu")
        .OnDigit("1", sales => sales
            .TransferToQueue("Sales")
            .Disconnect())
        .OnDigit("2", support => support
            .TransferToQueue("Support")
            .Disconnect())
        .OnDefault(invalid => invalid
            .PlayPrompt("Invalid selection. Please try again."))
        .OnTimeout(timeout => timeout
            .PlayPrompt("No input received."))

    // Max attempts handler (target of WhenDone)
    .JoinPoint("max-attempts")
    .PlayPrompt("Maximum attempts reached. Goodbye.")
    .Disconnect();

This is equivalent to the inline version but allows more flexibility in flow organization.

Early Exit with ContinueAt

csharp
Flow.Create("Loop with Early Exit")
    .Loop(5, "RetryLoop", loop => loop
        .WhileLooping(attempt => attempt
            .InvokeLambda("UnreliableService")
                .OnSuccess(success => success
                    .PlayPrompt("Service responded!")
                    .ContinueAt("AfterLoop"))  // Exit loop early
                .OnError(error => error
                    .PlayPrompt("Retrying..."))
            .ThenContinue())
        .WhenDone(done => done
            .PlayPrompt("Service unavailable after all retries.")))

    .JoinPoint("AfterLoop")
    .TransferToQueue("Support")
    .Disconnect();

Lambda Verification with Retry

csharp
private static void BuildAccountLookup(IFlowBuilder flow, string lambdaArn)
{
    flow.SetContactAttributes(attrs =>
        {
            attrs["EnteredAccountNumber"] = Attributes.System(SystemAttributes.StoredCustomerInput);
        })
        .InvokeLambda(lambdaArn, lambda =>
        {
            lambda.InputParameters["Action"] = "LOOKUP";
            lambda.InputParameters["AccountNumber"] = Attributes.Contact("EnteredAccountNumber");
        })
        .OnSuccess(success => success
            .CheckContactAttribute(check =>
            {
                check.Attribute(Attributes.External("StatusCode"))
                    .Equals("200", found => found
                        .SetContactAttributes(attrs =>
                        {
                            attrs["CustomerName"] = Attributes.External("CustomerName");
                        })
                        .PlayPrompt($"Welcome, {Attributes.External("CustomerName")}!")
                        .TransferToQueue("AccountServices")
                        .Disconnect())
                    .Otherwise(notFound => notFound
                        .PlayPrompt("Account not found."));
            }))
        .OnError(lambdaError => lambdaError
            .PlayPrompt("Service error. Please try again."))
        .ThenContinue();
}

// Usage
Flow.Create("Account Verification")
    .Loop(3, loop => loop
        .WhileLooping(attempt =>
            BuildAccountLookup(attempt, "arn:aws:lambda:..."))
        .WhenDone(maxAttempts => maxAttempts
            .PlayPrompt("Maximum attempts reached.")
            .TransferToQueue("Support")))
    .Disconnect();

How Loop Works

  1. WhileLooping - Executes on each iteration
  2. ThenContinue - Signals to continue to next iteration
  3. WhenDone - Executes after all iterations complete (or no ThenContinue)
  4. Exit early - Use Disconnect, TransferToQueue, or ContinueAt to leave loop

AWS Connect Block Type

This block generates the Loop action type in the exported flow JSON with ContinueLooping and DoneLooping transitions.

Best Practices

  1. Use ThenContinue - Required to continue iterations
  2. Provide exit paths - Success should exit the loop
  3. Limit iterations - 3-5 attempts is typical
  4. Clear messaging - Tell customers when retrying
  5. Handle WhenDone - Always have a final fallback

See Also

Preview release - Licensing terms TBD before 1.0