Back to the journal
Momo Business Guides

Flow steps: Ask

Buttons, lists, typed answers, forms, files, locations and carts — what each question accepts, where it stores the answer and which branch it leaves by.

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.

Ask steps send a question and wait for the answer. Each one stores what came back in a variable and leaves by a branch that says what happened — the tap, the typed answer, a timeout, or an answer that did not fit.

This is the reference for the Ask 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}}.

Buttons

Up to three reply buttons. One out per button.

Setting What it does
body The question above the buttons.
header Optional header line.
footer Optional footer line.
buttons Up to 3 of {id, title}; each id becomes an out of this node.
store_as Optional variable to store the tapped button id in.
retries How many times to re-ask after an answer that does not fit, before leaving by timeout (0–5, default 1).
timeout_minutes Give up waiting for a reply after this many minutes and leave by timeout.
retry_prompt What to say when the answer does not fit and there is a retry left.

Branches: timeout window_closed — One out per config.buttons[].id, plus timeout.

Keep in mind: At most three buttons of 20 characters. A tap on an older menu re-asks the current question instead of jumping.

List

Up to ten rows across sections. One out per row.

Setting What it does
body The text above the list.
header Optional header line.
footer Optional footer line.
button Label on the button that opens the list (default "Choose").
sections [{title, rows: [{id, title, description}]}], at most 10 rows in all; each row id becomes an out.
store_as Optional variable to store the chosen row id in.
retries How many times to re-ask after an answer that does not fit, before leaving by timeout (0–5, default 1).
timeout_minutes Give up waiting for a reply after this many minutes and leave by timeout.
retry_prompt What to say when the answer does not fit and there is a retry left.

Branches: timeout window_closed — One out per row id across config.sections[].rows[], plus timeout.

Keep in mind: At most ten rows in all. The customer may also type the row number or its title.

Ask text

Wait for a typed answer and store it, with optional validation.

Setting What it does
body The question to ask.
store_as Variable to store the typed answer in (required).
validate Optional: none, or any value kind — text, number, integer, decimal, money, percent, boolean, date, datetime, time, email, phone, url, uuid, id, regex, in, length, range, required. A miss leaves by invalid after the retries, and a hit stores the cleaned-up answer (number → a number, email → lower-cased, phone → 255712345678, date → YYYY-MM-DD).
pattern The regular expression when validate is regex (or id).
min With validate: the smallest number, the earliest date, or the fewest characters allowed.
max With validate: the biggest number, the latest date, or the most characters allowed.
decimals With validate number, decimal, money or percent: how many decimal places to keep.
currency With validate money: the three-letter code, default TZS.
country With validate phone: the two-letter country a leading 0 belongs to, default TZ.
values With validate in: the list of answers that count, matched case-insensitively.
accept_caption Optional true: the caption on a photo or document counts as the typed answer. Off, a sticker, pin, contact card, cart, form or file is re-asked, never stored ({{last.kind}} says what arrived).
join_burst Default true: two or three bubbles typed within 10 seconds of each other ("John" / "Doe") are stored as one answer joined with newlines.
retries How many times to re-ask after an answer that does not fit, before leaving by timeout (0–5, default 1).
timeout_minutes Give up waiting for a reply after this many minutes and leave by timeout.
retry_prompt What to say when the answer does not fit and there is a retry left.

Branches: ok invalid timeout window_closed

Keep in mind: A sticker, photo, pin, contact card or cart is re-asked, never stored — accept_caption lets a photo caption count.

Confirm

A yes / no question, rendered as two buttons.

Setting What it does
body The yes / no question.
header Optional header line.
footer Optional footer line.
yes_label Label on the yes button (default "Yes"); the out stays "yes".
no_label Label on the no button (default "No"); the out stays "no".
store_as Optional variable to store yes or no in.
retries How many times to re-ask after an answer that does not fit, before leaving by timeout (0–5, default 1).
timeout_minutes Give up waiting for a reply after this many minutes and leave by timeout.
retry_prompt What to say when the answer does not fit and there is a retry left.

Branches: yes no timeout window_closed

Keep in mind: Typed ndiyo / hapana, yes / no and 1 / 2 count as well as the buttons.

WhatsApp form

A native WhatsApp Flow form. Answers land in variables.

