← Back to Guides

Workflow Patterns and Control Flow

IntermediateAzure Logic Apps2026-03-14

Control Flow Overview

Azure Logic Apps provides rich control flow constructs for building complex workflows. Understanding these patterns is essential for production-grade integrations.

Microsoft Reference: Control flow actions in Logic Apps

Condition (If/Else)

Branch your workflow based on a condition:

{
  "Condition": {
    "type": "If",
    "expression": {
      "and": [
        {
          "greater": [
            "@body('Get_order')?['total']",
            1000
          ]
        }
      ]
    },
    "actions": {
      "Require_Approval": { }
    },
    "else": {
      "actions": {
        "Auto_Approve": { }
      }
    }
  }
}

Compound Conditions

{
  "expression": {
    "or": [
      {
        "and": [
          { "equals": ["@triggerBody()?['priority']", "high"] },
          { "greater": ["@triggerBody()?['amount']", 5000] }
        ]
      },
      {
        "equals": ["@triggerBody()?['escalated']", true]
      }
    ]
  }
}

Microsoft Reference: Conditional statements

Switch (Multi-Path Branching)

Route workflow execution based on a value:

{
  "Switch": {
    "type": "Switch",
    "expression": "@triggerBody()?['messageType']",
    "cases": {
      "Order": {
        "case": "order",
        "actions": {
          "Process_Order": { }
        }
      },
      "Invoice": {
        "case": "invoice",
        "actions": {
          "Process_Invoice": { }
        }
      },
      "Return": {
        "case": "return",
        "actions": {
          "Process_Return": { }
        }
      }
    },
    "default": {
      "actions": {
        "Send_to_Dead_Letter": { }
      }
    }
  }
}

Microsoft Reference: Switch statements

For Each Loop

Iterate over an array of items:

{
  "For_each_order": {
    "type": "Foreach",
    "foreach": "@body('Get_orders')?['value']",
    "actions": {
      "Process_Order": {
        "type": "Http",
        "inputs": {
          "method": "POST",
          "uri": "https://api.example.com/process",
          "body": "@items('For_each_order')"
        }
      }
    },
    "runtimeConfiguration": {
      "concurrency": {
        "repetitions": 20
      }
    }
  }
}

Concurrency Control

Setting Value Behaviour
repetitions 1 Sequential processing (ordered)
repetitions 20 Up to 20 iterations in parallel (default)
repetitions 50 Maximum parallelism (Consumption)

Accessing Loop Context

// Current item
@items('For_each_order')

// Current item property
@items('For_each_order')?['orderId']

// Current iteration index (Standard only)
@iterationIndexes('For_each_order')

Nested For Each

For_each_customer
  → Get_orders for this customer
  → For_each_order
      → Get_order_lines for this order
      → For_each_line
          → Process line item

Caution: Nested For Each loops can generate a large number of actions. Monitor execution counts and consider batching or chunking large datasets.

Microsoft Reference: For Each loops

Until Loop

Repeat actions until a condition is met:

{
  "Wait_for_approval": {
    "type": "Until",
    "expression": "@or(equals(body('Check_status')?['status'], 'approved'), equals(body('Check_status')?['status'], 'rejected'))",
    "limit": {
      "count": 60,
      "timeout": "PT1H"
    },
    "actions": {
      "Check_status": {
        "type": "Http",
        "inputs": {
          "method": "GET",
          "uri": "https://api.example.com/approval/@{triggerBody()?['requestId']}"
        }
      },
      "Delay": {
        "type": "Wait",
        "inputs": {
          "interval": {
            "count": 1,
            "unit": "Minute"
          }
        }
      }
    }
  }
}

Until Loop Limits

Limit Consumption Standard
Maximum count 5,000 5,000
Maximum timeout PT1H (1 hour) Configurable
Default count 60 60
Default timeout PT1H PT1H

Microsoft Reference: Until loops

Parallel Branches

Execute multiple paths simultaneously:

Trigger
  ├── Branch 1: Call CRM API → Update customer record
  ├── Branch 2: Call ERP API → Create invoice
  └── Branch 3: Send notification → Teams/Email
  → Join: All branches complete
  → Next action

Workflow Definition

{
  "Parallel_Processing": {
    "type": "Scope",
    "actions": {
      "Update_CRM": {
        "type": "Http",
        "inputs": { "method": "POST", "uri": "https://crm.example.com/update" }
      },
      "Create_Invoice": {
        "type": "Http",
        "inputs": { "method": "POST", "uri": "https://erp.example.com/invoice" }
      },
      "Send_Notification": {
        "type": "ApiConnection",
        "inputs": { }
      }
    }
  }
}

Actions within a scope that have no runAfter dependencies execute in parallel automatically.

Delay and Schedule Patterns

Fixed Delay

{
  "Delay_5_minutes": {
    "type": "Wait",
    "inputs": {
      "interval": {
        "count": 5,
        "unit": "Minute"
      }
    }
  }
}

Delay Until a Specific Time

{
  "Delay_until_9am": {
    "type": "Wait",
    "inputs": {
      "until": {
        "timestamp": "@formatDateTime(addDays(startOfDay(utcNow()), 1), 'yyyy-MM-ddT09:00:00Z')"
      }
    }
  }
}

