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
form_id A Momo form (Forms page) by id or uuid. Its live version is sent, the send is logged, answers are recorded on the form and saved to its Daftari table. Takes precedence over flow_id.
flow_id Only without form_id: a WhatsApp Flow id pasted from Meta's WhatsApp Manager.
template_id Optional, with form_id: an approved template with an "Open form" button for the same form, used when the 24-hour window has closed. Without it a closed window leaves by window_closed.
auto_map With form_id, default true: every question lands on a variable named after its saved-as key (vars.jina_kamili). map still overrides one key.
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: Pick a Momo form or build one without leaving the canvas; its answers land on variables named after the questions. A form id pasted from Meta still works.

Send your shop page — the menu, one section, one product or a basket already filled; the order comes back to the chat.

Setting What it does
shop_channel_id The shop page (a storefront channel) that opens. Blank uses the workspace's only one; with two or more, publish asks you to choose.
target Where the page opens: shop (the menu, default), category (one section) or product (one product).
category_id With target category: the section — a category id or slug (templated).
product_sku With target product: the product's SKU (templated).
prefill_from Optional variable holding lines [{sku or name, variant?, addons?, quantity?, note?}] — or a cart — to put in the basket before the customer opens it (e.g. what an AI step extracted). A product with sizes gets its first size in stock; unmatched lines are left out.
body Message text above the link button, e.g. "Tap below to see today's menu."
cta Label on the link button (20 characters). Default "See the menu".
template_id Optional: an approved template whose URL button ends in {{1}} (…/w/{{1}}), used when the 24-hour window has closed. A utility or marketing template is also what opens the page inside WhatsApp (2K+ messaging tier). Without it a closed window leaves by window_closed.
store_as Variable the basket lands on when the customer sends it, default order — the one cart shape: {ref, order_id, order_reference, reference, items[{sku, retailer_id, name, label, variant, options[{group, option, price_delta_minor}], note, quantity, unit_price_minor, line_total_minor, currency}], count, quantity, subtotal_minor, subtotal, total_minor, currency, note}. The order already exists — use order_id rather than Create order.
confirm Default true: say the order summary in the chat ("Oda #1042 ✓ …") when it comes back. Off, the next step speaks.
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: submitted typed timeout window_closed

Keep in mind: A page cannot close WhatsApp's browser: after sending, the customer taps ✕ and finds the order in the chat. The order is made when they send — every price, size and add-on checked on our side, never taken from the page.

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. A basket from the shop page (sent by its code, or through offer_link) is stored in the same shape with more on each line (variant, options, note) and order_id — that order already exists.
offer_link Optional true: under the question, send a link to your shop page; the basket the customer sends from it answers this step too (the workspace's only storefront).
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