Features Add-ons Pricing Cost calculator Use cases Compare Blog

Forms inside the chat

WhatsApp Flows: How Forms Inside a Chat Really Work

Last checked · 14 min read

The short answer

WhatsApp Flows are Meta's forms inside a chat: a customer taps a button and answers screens without leaving WhatsApp. A Flow is sent as an interactive message inside an open 24-hour window, or on an approved template's FLOW button. Answers return as an nfm_reply message carrying the send's flow_token, but not the Flow ID. Checked 15 October 2026.

The WhatsApp forms builder in WabaCRM's demo workspace, open at its Questions section beside an Add a question button. Three question cards show a kind, the question and a hint box with move and remove controls: Your name, Phone number, and a Which store? dropdown listing two Bengaluru stores.

What are WhatsApp Flows?

WhatsApp Flows are forms that open on Meta's own screens inside a WhatsApp chat: the customer taps a button on a message, answers one or more screens, and submits without leaving the conversation. In Meta's words:

"WhatsApp Flows is a way to build structured interactions for business messaging." (Meta — WhatsApp Flows)

The examples Meta packages on that page are a pre-approved loan offer, an insurance quote, collecting purchase intent and a personalised offer. Its service messages page gives the everyday list:

"For example, you can use WhatsApp Flows to book appointments, browse products, collect customer feedback, get new sales leads, or anything else." (Meta — Service messages)

What decides whether a Flow works in production is three objects and the joins between them: the Flow, which belongs to one WhatsApp Business Account and moves through statuses; the message that carries it, which obeys the same window rules as any other message; and the response, which arrives on your ordinary messages webhook without saying which Flow it answers. This page takes each in Meta's own words, including one question on which Meta's documentation disagrees with itself.

The round trip of a WhatsApp Flow, from a token stored before the send to answers that return without naming their Flow
A Flow response names no Flow, so the token you stored before sending is what matches the answers to the send.

How does a Flow reach a customer's chat?

By one of two routes, and the customer service window decides which:

"When a WhatsApp user messages you or calls you, a 24-hour timer called a customer service window starts. If the user messages or calls you again before the timer expires, the timer resets to 24 hours." (Meta — Service messages)

Interactive Flow messages are on that page's list of service messages, and the page is plain: "When the window closes, you can only send pre-approved template messages." The refusals you get for ignoring that are in the 24-hour window error codes.

Inside the window, Meta's sending guide describes the interactive route:

"You can send a Message with a Flow in a user-initiated conversation using a Message with a Call To Action (CTA)." (Meta — Send a Flow message)

Outside it, the Flow rides on a template:

"To send a business initiated message with a Flow, you can create and send a message template with a WhatsApp Flow attached to it. A new button type called FLOW is available." (Meta — Send a Flow message)

Interactive Flow message Template with a FLOW button
When it can go Open 24-hour window only Outside the window too, once approved
Button text Advised 30 characters or less 25 characters maximum
Review No pre-approval Template must read APPROVED
Which Flow flow_id or flow_name ID, name or inline Flow JSON

The button limits are Meta's wording on that page: "CTA text length is advised to be 30 characters or less (no emoji)." and "Button label text. 25 characters maximum." Template review is its own subject, covered in every status a template can be in. Both routes share one prerequisite: "You will need to verify your business and maintain a high message quality."

On the wire, the interactive route is an interactive message of type flow. Trimmed from Meta's full-parameter sample:

"interactive": {
  "type": "flow",
  "body": { "text": "Flow message body" },
  "action": {
    "name": "flow",
    "parameters": {
      "flow_message_version": "3",
      "flow_token": "AQAAAAACS5FpgQ_cAAAAAD0QI3s.",
      "flow_id": "123456",
      "flow_cta": "Book!"
    }
  }
}

What ties a submitted Flow back to the message you sent?

Not the Flow. This is the sentence the whole integration hangs on:

"The Flow response does not include the Flow ID. You can include a custom field in your Flow payload or an identifier in the flow_token field to identify the corresponding Flow." (Meta — Flows Webhooks)

The flow_token is yours to mint. Meta's parameter table calls it "Flow token that is generated by the business to serve as an identifier." and gives its default as unused. Send a Flow without one and the token is that default, which tells you nothing about who was asked what.

