Message flows run on WhatsApp in this release. Other channels are on the roadmap; a flow bound to any other channel is refused at publish.
Decide steps are the logic between the messages: variables, conditions, switches, splits, loops and jumps. They run instantly and the customer sees nothing.
This is the reference for the Decide group of the flow builder's palette. Every step below is listed with what it does, the settings it takes, the branches it can leave by and the one thing to keep in mind. Settings marked "templated" accept {{vars.x}}, {{contact.name}} and {{last.text}}.
Set variable
Assign one or more variables.
| Setting | What it does |
|---|---|
assignments |
[{name, value}] — value is templated and keeps its type when it is exactly one {{…}}. Arithmetic: {{vars.qty * vars.price}}, round/min/max/number. Dates: now(), today(), date_add(date, n, unit), date_sub(date, n, unit), date_diff(a, b, unit) (a minus b, in seconds unless a unit is named), date_format(date, pattern), date_part(date, part) where part is year, month, day, weekday (1 Monday – 7 Sunday), hour, minute, second, week, quarter or timestamp; and the literal N unit — {{vars.created_at + 7 days}}, {{today() + 1 month}}, {{now() - 90 minutes}}, {{vars.due - now()}} for the seconds between two dates. |
Branches: next
Keep in mind: A value that is exactly one {{…}} keeps its type, so a list stays a list.
Decision
Branch on a condition.
| Setting | What it does |
|---|---|
condition |
{var, op, value} | {all: [...]} | {any: [...]}; var is a path such as vars.choice, or a whole expression ({{vars.due - now()}} gt 0). Operators: eq / neq — case-insensitive text, numeric for numbers, truthy for booleans; contains / not_contains / starts_with / ends_with — case-insensitive text; on a LIST var, contains / not_contains / in test membership; gt / gte / lt / lte — numbers (thousands separators such as 1,000 accepted), else two dates as dates, else false with the reason in vars.__compare; empty / not_empty — null, "" and [] are empty; in — value is a list or a comma-separated string, matched case-insensitively; matches — a regular expression, case-insensitive; one that does not compile is false with vars.__regex_error set. |
Branches: true false
Keep in mind: Text compares case-insensitively; a comparison that cannot be made is false, with the reason in vars.__compare.
Switch
Many cases on one value, plus a default.
| Setting | What it does |
|---|---|
var |
The path to compare, e.g. vars.choice. |
cases |
[{value, out}] — the value to match and the out to leave by (out defaults to value); no match leaves by default. |
Branches: default — One out per config.cases[].out (defaulting to the case value), plus default.
Keep in mind: No match leaves by default — draw it.
Check value
Is it a real amount, phone, date or email? Store it cleaned up, or branch on why not.
| Setting | What it does |
|---|---|
rules |
[{value, as, store_as?, label?, message?, optional?, …options}] — value is templated; as is one of text, number, integer, decimal, money, percent, boolean, date, datetime, time, email, phone, url, uuid, id, regex, in, length, range, required. A rule that passes writes its cleaned-up value to store_as (money → a number, phone → 255712345678, date → YYYY-MM-DD, uuid → lower-case). Options: min / max (a number, a length, or a date), decimals, currency, country (phone, default TZ), version (uuid: any, 4 or 7), pattern (regex and id), values (in), optional (an empty answer is fine), label (what to call it to a customer), message (your own wording instead of ours). |
mode |
all (every rule must pass, the default) or any (one is enough). |
store_errors_as |
Optional variable to put the full list of failures in; the first one is always in vars.__check as {field, label, kind, reason, code, message, value}. |
Branches: valid invalid
Keep in mind: The first failure is always in vars.__check, so the invalid branch can say what was wrong.
Split test
A weighted split, sticky per contact.
| Setting | What it does |
|---|---|
variants |
[{out, weight}] — each out becomes an exit; the choice is weighted and sticky per contact. |
Branches: (dynamic) — One out per config.variants[].out — there are no fixed outs.
Keep in mind: Sticky per contact: the same customer takes the same branch every time.
Loop
Repeat a branch a bounded number of times.
| Setting | What it does |
|---|---|
max_iterations |
How many times the body branch may run before leaving by done (1–50). |
index_as |
Variable name for the position, from 0 (default loop_index); give a nested loop its own. |
reset |
true (default) starts the count over when the loop is entered from outside its body; false carries on where it left off. |
Branches: body done
Keep in mind: Bounded at 50 laps; sends inside a loop count against the session's quotas.
For each
Walk a list one item at a time — records from Find records, or any list variable.
| Setting | What it does |
|---|---|
list |
The path to the list to walk, e.g. vars.customers. |
item_as |
Variable name for the current item (default item); give a nested walk its own. |
index_as |
Variable name for the position, from 0 (default index); give a nested walk its own. |
max_items |
Stop after this many items (1–50). |
reset |
true (default) starts the walk over when the node is entered from outside its body; false carries on where it left off. |
Branches: item done
Keep in mind: Walks at most 50 items; the body must lead back to this node to close.
Subflow
Call another published flow and come back with its answers. A child that ends — by an End node or by running off its last edge — returns to next; one that fails, or ends with a reason other than completed, takes failed, with vars.__subflow = {reason, via} saying why.
| Setting | What it does |
|---|---|
flow_id |
Id of the published flow to run. |
inputs |
Optional {name: value} passed into the subflow's variables (templated; a value that is exactly one {{…}} keeps its type, so a list crosses as a list). |
outputs |
Optional list of variable names to bring back when it returns. The child's own defaults (onError, onTimeout) are ignored: the root flow's apply throughout. |
Branches: next failed
Keep in mind: The child must be published (a draft child runs only in the simulator); its own defaults are ignored.
Go to
Jump to another node.
Branches: next
Keep in mind: Jumps inside this flow only.
End
Finish the session with a reason.
| Setting | What it does |
|---|---|
body |
Optional goodbye message. |
reason |
Why the session ended, e.g. completed; shown in analytics. |
Branches: (none — the flow ends)
Keep in mind: The reason is what analytics and the runs list show — pick a stable one.
Where next
- Open the builder at Message flows and drag any of these onto the canvas — the panel on the right shows the same settings.
- New to flows? Start with Build your first WhatsApp flow.
- The other groups: Say · Ask · Think · Wait · Do · Route.
- The rules every flow lives under: the 24-hour window, payments, what we keep and why a flow stops.