Module · Replying and messaging
WhatsApp Question
Lesson 9 of 21 · 4 min
Every node so far finishes what it is doing and hands on. This one sends a message and then stops — the execution ends, its state is written down, and nothing is running. When the customer replies, days later if that is when they get round to it, the workflow starts again from exactly this node with their answer in hand.
That is the whole idea, and it is what separates a WhatsApp broadcast from a WhatsApp conversation. Chain a few of these and you have an intake form that lives in someone's messages: what are you interested in, when suits you, what is your budget — each answer stored under a name, each one available to everything downstream.
No workflow on this page. Like the last lesson, this node needs a connected WhatsApp provider and the tutorial organisation has none — so there is no validated workflow to show, and this lesson stays with the configuration and the branching model.
The fields
| Field | What it holds |
|---|---|
question_text | The question/message to send to user |
buttons | Quick reply options. Up to 3 → sent as WhatsApp reply buttons (Meta hard limit on the interactive button payload). 4 to 10 → automatically rendered as a WhatsApp list message (Meta's other primitive, capped at 10 rows total). Anything beyond 10 is silently truncated. Reply-button titles are visible up to 20 chars on Meta's side; list rows up to 24 chars. |
variable_name | Name to store the user's response (for use in subsequent nodes) Defaults to user_response. |
expected_type | Expected response type One of: any · button · text · number · date Defaults to any. |
timeout_minutes | Timeout in minutes (0 for no timeout) Defaults to 60. |
timeout_message | Message to send if user doesn't respond in time |
question_text
- What it holds
- The question/message to send to user
buttons
- What it holds
- Quick reply options. Up to 3 → sent as WhatsApp reply buttons (Meta hard limit on the interactive button payload). 4 to 10 → automatically rendered as a WhatsApp list message (Meta's other primitive, capped at 10 rows total). Anything beyond 10 is silently truncated. Reply-button titles are visible up to 20 chars on Meta's side; list rows up to 24 chars.
variable_name
- What it holds
- Name to store the user's response (for use in subsequent nodes) Defaults to
user_response.
expected_type
- What it holds
- Expected response type One of:
any · button · text · number · dateDefaults toany.
timeout_minutes
- What it holds
- Timeout in minutes (0 for no timeout) Defaults to
60.
timeout_message
- What it holds
- Message to send if user doesn't respond in time
Name the variable. variable_name defaults to user_response, which is fine for one question and useless for four — every node would be writing to the same name. Rename each one for what it holds and the rest of the workflow reads like prose.
Options are branches, not just buttons
The buttons array is the interesting field. Each entry has a label the customer sees and a payload that identifies it — and that payload becomes an output handle on the node. Tap “Machine Learning” and the workflow does not merely record the words; it leaves down the ML edge.
| How many options | What the customer gets | What the canvas gets |
|---|---|---|
| A plain question. They type whatever they like. | One output handle. | |
| Tappable reply buttons under the message. | One handle per option, plus a default for anything unmatched. | |
| A list message — a button that opens a scrollable list of choices. | Same: one handle per option, plus default. | |
| The first ten. The rest are dropped before the message is sent. | Ten handles. The eleventh option has no branch either. |
- What the customer gets
- A plain question. They type whatever they like.
- What the canvas gets
- One output handle.
- What the customer gets
- Tappable reply buttons under the message.
- What the canvas gets
- One handle per option, plus a default for anything unmatched.
- What the customer gets
- A list message — a button that opens a scrollable list of choices.
- What the canvas gets
- Same: one handle per option, plus default.
- What the customer gets
- The first ten. The rest are dropped before the message is sent.
- What the canvas gets
- Ten handles. The eleventh option has no branch either.
The truncation is the node's, not Meta's. It is easy to assume WhatsApp is trimming your list somewhere out of sight. It is not — the node itself cuts to ten before sending, and the routing graph is cut to ten to match, so the two stay consistent. The practical rule: if you have more than ten choices, this is the wrong shape. Ask a narrowing question first.
The default path matters more than it looks. Someone who ignores your three buttons and types “can I speak to a person” goes down it. Wire it. A flow with three branches and nothing on default silently drops every customer who did not do as they were told.
What comes back
| Reference | What you get |
|---|---|
{{alias.response}} | The reply text. Always populated, whether they typed or tapped. |
{{alias.response_data._matched_button}} | Which option they tapped — absent if they typed. The way to tell a chosen answer from a written one. |
{{your_variable_name}} | The same reply under the name you set in variable_name. |
{{alias.response}}
- What you get
- The reply text. Always populated, whether they typed or tapped.
{{alias.response_data._matched_button}}
- What you get
- Which option they tapped — absent if they typed. The way to tell a chosen answer from a written one.
{{your_variable_name}}
- What you get
- The same reply under the name you set in variable_name.
There is one more thing that is not in any list of outputs and is worth knowing, because it looks like magic when you meet it: the node also files the reply under message, message_text, user_input and query. Those are the names a Core Agent, an LLM node or a Knowledge Base search look for by default — so putting one of those directly after this node works without wiring anything. Convenient, and worth remembering when a node downstream mysteriously already knows the answer.
The timeout
The pause is bounded. timeout_minutes defaults to 60 and caps at 1440 — a day. When it expires the node sends timeout_message and the workflow carries on down its default path, so a customer who wandered off gets a polite close rather than nothing.
Zero means wait forever, and it is a trap rather than a feature. A workflow parked with no timeout holds its state until something ends it, and nothing will. Every abandoned conversation becomes a run that never finishes, and they accumulate quietly. Pick a real number — ten minutes for a qualification question somebody is answering right now, a day for a booking they need to think about.
What breaks
The 24-hour window applies to the question too. This node sends a free-form message, so everything from the last lesson holds: if the customer has not messaged you in 24 hours, the question does not go out. A multi-step flow is therefore safe while they are actively replying and stops dead the moment they leave it overnight — which is exactly when your timeout was going to save you and cannot, because the question was never asked.
An empty payload is not the same as no payload. The handle id falls back through payload, then the label, then a generated name. Give every option an explicit short payload — ml, ai, da — and your edges stay stable when somebody rewords a label. Leave payloads blank and renaming “Machine Learning” to “ML & AI” silently moves the branch.
Expected type does not validate for free. Setting expected_type to number constrains what counts as an answer, which means a customer who types “about five thousand” has not answered. That is correct behaviour and it is also how a flow gets stuck arguing with someone. Use any and check the value yourself downstream unless you genuinely need the shape.
Try it
- Add one Question with three options and give each an explicit payload. Save it and look at the node on the canvas — it now has four handles where it had one.
- Wire a different reply to each, plus something on
default. Message your business number and tap one. - Now reply with free text instead of tapping. You should come out of
default— and{{alias.response_data._matched_button}}should be empty. That is the check for “did they choose or did they write”. - Add a fourth option and send again. The buttons become a list, with nothing in the config changed.
- Set
timeout_minutesto 1, ask a question, and ignore it. A minute later the timeout message arrives and the workflow moves on — watch it in the execution log.
Next: Campaign Send — the other way to reach a list, where the words live in a template somebody else maintains.