The webhook carries one more clue: "Context object contains message_id of flows request message and sender number." That points at a message rather than a Flow, and it only helps if you kept the message id your send returned.

So the order matters. Write your record of the send after Meta answers, and a crash between the two leaves a form on a customer's phone whose answers match nothing. Mint the token, store it with the contact and the Flow, then send.

WabaCRM's WhatsApp forms, an optional add-on, work in that order. A random token is written to the database before the message is queued, and the send is linked to its outgoing message afterwards; answers are matched on the token alone. When an answer arrives, its token must belong to the same workspace as the number the webhook came in on, and a redelivered webhook changes nothing. If a customer submits the same form twice, the first answer is kept and the second does not replace it.

What does the Flow response webhook actually contain?

An ordinary incoming message:

"When a user completes the Flow, a response message is sent to the WhatsApp chat. You receive it through the same webhook you use to process all other messages from users." (Meta — Flows Webhooks)

Trimmed, this is the structure Meta documents:

"type": "interactive",
"interactive": {
  "type": "nfm_reply",
  "nfm_reply": {
    "name": "flow",
    "body": "Sent",
    "response_json": "{\"flow_token\": \"<FLOW_TOKEN>\", \"optional_param1\": \"<value1>\", \"optional_param2\": \"<value2>\"}"
  }
}

Three things in it catch people. Meta's field table says interactive.type is "Always nfm_reply", so a handler that only knows button and list replies drops Flow answers. The body is "Always Sent", so it is never the answer. And response_json is a string holding JSON, which has to be decoded a second time before any field can be read.

What is inside that string is whatever your Flow put there. The Flow JSON reference describes the complete action that ends a form:

"Terminates the flow and sends the response message to the chat thread. The business will receive the termination message bubble on the webhook, together with the flow_token and all of the other parameters from the payload." (Meta — Flow JSON)

The customer can look back at what they sent: "As of Flows version 5.1, upon submitting a Flow, a user will be able to access a summary of their submitted data via the Flow response message UI." Meta's same paragraph says that summary leaves out password and passcode fields, and any field the business marks sensitive. Meta's best-practice guide also says "Respond to the user with a message when they submit a Flow." WabaCRM's forms add-on sends no such reply by itself.

Why does a Flow that saved successfully still refuse to publish?

Because Meta reports a broken Flow as a successful save. The Flows API reference says "New Flows are by default created in DRAFT status and you can make changes to the Flow by uploading a JSON file." and "Every update request will return validation errors in the Flow JSON, if any." Its sample response, trimmed, shows both at once:

{
  "success": true,
  "validation_errors": [
    {
      "error": "INVALID_PROPERTY_VALUE",
      "message": "Invalid value found for property 'type'."
    }
  ]
}

Code that branches on success alone records that Flow as saved, yet "All errors must be fixed before the Flow can be published." Read validation_errors on every create and update.

WabaCRM stores the validation errors even when Meta's answer says success, shows each one with the part of the form it points at when Meta names one, and refuses to publish the form while any of them stand.

Can you edit a WhatsApp Flow once it is published?

Meta's pages answer both ways. The Flows API status table says of a published Flow: "This Flow cannot be deleted or updated afterwards." The deprecation section agrees: "Once a Flow is published, it cannot be modified or deleted, but can be marked as deprecated."

The publishing section of the same page says the opposite:

"You can either edit this flow in the future and turn it back to the “DRAFT” state, or create a new flow by specifying the existing Flow ID as the clone_flow_id parameter." (Meta — Flows API)

And the sending guide assumes editing works:

"In case you edited published flow and now it is in the draft state, use “mode=draft” to send the current draft flow version, or “mode=published” (default value) to send the last published flow version." (Meta — Send a Flow message)

Two sentences against two. The route no sentence contradicts is in that publishing sentence: create a new Flow with clone_flow_id set to the published one and edit the copy, which gets its own Flow ID. WabaCRM attempts an edit to a published form and reports Meta's refusal if one comes back, rather than promising either outcome.

The status table itself reads:

