Branching, loops, waits, limits, and calling other flows.
Generated from the node definitions in
src/lib/flow/node/flow/and the English labels (component.flow.node.flow). Every node also has the common settings.
Contents
- If —
flow/if - Loop over items —
flow/loop - Merge —
flow/merge - Return —
flow/return - Run flow —
flow/run - Stop and error —
flow/stop - Switch —
flow/switch - Wait —
flow/wait - Respond —
flow/respond - Repeat —
flow/repeat
If
flow/if · icon git-branch
Takes the true or false path
Fields
| Field | Label | Kind | Default | Shown for |
|---|---|---|---|---|
isIgnoreCase | Ignore case | boolean | false | |
isLoose | Loose types (numbers as numbers, "true" as yes) | boolean | true | |
combine | Combine | select: and (All conditions), or (Any condition) | "and" | |
condition | Conditions | list of { left: text, operator: select, right: text } | [{"left": "", "operator": "equals", "right": ""}] |
Output dots
true— Truefalse— False
Output shape
Later nodes read its item as {{ item… }} (right after it) or {{ nodes.<id>.output… }}.
{
"result": "boolean",
"conditions": [
"unknown"
]
}
Example node
{
"id": "if",
"type": "flow/if",
"name": "If",
"field": {
"combine": "and",
"condition": [
{
"left": "",
"operator": "equals",
"right": ""
}
]
},
"setting": {
"retry": {
"count": 0,
"waitMs": 1000,
"backoff": "fixed"
},
"error": { "mode": "stop", "fallback": null, "break": { "count": 3, "waitMs": 600000 } },
"timeoutMs": 60000,
"disabled": false
},
"pin": null
}
Loop over items
flow/loop · icon repeat
Runs the steps once per item
Fields
| Field | Label | Kind | Default | Shown for |
|---|---|---|---|---|
source | Go through | select: list (A list), items (The items that came in) | "list" | |
list | List | text | source: list | |
batch | Items at a time (a batch) | number | 1 | |
until | Stop when (an expression on what came back) | text | ||
max | Maximum items | number | 100 | |
parallel | Items at once (each in its own lane) | number | 1 | |
failure | When an item fails | select: stop (Stop the run), skip (Skip it, go on with the next) | "stop" |
With parallel above 1 it starts that many items, each in a lane of its own (its own tab); a lane coming back takes the next waiting item, and done goes out once, when the last lane is back. failure: skip: an item that fails ends its lane's turn (logged as "Skipped a failed item") and the lane takes the next one, instead of failing the run. Needs a plan with lanes ().
Output dots
item— Itemdone— Done
Output shape
Later nodes read its item as {{ item… }} (right after it) or {{ nodes.<id>.output… }}.
{
"item": "unknown",
"batch": [
"unknown"
],
"index": "number",
"total": "number",
"items": [
"unknown"
],
"count": "number",
"isStopped": "boolean"
}
Its stack
The loop's items is a stack (take: "item"): on a campaign it shows as its own tab in . Each time it runs it takes the first waiting item of its stack, so reordering, removing or finishing (Done) an item while it runs changes what it takes next. The item it handed out is Now until it comes back, then Done. A different list starts a new lap.
Example node
{
"id": "loop",
"type": "flow/loop",
"name": "Loop over items",
"field": {
"max": 100
},
"setting": {
"retry": {
"count": 0,
"waitMs": 1000,
"backoff": "fixed"
},
"error": { "mode": "stop", "fallback": null, "break": { "count": 3, "waitMs": 600000 } },
"timeoutMs": 60000,
"disabled": false
},
"pin": null
}
Merge
flow/merge · icon merge · runs once with every item
Joins the items of its two inputs, A and B. It waits until both connected inputs have delivered; when nothing else can run it goes with what it has (an input that sent nothing counts as no items).
Input dots
a— Ab— B
Fields
| Field | Label | Kind | Default | Shown for |
|---|---|---|---|---|
mode | Mode | select: append (A then B), position (By position), field (By matching field), choose (One input) | "append" | |
key | Field to match (e.g. id) | text | mode: field | |
join | Keep | select: inner (Matches only), left (Every A item), outer (Everything) | "inner" | mode: field |
prefer | On the same field, keep | select: a (A's value), b (B's value) | "b" | mode: position, field |
pick | Input to keep | select: a, b | "a" | mode: choose |
- append: A's items, then B's.
- position: A's first item with B's first, and so on; the longer side's extra items go on alone.
- field: items whose
key(a dotted path) is equal are joined;leftalso keeps A items without a match,outereverything from both. - choose: one input's items as they are (the other only makes it wait).
Output dots
next— Next
Example node
{
"id": "merge",
"type": "flow/merge",
"name": "Merge",
"field": { "mode": "field", "key": "id", "join": "left", "prefer": "b" },
"setting": { "retry": { "count": 0, "waitMs": 1000, "backoff": "fixed" }, "error": { "mode": "stop", "fallback": null, "break": { "count": 3, "waitMs": 600000 } }, "timeoutMs": 60000, "disabled": false, "item": { "isOnce": false, "isAlways": false, "batch": { "size": 0, "waitMs": 0 } } },
"pin": null
}
Wire the two branches into a and b (edge … -> merge.a, edge … -> merge.b).
Return
flow/return · icon corner-down-left
Ends a callable flow and hands back its output
Fields
| Field | Label | Kind | Default | Shown for |
|---|---|---|---|---|
send | Send back | select: output (This output), first (The first item), all (All items) | "output" | |
output | Output | json | send: output |
Output dots
Output shape
Later nodes read its item as {{ item… }} (right after it) or {{ nodes.<id>.output… }}.
"the item it got (unchanged), or what its code returns"
Example node
{
"id": "return",
"type": "flow/return",
"name": "Return",
"field": {
"output": {}
},
"setting": {
"retry": {
"count": 0,
"waitMs": 1000,
"backoff": "fixed"
},
"error": { "mode": "stop", "fallback": null, "break": { "count": 3, "waitMs": 600000 } },
"timeoutMs": 60000,
"disabled": false
},
"pin": null
}
Run flow
flow/run · icon workflow
Runs another flow, and waits for its result or not
Fields
| Field | Label | Kind | Default | Shown for |
|---|---|---|---|---|
flowId | Flow | flow | ||
mode | Run | select: each (Once per item), once (Once with all items (input.items)) | "each" | |
input | Input | flow.input | ||
isWait | Wait for it to finish | boolean | true | |
cache | Reuse result for (minutes) | number | 0 | isWait: True |
Output dots
next— Nexterror— Error
Output shape
Later nodes read its item as {{ item… }} (right after it) or {{ nodes.<id>.output… }}.
"the item it got (unchanged), or what its code returns"
Default settings
timeout 15 minutes; the rest are the .
Example node
{
"id": "run",
"type": "flow/run",
"name": "Run flow",
"field": {
"input": {},
"cache": 0
},
"setting": {
"retry": {
"count": 0,
"waitMs": 1000,
"backoff": "fixed"
},
"error": { "mode": "stop", "fallback": null, "break": { "count": 3, "waitMs": 600000 } },
"timeoutMs": 900000,
"disabled": false
},
"pin": null
}
Stop and error
flow/stop · icon octagon-x
Ends the run: failed with your message, or succeeded
Fields
| Field | Label | Kind | Default | Shown for |
|---|---|---|---|---|
end | End as | select: failed, succeeded | "failed" | |
message | Error message | text | end: failed | |
type | Error type | text | end: failed |
Output dots
Default settings
on error stop; the rest are the .
Example node
{
"id": "stop",
"type": "flow/stop",
"name": "Stop and error",
"field": {},
"setting": {
"retry": {
"count": 0,
"waitMs": 1000,
"backoff": "fixed"
},
"error": { "mode": "stop", "fallback": null, "break": { "count": 3, "waitMs": 600000 } },
"timeoutMs": 60000,
"disabled": false
},
"pin": null
}
Switch
flow/switch · icon split
Routes to the rule that matches
Fields
| Field | Label | Kind | Default | Shown for |
|---|---|---|---|---|
mode | Mode | select: rules, expression (An expression) | "rules" | |
rule | Rules (one dot each) | list of { name: text, combine: select, condition: list } | [{"name": "a", "combine": "and", "condition": [{"left": "", "operator": "equals" | |
index | Dot by position (0 is the first) | text | mode: expression | |
isAll | Send to every rule that matches | boolean | false | mode: rules |
fallback | When nothing matches | select: dot (The fallback dot), none (Drop the item), first (The first rule's dot) | "dot" | |
isIgnoreCase | Ignore case | boolean | false | mode: rules |
isLoose | Loose types | boolean | true | mode: rules |
Output dots
fallback— Fallback- one dot per
ruleentry
Output shape
Later nodes read its item as {{ item… }} (right after it) or {{ nodes.<id>.output… }}.
"the item it got (unchanged), or what its code returns"
Example node
{
"id": "switch",
"type": "flow/switch",
"name": "Switch",
"field": {},
"setting": {
"retry": {
"count": 0,
"waitMs": 1000,
"backoff": "fixed"
},
"error": { "mode": "stop", "fallback": null, "break": { "count": 3, "waitMs": 600000 } },
"timeoutMs": 60000,
"disabled": false
},
"pin": null
}
Wait
flow/wait · icon timer
Waits a time, until a moment, a random while, a free slot of a limit, a call or a form, then goes on. Pause and cancel interrupt it.
limit waits until a has a free slot: give it the key, max and period of the node limit it guards (limit.key x/post:{{ option.account.x }}, the same as that node's). It only looks; the guarded node takes the slot. Put before a posting round, nothing is read or written for a post that couldn't go: the posting templates start every round with Wait for a post slot.
Fields
| Field | Label | Kind | Default | Shown for |
|---|---|---|---|---|
mode | Mode | select: interval (A fixed time), until (Until a moment), random (A random time (human-like)), limit (Until a limit has a free slot), call (Until it's called ({{ run.resume }})), form (Until a form is answered) | "interval" | |
durationMs | Duration (ms) | number | 1000 | mode: interval |
until | Until (09:00, 2026-11-01 09:00 or ISO) | text | mode: until | |
timezone | Timezone (empty: the flow's) | text | mode: until | |
minMs | Minimum (ms) | number | 20000 | mode: random |
maxMs | Maximum (ms) | number | 60000 | mode: random |
distribution | Distribution | select: uniform (Even), human (Human-like) | "human" | mode: random |
every.min | every.min | number | 1 | mode: random |
every.max | every.max | number | 1 | mode: random |
limit.key | Limit key (the node it guards) | text | mode: limit | |
limit.mode | Limit: set here, or follow the account (Dataset › Account) | select (manual, account) | manual | mode: limit |
limit.window | Limit windows (a list of max and everyMs, every one at once) | json | [{ "max": 1, "everyMs": 86400000 }] | mode: limit, limit.mode: manual |
title | Form title | text | "A few questions" | mode: form |
description | Form description | textarea | mode: form | |
question | Questions | list of { name: text, label: text, kind: select, required: boolean, option: text, isMultiple: boolean, accept: text, placeholder: text, default: text, html: textarea } |
call: the run waits until something calls its resume URL, {{ run.resume }}, with any method. Put that URL in a message or an outside system before the Wait. The call's method, query, header and body come out, and the caller gets 200 { "ok": true }. A link opened in a browser gets a short "Thanks" page. A link with ?answer=approve (or a body { "answer": … }) adds answer: that's how send-and-wait messages carry approve and decline links.
form: the run waits for its public form at /form/wait/<token> (the resume URL's token; the run's log shows the link). The answers come out as { answer: { <name>: … } }.
While it waits, the run is Waiting for you with no buttons: only the outside call answers it, a viewer can't. With limitMs, no answer in time takes the timeout dot.
until: "09:00" is the next 09:00; "2026-11-01 09:00" is read in the timezone; an ISO time keeps its offset; a moment already past goes on at once. every: in random mode it only waits each N-th time it's reached (N rolled again after each wait), the other times it goes straight on: a rest every few replies.
Output dots
next— Nexttimeout— Timed out
Output shape
{
"waitedMs": "number",
"until": "string",
"left": "number",
"method": "string",
"query": "unknown",
"body": "unknown",
"answer": "unknown"
}
until in until mode; left in random mode (how many more times until it waits again, 0 right after a wait); method, query, header, body and answer in call mode; answer in form mode.
Respond
flow/respond · icon reply
Answers the webhook call that started the run, when its is set to Respond: With a Respond node. Only the first Respond of a run counts; a second one logs that it was skipped. Its items go on unchanged (it runs once with all of them).
| Field | Label | Kind | Default | Shown for |
|---|---|---|---|---|
with | Respond with | select: first (The first item), all (All items), json, text, file (A file of the first item), redirect (A redirect), jwt (A JWT), nothing (No body) | "first" | |
json | JSON | json | with: json, jwt | |
text | Text | textarea | with: text | |
file | File key (empty: its first) | text | with: file | |
url | Redirect to | text | with: redirect | |
credential | JWT credential | credential (http/jwt) | with: jwt | |
key | Put the body under (field name) | text | with: first, all, json | |
status | Status code | number | 200 | |
header | Headers (JSON) | json |
Output dots: next.
Default settings
timeout 7 days; the rest are the .
Example node
{
"id": "rest",
"type": "flow/wait",
"name": "Pause like a person",
"field": { "mode": "random", "minMs": 45000, "maxMs": 120000, "distribution": "human" },
"setting": { "retry": { "count": 0, "waitMs": 1000, "backoff": "fixed" }, "error": { "mode": "stop", "fallback": null, "break": { "count": 3, "waitMs": 600000 } }, "timeoutMs": 604800000, "disabled": false, "item": { "isOnce": false, "isAlways": false, "batch": { "size": 0, "waitMs": 0 } } },
"pin": null
}
Repeat
flow/repeat · icon repeat
Repeats a branch: connect its again dot to the steps to repeat, and the last of them back into Repeat. A number of times: that many laps (default 3). While a condition holds: as long as the condition (a template such as {{ item.hasMore }}) is true, up to Laps at most (100). Each lap's item carries index (0, 1, 2…); when it's done, the item goes on through done and the count starts over.
| Field | Label | Kind | Default | Shown for |
|---|---|---|---|---|
mode | Repeat | select: times (A number of times), while (While a condition holds) | "times" | |
times | Times | number | 3 | mode: times |
condition | While (true / false) | text | mode: while | |
max | Laps at most | number | 100 |