Logic nodes
Branching and iteration. See Control flow & branching for how branches are walked.
Condition
@wf::logic:condition · ConditionNode
Evaluates field <operator> value and routes to the true or false branch.
Only the matched branch runs.
Config
| Field | Type | Default | Notes |
|---|---|---|---|
field | text | — | Required. The value to test. Usually an expression, e.g. {{ trigger.age }}. |
operator | select | == | See operators below. |
value | text | — | The value to compare against. Not shown for the is_* operators. |
Operators: ==, ===, !=, >, >=, <, <=, contains, not_contains,
starts_with, ends_with, in, not_in, is_null, is_not_null,
is_empty, is_not_empty.
Branches: true, false
Output
{ "actual": "<field>", "operator": "==", "expected": "<value>", "result": true, "branch": "true" }
Switch
@wf::logic:switch · SwitchNode
Compares field against a list of cases and routes to the first matching case's
branch, or default.
Config
| Field | Type | Default | Notes |
|---|---|---|---|
field | text | — | Required. Value to switch on, e.g. {{ trigger.status }}. |
cases | json | — | Required. Array of { "value": ..., "branch": ... } objects. |
strict | checkbox | false | Use === instead of == for matching. |
Branches: default, plus one per case's branch.
Output
{ "field": "pending", "matched": "pending" }
Not available in the interactive
workflow:buildCLI (its per-case branch configuration needs the visual builder). Define it in code.
For each
@wf::logic:loop · LoopNode — label "For each"
Iterates an array. Each visit emits the next item on the body branch; after the
last item it emits on main. Wire the end of the body back to the loop node to
iterate.
Config
| Field | Type | Notes |
|---|---|---|
source | text | Required. A dot path to an array in context, e.g. nodes.list-orders.result. (A path, not a {{ }} expression.) |
Branches: body, main
Output
- On
body:{ "index": 0, "item": <element> }— read as{{ nodes.<id>.item }}. - On
main(after the last item):{ "iterations": <n> }.
The loop cursor is stored in per-execution state, so iteration survives a suspend/resume that happens inside the body.
Example
'nodes' => [
['id' => 'orders', 'type' => ModelListNode::type(), 'config' => ['model' => \App\Models\Order::class]],
['id' => 'loop', 'type' => LoopNode::type(), 'config' => ['source' => 'nodes.orders.result']],
['id' => 'ping', 'type' => HttpRequestNode::type(), 'config' => [
'method' => 'post', 'url' => 'https://hooks.example.com/order',
'body' => ['id' => '{{ nodes.loop.item.id }}'],
]],
['id' => 'done', 'type' => LogNode::type(), 'config' => [
'message' => 'Pinged {{ nodes.loop.iterations }} orders',
]],
],
'edges' => [
['from' => 'trigger', 'to' => 'orders', 'branch' => 'main'],
['from' => 'orders', 'to' => 'loop', 'branch' => 'main'],
['from' => 'loop', 'to' => 'ping', 'branch' => 'body'],
['from' => 'ping', 'to' => 'loop', 'branch' => 'main'],
['from' => 'loop', 'to' => 'done', 'branch' => 'main'],
],