Status Sending What else Meta says
DRAFT Only in draft mode, for testing Can be deleted
PUBLISHED To customers Cannot be deleted or updated
DEPRECATED Cannot be sent or opened Cannot be restored
THROTTLED 10 messages an hour Endpoint unhealthy
BLOCKED Cannot be sent or opened Endpoint unhealthy

Every row comes from the Flows API reference. Of a draft:

"The Flow can only be sent with "mode": "draft" for testing." (Meta — Flows API)

Of throttling: "Flows with throttled status can be opened, however only 10 messages of the Flow could be sent per hour." The last two rows are endpoint health, not policy, and the Flows error 132068 and 132069 explainer covers them.

Can you build a WhatsApp Flow without writing JSON?

For a one-screen form in WabaCRM, yes; Meta's own tools help too, but underneath every Flow is Flow JSON. Meta's sending guide says "You can quickly build a Flow in the playground and pass the Flow JSON in the message template creation request." To see a whole Flow, Meta points to a Builder in WhatsApp Manager, under Account tools and then Flows:

"To visualize the complete user experience, use the Builder. The Builder emulates the entire Flow experience and can be updated on the fly." (Meta — Flow JSON)

Whoever writes the JSON, the same rules apply: "It’s mandatory to have a Footer component on the terminal screen." The Components reference adds "The maximum number of components per screen is 50.", and its limits table caps a text input's label at 20 characters, helper text at 80, a heading at 80 and body text at 4,096. The best-practice guide sets the human limit: "Users should enter flows aiming to complete a task as quickly as possible, with tasks taking no longer than 5 minutes to complete."

WabaCRM guided form builder in the demo workspace with three questions: a required short-text Your name, a required phone number hinted We will confirm on WhatsApp, and a Which store? dropdown with two Bengaluru choices
The guided builder: each question has a kind, the question itself, an optional hint and a must-be-answered tick.

WabaCRM's guided builder makes one screen, with ten question kinds: short text, email address, phone number, number, long text, a dropdown, single choice as buttons, several choices, a date and a consent tick-box. Any question can be marked as required. The builder refuses, in words, a label over 20 characters on the five text kinds. Save to Meta creates the form there as a draft and puts anything Meta objects to on screen.

A multi-screen form, a calendar or branching has to come from outside the guided builder: built at Meta and brought in with Bring in forms from Meta, or pasted as Flow JSON. Either can then be sent from the inbox, but the guided builder will not reopen it, because it cannot represent everything such a form may hold. None of this is WabaCRM's multi-step bot flows, a separate add-on that sends ordinary messages rather than Meta forms; the WhatsApp chatbot builder guide covers those.

Does a form-style Flow need a server endpoint?

No. A Flow that ends in a complete action needs nothing of yours running while the customer fills it in. Meta's endpoint guide says what an endpoint is for: "Endpoints provide dynamic data for the screens and control routing." The best-practice guide draws the line:

"Only use endpoint powered flows, when the live data is required while user completes a flow - for example, for ticket booking." (Meta — WhatsApp Flows best practices)

An endpoint also brings monitoring: Meta's status table opens both its throttled and blocked entries with "Monitoring detected that the endpoint is unhealthy", and gives no other reason for either status.

WabaCRM hosts no Flow endpoint, so its forms are static: no live appointment slots, prices or stock when the customer opens one.

How does WabaCRM send a form and keep its answers?

From the inbox, into an open window. The Send a form button appears in a WhatsApp conversation only while its 24-hour window is open, the add-on is installed, and that thread's number has a form that can be sent. The agent picks a form, types a button label of up to 30 characters, Meta's advised length, adds a line saying why they are asking, and sends. Anyone whose role can reply in the shared team inbox can send a form; building forms is for owners, admins and managers by default.

In WabaCRM a form belongs to one number (Meta holds each Flow under one WhatsApp Business Account), and its number cannot be changed once it exists. A draft form is listed in the picker with "(draft)" after its name and goes out in Meta's draft mode, which Meta reserves for testing, so publish before customers see it.

When the customer submits, the chat thread shows a bubble with the body text Meta sends with the response, which Meta's field table says is always "Sent", or "Form submitted" when there is none, never the answers. Those appear in the form's Answers panel on the forms screen as label and value pairs, with several choices joined by commas and a tick-box shown as Yes or No. The panel lists the newest 50, and each form's row counts how many sends were answered. Answers are stored against the contact the form was sent to; they are not written into any contact field. What the add-on costs is on the add-ons page.