Setting What it does
flow_id The WhatsApp Flow id from Meta's Flow Builder.
body Message text sent with the form button.
cta Label on the button that opens the form.
screen Optional first screen id of the form.
prefill Optional [{name, value}] to pre-populate form fields (templated).
map Optional {form_field: variable} to copy individual answers into variables.
store_as Optional variable to store all answers in as one object.
mode Optional draft while the form is still a draft in Meta's Flow Builder; blank means published.
retry_prompt What to say when the customer types instead of opening the form (default: "please use the Open form button above"). The original timeout stands; after the retries the node leaves by invalid, then abandoned, then timeout.
retries How many times to re-ask after an answer that does not fit, before leaving by timeout (0–5, default 1).
timeout_minutes Give up waiting for a reply after this many minutes and leave by timeout.

Branches: submitted invalid abandoned timeout window_closed

Keep in mind: The form itself is built in Meta's Flow Builder; only the fields you map land in variables.

Ask for a file

Wait for a photo or document — proof of payment, an ID.

Setting What it does
body What to ask for.
accept Optional list of image, video, audio, document, sticker and/or mimes such as application/pdf or image/*; blank accepts anything, a mismatch leaves by wrong_type.
store_as Variable to store the received file in: {url (signed link, no login, valid 30 days), path, disk, mime, type, size_bytes, filename, sha256, message_id, whatsapp_media_id, status, expires_at}. The local copy is what the flow owns — Meta's media id expires. A download the router could not complete leaves by failed with vars.__media_error.
retries How many times to re-ask after an answer that does not fit, before leaving by timeout (0–5, default 1).
timeout_minutes Give up waiting for a reply after this many minutes and leave by timeout.
retry_prompt What to say when the answer does not fit and there is a retry left.

Branches: ok wrong_type failed timeout window_closed

Keep in mind: Meta's media link expires, so the flow keeps its own copy; the signed link in the variable lasts 30 days.

Ask for the cart

Wait for the customer to send their WhatsApp cart, and read what is in it.

Setting What it does
body What to say while waiting — usually "tap Send cart when you are done".
store_as Variable to store the cart in: {catalogue_id, items[{retailer_id, quantity, unit_price_minor, line_total_minor, currency}], count, quantity, subtotal_minor, subtotal, currency, note}. Defaults to cart.
retries How many times to re-ask after an answer that does not fit, before leaving by timeout (0–5, default 1).
timeout_minutes Give up waiting for a reply after this many minutes and leave by timeout.
retry_prompt What to say when the answer does not fit and there is a retry left.

Branches: received no_items timeout window_closed

Keep in mind: Needs a connected catalogue; the cart arrives when the customer taps Send cart.

Ask for location

Request a pin with WhatsApp's own location prompt.

Setting What it does
body Text sent with WhatsApp's location request.
store_as Variable to store the pin ({latitude, longitude, name, address}) in.
accept_text Optional true: a typed address counts and is stored as {address}. Off, typing re-asks with the location request, then leaves by invalid.
retries How many times to re-ask after an answer that does not fit, before leaving by timeout (0–5, default 1).
timeout_minutes Give up waiting for a reply after this many minutes and leave by timeout.
retry_prompt What to say when the answer does not fit and there is a retry left.

Branches: ok invalid timeout window_closed

Keep in mind: Uses WhatsApp's own location request; a typed address only counts with accept_text.

A button that opens a URL. It does not wait for a reply.

Setting What it does
body The message text above the button.
display_text Label on the button.
url The URL the button opens (templated).

Branches: next window_closed

Keep in mind: It does not wait — the next step runs at once.

Numbered menu

A plain-text "1. … 2. …" menu — how a list reads on SMS.

Setting What it does
body The text above the numbered items.
items [{id, label}] rendered as "1. label"; each id becomes an out, and the customer replies with the number.
store_as Optional variable to store the chosen item id in.
retries How many times to re-ask after an answer that does not fit, before leaving by timeout (0–5, default 1).
timeout_minutes Give up waiting for a reply after this many minutes and leave by timeout.
retry_prompt What to say when the answer does not fit and there is a retry left.

Branches: timeout window_closed — One out per config.items[].id, plus timeout.

Keep in mind: A plain-text menu; the customer answers with the number (or the title).

Where next

Thanks for reading.Explore more stories