Sliding Window Trigger

Process data in non-overlapping time windows (catches up on missed windows):

{
  "triggers": {
    "Sliding_Window": {
      "type": "SlidingWindow",
      "recurrence": {
        "frequency": "Hour",
        "interval": 1
      }
    }
  }
}

Batching

Batch Sender

Send individual messages to a batch:

{
  "Send_to_batch": {
    "type": "SendToBatch",
    "inputs": {
      "batchName": "OrderBatch",
      "content": "@triggerBody()",
      "host": {
        "triggerName": "Batch_trigger",
        "workflow": {
          "id": "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Logic/workflows/batch-receiver"
        }
      }
    }
  }
}

Batch Receiver

Process accumulated messages when a condition is met:

{
  "triggers": {
    "Batch_trigger": {
      "type": "Batch",
      "inputs": {
        "mode": "Inline",
        "batchGroupName": "OrderBatch",
        "releaseCriteria": {
          "messageCount": 100,
          "recurrence": {
            "frequency": "Minute",
            "interval": 5
          }
        }
      }
    }
  }
}

Release criteria options:

  • Message count — Release when N messages accumulate
  • Schedule — Release at regular intervals
  • Both — Release when either condition is met (whichever comes first)

Microsoft Reference: Batch processing in Logic Apps

Concurrency and Throttling

Trigger Concurrency

Control how many workflow instances can run simultaneously:

{
  "triggers": {
    "manual": {
      "type": "Request",
      "runtimeConfiguration": {
        "concurrency": {
          "runs": 10
        }
      }
    }
  }
}
Setting Consumption Standard
Maximum concurrent runs 50 100
Default concurrent runs Unlimited* 100
Queue depth 100,000 100,000

*Unlimited means triggers fire without waiting for previous runs to complete.

SplitOn (Debatching)

Automatically split an array trigger payload into individual workflow runs:

{
  "triggers": {
    "manual": {
      "type": "Request",
      "splitOn": "@triggerBody()?['orders']",
      "runtimeConfiguration": {
        "concurrency": {
          "runs": 25
        }
      }
    }
  }
}

Each item in the orders array triggers a separate workflow run.

Microsoft Reference: Concurrency, debatching, and loops

Workflow Chaining

Child Workflows

Call one Logic App from another:

{
  "Call_child_workflow": {
    "type": "Workflow",
    "inputs": {
      "host": {
        "triggerName": "manual",
        "workflow": {
          "id": "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Logic/workflows/child-workflow"
        }
      },
      "body": {
        "orderId": "@triggerBody()?['orderId']",
        "customerId": "@triggerBody()?['customerId']"
      }
    }
  }
}

When to Use Child Workflows

  • Reusable logic — Share common processing across multiple parent workflows
  • Reduce complexity — Break large workflows into manageable pieces
  • Independent scaling — Different concurrency settings for parent and child
  • Separate error handling — Isolate failure domains
  • Team ownership — Different teams manage different workflow components

Long-Running Workflows

Asynchronous Patterns (202 + Polling)

For workflows that take minutes or hours:

1. Client sends request
2. Logic App returns 202 Accepted with Location header
3. Client polls the Location URL
4. Logic App returns 200 OK when complete

// Enable in HTTP trigger
{
  "triggers": {
    "manual": {
      "type": "Request",
      "operationOptions": "DisableAsyncPattern"  // Return final response synchronously (for short workflows)
    }
  }
}

Webhook Action Pattern

For human approvals or external callbacks:

{
  "Wait_for_callback": {
    "type": "HttpWebhook",
    "inputs": {
      "subscribe": {
        "method": "POST",
        "uri": "https://approval.example.com/register",
        "body": {
          "callbackUrl": "@listCallbackUrl()",
          "orderId": "@triggerBody()?['orderId']"
        }
      },
      "unsubscribe": {
        "method": "POST",
        "uri": "https://approval.example.com/unregister"
      }
    }
  }
}

Microsoft Reference: Handle long-running tasks

Trigger Conditions

Add expressions that must evaluate to true for the trigger to fire:

{
  "triggers": {
    "When_a_message_is_received": {
      "type": "ApiConnection",
      "conditions": [
        {
          "expression": "@greater(triggerBody()?['Properties']?['Priority'], 5)"
        },
        {
          "expression": "@not(equals(triggerBody()?['ContentType'], 'test'))"
        }
      ]
    }
  }
}

This reduces unnecessary workflow runs and saves costs.

Best Practices

  1. Set concurrency limits on triggers to prevent overwhelming downstream systems
  2. Use sequential For Each (concurrency: 1) when order matters or for rate-limited APIs
  3. Implement timeouts on Until loops to prevent infinite loops
  4. Use SplitOn for array payloads to process items independently
  5. Break large workflows into child workflows for maintainability
  6. Use Scope actions to group related actions and simplify error handling
  7. Add trigger conditions to filter unwanted executions early
  8. Monitor loop counts — nested loops can generate thousands of actions per run
  9. Use batching for high-volume, low-latency scenarios
  10. Implement idempotency — design actions to be safely retried

Official Microsoft Resources