How-to guide

Branch, loop and run in parallel

Route with if_else and switch, run branches concurrently, iterate with foreach and repeat_while, and use loopback safely.

Flow Current 3 min read Updated

Prerequisites

  • A basic workflow ([Build a workflow](build-a-workflow.md))

If / else#

An if_else evaluates its branches top to bottom and takes the first match. A branch with "condition": null is the else branch. Each branch declares its own next, so the activity has no next of its own.

json
{
  "id": "a3",
  "type": "if_else",
  "inputs": [ ... ],
  "branches": [
    {
      "branchId": "b1",
      "label": "High value VIP",
      "condition": { "type": "expression", "body": "finalValue > 1000 && isVip === true" },
      "next": "a4"
    },
    { "branchId": "b_else", "label": "Standard", "condition": null, "next": "a5" }
  ]
}

Switch#

switch resolves a variable path and routes to the matching case. Combined with an llm_decision, it is the main pattern for AI-driven routing.

json
{
  "id": "a_sw",
  "type": "switch",
  "switch": {
    "on": "a_llm.output.decision",
    "cases": [
      { "value": "low",  "next": "a_approve" },
      { "value": "high", "next": "a_decline" }
    ],
    "default": "a_review"
  }
}

Parallel branches#

Branches run concurrently. Each branch holds an inline activities array, and outputs are referenced by activity id directly (not namespaced by branch). The activity's own next runs after every branch has finished.

json
{
  "id": "a_checks",
  "type": "parallel",
  "parallel": {
    "branches": [
      { "id": "b_credit", "name": "Credit check", "activities": [ { "id": "a_credit", "type": "js_script", "script": { "body": "output.approved = true;" } } ] },
      { "id": "b_stock",  "name": "Stock check",  "activities": [ { "id": "a_stock",  "type": "js_script", "script": { "body": "output.inStock = true;" } } ] }
    ]
  },
  "outputs": [
    { "name": "creditApproved", "from": "a_credit.output.approved" },
    { "name": "inStock",        "from": "a_stock.output.inStock" }
  ],
  "next": "a_next"
}

A parallel activity needs two or more branches. Use merge when you need to combine results with a strategy (combine, array, coalesce, sum, avg).

Loops#

  • foreach iterates over an array (items is a path such as a_receive_order.output.lineItems), running its body activities for each item with itemVariable bound. Its outputs are itemCount and results.
  • repeat_while repeats its body while a condition holds, up to maxIterations. Its outputs are iterationCount and results.
json
{ "repeat_while": { "condition": "context.get('globals.status') !== 'done'", "maxIterations": 1000, "body": [] } }

Loopback connections#

You can draw a connection back to an earlier activity. The rule is that every cycle must contain at least one if_else or switch, because that is where the exit condition lives. Three layers enforce it:

  1. In Studio, the canvas refuses a back-edge that would create a cycle without a branching node.
  2. At load time, cycle detection rejects an invalid workflow with a validation error such as: "Loopback connections require a branching condition (if_else or switch) in the cycle."
  3. At run time, a visit counter stops any activity that runs more than maxLoopIterations times (default 1000, override per workflow). This guards against an exit condition that never fires.
json
{ "workflowId": "wf_poll", "maxLoopIterations": 10 }

Waiting#

Use delay (seconds or milliseconds) for timed or SLA-driven pauses, and approval or external_task to wait for people or other systems. See Add approvals and external tasks.

Expected result

  • Execution follows the intended branch, branches run concurrently, and loops end