Skip to main content

Purpose

The Condition node evaluates a condition and routes the workflow to one of several branches. Each branch names a target activity to jump to, so a condition can send execution forward to different paths, or back to an earlier node to retry a step. Only one branch is chosen per evaluation. Condition nodes are the decision points in your workflows. They enable dynamic routing based on runtime data, creating adaptive workflows that respond to different scenarios intelligently.

How conditions work

When execution reaches a Condition node:
1

Evaluate the condition

The condition is evaluated against the variable store and produces a result, a boolean, a label, or a raw value depending on the condition type.
2

Match a branch

The result is matched against the node’s branches. Each branch has a label and a goto (the id of the activity to jump to). If nothing matches and a default_branch is set, that branch is used.
3

Jump to the target

The workflow jumps to the matched branch’s goto activity. Only one branch is taken; the others are not run.
4

Continue from the target

Execution continues from the target activity. If the target sits earlier in the workflow, the jump forms a loop (see Loops).
Condition nodes enable dynamic, adaptive workflows. The same workflow definition can handle different scenarios by routing through different branches based on runtime data.

Configuration

A Condition node is configured with a condition_type, the fields that type needs, and a list of branches.

Condition types

There are three condition types, set by condition_type:
Compare a single value from the variable store against a fixed value using an operator. This is the default type.
variable_path is a plain dot-path (not a {{...}} template). See Referencing data.

Branches and goto

Every condition lists one or more branches. Each branch has:
  • label — the value this branch matches.
  • goto — the id of the activity to jump to when this branch is chosen.
  • value (optional) — an explicit value to match instead of the label.
How the result matches a label depends on the type:
  • variable conditions with a boolean result match the true / false labels (also yes / no and 1 / 0). With the match operator, the raw value is compared directly against the labels for multi-way routing.
  • llm conditions match the label the model returned.
  • expression conditions match true / false.

Default branch

default_branch names the label to fall back to when nothing matches. Without a default and with no match, the node fails, so set a default whenever a condition might not match cleanly (missing data, an unexpected LLM label).

Referencing data in conditions

Conditions read from the variable store using dot-paths, not {{...}} templates. For a variable condition, variable_path starts with a scope:
  • Activity outputs are stored flat under the activity’s id. Reference a field as activity_id.field, not activity_id.output.field.
  • Trigger and user input live in the input scope (input.field), execution context in the system scope (system.field).
  • Descend into nested objects with dots, and into list elements by numeric index: plan.steps.0.action.
For an expression condition, names resolve against input and variables, so an activity output is reached as variables.activity_id.field and execution input as input.field. For an llm condition, llm_prompt uses {{path}} templates (for example {{input.message}}), which follow the same scope names.

Operator reference

For condition_type: "variable", operator selects how the resolved variable_path value is compared with compare_value:
There is no regex operator. match routes on the raw value; it does not match a pattern. Equality and numeric operators coerce types where sensible (for example 10 == "10", "5" > 3), but greater_than / less_than on non-numeric values fail the node.

Loops (route back to an earlier node)

A branch whose goto points at an earlier activity forms a loop. Use it to retry a step, refine a search, or keep iterating until a result is good enough, then exit.

Building a retry loop

The pattern is: do work, verify it, then a condition either exits or loops back.
The condition routes false back to the first step and true to the end:
Each pass can carry state forward. In the canonical example the planner reads the verifier’s feedback (verify.gap), which is empty on the first pass and refined on later passes, so each retry searches differently.
See the full runnable example at docs/examples/workflows/07-verify-retry-loop.json in the platform repository: a plan → gather → verify → check loop that retries until the verifier is satisfied.

The bounded-exit branch

Loops are bounded so they can’t run forever. Two fields make the exit explicit and graceful:
  • max_loops — the maximum number of times this condition may take a backward edge. After the budget is spent, the runtime stops looping.
  • loop_exit_branch — the branch label to route through once max_loops is reached. This lets a budget-stopped loop finish through a real branch (typically the one that leads to an END node) with normal output formatting, instead of erroring.
loop_exit_branch must be one of the node’s branch labels, and setting max_loops requires loop_exit_branch. Both are validated at save time and re-checked at runtime.

The safety cap

Even without max_loops, every backward edge is bounded so a stuck loop can’t drain the workflow:
  • A conditional with max_loops exits through its loop_exit_branch (or, if that can’t be resolved, the terminal END node) once the budget is spent.
  • Any other backward edge (a raw next cycle, or a conditional with no budget) is capped at MAX_LOOPBACKS_PER_EDGE (default 25, env-configurable) traversals of the same edge, then routed through END.
  • As a final catch-all, the whole workflow is capped at MAX_WORKFLOW_ITERATIONS (1000) activity steps.
The cap is counted per edge (source activity, goto target). If no END node is reachable when a loop is stopped, the run finishes softly with a success result flagged loop_budget_exceeded.
Loop budgets are counted per edge across the whole run and never reset. Avoid nesting or overlapping loop regions: an inner loop shares the same run-global counters, so its budget can be undercounted.

Examples

Let’s look at practical Condition nodes in real workflows.

Example 1: Confidence-based routing

Scenario: Route documents to auto-processing or human review based on a confidence score.
High-confidence documents route to auto-process; low-confidence documents route to human-review, balancing speed with accuracy.

Example 2: Document type routing

Scenario: Route to different processors based on document type, using match for multi-way routing.
The raw document_type value is matched directly against the branch labels, so each document type gets its own path.

Example 3: Intent routing with an LLM

Scenario: Send a message to the right handler based on what it’s about.

Example 4: Multi-factor escalation

Scenario: Escalate only when several factors line up, using an expression.

Example 5: Retry until satisfied

Scenario: Keep refining until a verifier is happy, then exit. See Loops.

Advanced patterns

If-else-if chains

Chain Condition nodes so a false branch leads to the next condition:

Condition with a fallback

Verify a step succeeded before continuing, and route failures to a human:

Condition with parallel branches

Route into a Parallel node when multi-agent analysis is needed:

Best practices

Complex expressions are hard to debug. Break complex logic into a chain of simple Condition nodes rather than one giant expression.
A missing value can make a comparison fail the node. Use the exists operator (or gate on a value the upstream activity is guaranteed to set) before comparing.
Set default_branch whenever a condition might not match cleanly, especially for llm conditions where the model could return an unexpected label. Without a default, an unmatched result fails the node.
When a branch routes backward, set max_loops and a loop_exit_branch that reaches an END node, so the loop exits gracefully with a real result rather than hitting the global safety cap.
For multi-way (match / llm) routing, name branches after what they mean (passport, complaint) so the workflow reads clearly.
Test with data that triggers each branch, including the default and the loop-exit path. Don’t just test the happy path.
Combine Condition nodes with Human Task nodes for approval workflows. Route high-risk items to human review while auto-processing low-risk items. This balances automation with human oversight.

Next steps

Human task node

Add human approval in conditional branches

Parallel node

Combine conditions with parallel execution

Memory system

Understand the variable store conditions read from

Agent node

Use agent outputs as condition inputs