What does the WhatsApp forms add-on leave out?

You might expect What actually happens
A form in a campaign, drip or chatbot Sent from the inbox only
A form outside the 24-hour window Not sent; no template FLOW button
Answers copied into contact fields Kept per send, in the Answers panel
A multi-screen form built here Build at Meta, then import or paste
Live data from your own systems No endpoint; forms are static
An alert when Meta throttles a form Status refreshes on save, publish or ↻
Forms on Messenger or Instagram WhatsApp numbers only
Moving a form to another number Not possible
Sending a form from the Android app Not in the app; it links to the web

Plan around the window row. Meta allows a Flow outside the window on an approved template's FLOW button, but WabaCRM's template builder does not offer that button, so a form here only goes into a window the customer opened by writing or calling. There is no export of answers either.

Every Meta quotation on this page was read from Meta's own documentation on 15 October 2026. Meta changes that documentation without notice; the linked pages are authoritative and this one is not.

Frequently asked

Can a WhatsApp Flow be sent to someone who has not messaged you?

Yes, but only on a template. Meta's sending guide says a business-initiated Flow goes out as a message template with a FLOW button attached, and the template must pass review and read APPROVED before it can be sent. The interactive Flow message, the one with a call-to-action button and no template, is a service message, so it can only go out while the customer's 24-hour customer service window is open. WabaCRM's form add-on uses the second route only: an agent sends a form from the inbox into an open window, and WabaCRM's template builder does not offer a FLOW button.

How do I tell which Flow a webhook response belongs to?

Not from the response itself. Meta's Flows webhook page states that the Flow response does not include the Flow ID, and suggests a custom field in the completion payload or an identifier in the flow_token field. The flow_token is a string you generate and pass when you send the Flow, and it comes back inside response_json when the customer submits. Store it, together with whatever it should match, before the message goes out. The webhook also carries a context object pointing at the message the customer replied to, which only helps if you recorded the message id your send returned.

Can I delete a WhatsApp Flow after publishing it?

No. Meta's Flows API reference says a Flow can be deleted while it is in DRAFT status, and that a published Flow cannot be deleted; it can only be marked as deprecated. Deprecating is final as well: Meta says deprecated Flows cannot be deleted or restored to a previous state, and deprecation prevents the Flow being sent or opened. Meta also says to always test on a mobile device before publishing, so try a Flow as a draft first. WabaCRM follows the same rules: Delete is offered only before a form is published, Retire is permanent, and both ask you to confirm first.

Do WhatsApp Flows need a server or a data endpoint?

Not for an ordinary form. A Flow whose last screen ends in a complete action sends its answers back as a message on the webhook you already use for incoming messages, with no server of yours involved while the customer fills it in. Meta describes endpoints as providing dynamic data for the screens and controlling routing, and its best-practice guide says to use endpoint-powered Flows only when live data is needed during the Flow, with ticket booking as its example. Endpoint Flows are also watched by Meta's health monitoring. WabaCRM hosts no endpoint, so its forms are static ones.

Where do the answers to a WabaCRM form end up?

In the form's Answers panel on the WhatsApp forms screen, matched to the send by the token WabaCRM minted for it and stored against the contact the form was sent to. Each answer is shown as label and value pairs, the newest 50 are listed, and the list row counts how many sends were answered. The chat thread shows only a bubble carrying the body text Meta sends with the response, which Meta documents as always Sent, never the values. Answers are not copied into contact fields or custom fields, and if a customer submits the same form twice, the first answer is the one kept.

Related guides

Ask for details in a form, not ten messages

Free for your first 1,000 contacts, with no time limit and no card. Setting up the workspace takes minutes; connecting a number takes as long as Meta's own review of it.

Sign up with your company email address. No sales call, no onboarding fee, nothing to schedule.

Why this is safe to point your customer list at

Payments are processed by Razorpay on their own checkout — your card details are never entered on, or stored by, WabaCRM. Every inbound WhatsApp webhook is checked against its signature before it is trusted.

Tech Provider is a Meta platform access tier — not a partnership, a reseller agreement or an endorsement.