Links

Flows

If you are using On-Premise API, remember that it is being discontinued by Meta. No new signups will be allowed with this type of integration from May 15, 2024.
Numbers registered before this date will still be supported, but should start planning a change of hosting type as soon as possible.
WhatsApp Flows is a way to build structured interactions for business messaging. With Flows, businesses can define, configure, and customize messages with rich interactions that give customers more structure in the way they communicate.
You can use Flows to book appointments, browse products, collect customer feedback, get new sales leads, or anything else where structured communication is more natural or comfortable for your customers.
With Flows, you can:
  • Present simple input forms (in order to schedule an appointment, for example)
  • Create workflows that guide users through multiple screens (for ordering products, for example)
  • Create endpoints that exchange data across screens to enable more complex interactions (such as guiding a user through a process with multiple potential outcomes)

How does it work?

Flows is a feature of the WhatsApp Business Platform that allows you to swiftly develop and deploy native, task-centric workflows on WhatsApp. This results in enhanced interactions between customers and businesses.
With WhatsApp Flows businesses can design, build and customize their own journeys, which can make chatbot and AI agent solutions better, as well as offer end-to-end experiences.
For users, Flows can improve interactions with businesses on WhatsApp, leading to better task completion and fewer drop offs than alternative channels.
For businesses, Flows can improve engagement and completion rates, resulting in improved business outcomes.

Leveraging Flows

Flows is built for form-based use cases. You can create Flows to achieve a range of tasks with your customers, including:
  • Lead generation
  • Appointment booking
  • Registration, Sign up, and Sign in
  • Customer support and feedback
  • And many more
Meta will continue to expand Flows capabilities to unlock additional use cases in the future. We will keep you updated.
Sign Up
Appointment Booking

How are Flows configured and used?

Flows are linked from a CTA in a message. Flows are composed of:
  1. 1.
    Screens: When tapping on the Flows CTA in a message, the user will access the initial screen of the Flow. The user can then interact with the Flow to move through multiple screens until completion.
  2. 2.
    Layouts: These define how components are presented within a Flow, providing a structured look and feel.
  3. 3.
    Components: You can use components to display information, and to create input fields for your users. You can display information with Text, Images, and Embedded links. You can create input fields for your users to complete using Text Inputs, Dropdowns, Checkboxes, Radio Buttons, Opt-in, and Date Pickers.
Flows can be attached and sent as Business Initiated Messages, as well as standard messages. See Best Practices.

Getting Started

WhatsApp Flows are supported for numbers hosted either through Cloud API or as On-Premise API hosted by 360dialog.
The minimum version for development and go-live with WhatsApp Flows using the On-Premise API is v2.51.1.
As announced in November 2023, Meta is transitioning to a fully Cloud-hosted WhatsApp Business Platform and will stop supporting On-Premise API in October 2025.
Starting from On-Premise client v2.53, all new feature updates will be exclusively delivered to Cloud API. While the On-Premise API client will receive quarterly releases, they will focus solely on bug fixes and security patches. From May 15, 2024, 360dialog will not allow for new numbers to be onboarded with On-Premise API. We will continue supporting already registered On-Premise API throughout 2024, but we strongly recommend to start changing the hosting type of numbers to Cloud as soon as possible. Learn here how to integrate with Cloud API.

Manage Flows

Manage Flows through the WhatsApp Manager

You can set up Flows directly from the WhatsApp Business Manager UI. To access it, make sure you have admin rights to manage the account. The Flows editor can be found under the "Account tools" section under the main menu in Meta's WhatsApp Manager.
Find in Form Builder section below payload examples with code snippets and explanations of how different Flows come together to create immersive experiences for users.

Manage Flows through the Partner API

Flows can also be crafted through our Partner API. If you are integrating with a Solution Provider, or wish to build the flow on top of the API, we have various endpoints to perform different operations to Flows. Alternatively, you can access a Flow builder user interface through the "Account tools" section in Meta's WhatsApp Manager.

Setup Data Channel

In order to later de- and encrypt data passed through WhatsApp Flows, every WhatsApp Business Account (WABA) requires a key pair, which needs to be signed for every phone number sending Flows within the WABA.

Generating a Key Pair

Generate a public and private RSA key pair by typing in the following command in the terminal/console within your operating system (Note: OpenSSL needs to be installed):
openssl genrsa -des3 -out private.pem 2048
The generates 2048-bit RSA key pair encrypted with a password you provided. Next, you need to export the RSA public key to a file, which can be accessed within your file system:
openssl rsa -in private.pem -outform PEM -pubout -out public.pem

Signing the Public Key

For every phone number sending Flows the public key needs to be signed. This will be done through the our WhatsApp Business API endpoints. The process differs for Cloud API and On-Premise.
In Postman, when inputting the business public key as a parameter in the Body, select JSON format.
post
https://waba-v2.360dialog.io/
whatsapp_business_encryption
Set Business Public Key (Cloud API)
Parameters
Body
public_key*
2048-bit RSA business public key generated.
Responses
200: OK
A successful response will include the following payload:
post
https://waba.360dialog.io
/v1/settings/business/whatsapp_business_encryption
Set Business Public Key (On-Premise API) [will be deprecated]
Parameters
Body
public_key*
2048-bit RSA business public key generated.
Responses
200: OK
A successful response will include the following payload:

Verifying the Public Key

You can verify the public key by retrieving it through the following endpoints.
get
https://waba-v2.360dialog.io/
whatsapp_business_encryption
Set Business Public Key (Cloud API)
get
https://waba.360dialog.io
/v1/settings/business/whatsapp_business_encryption
Set Business Public Key (On-Premise API) [will be deprecated]

Set up Data Channel Endpoint

When the Flow needs to exchange data with your backend systems, it will make an HTTP request to an endpoint, that you provide. This endpoint needs to be set up so that it can receive and process POST requests.
With this endpoint you will receive data passed into the Flow by the end user, which you can process e.g. storing it in your database.
Once set up you will share the data channel endpoint URL with the Flow through the Flow's Flow JSON.
Since you're the owner of the endpoint you will not be able to validate the payload and verify the origin of the incoming webhook event. We will launch a solution during Q1/24 to improve the endpoint security by providing a secure proxy service. Announcements will be made here and through email communication.

Implement Encryption/Decryption

The body of each request passed to your endpoint will be encrypted and will have the following format:
{
encrypted_flow_data: "<ENCRYPTED FLOW DATA>",
encrypted_aes_key: "<ENCRYPTED_AES_KEY>",
initial_vector: "<INITIAL VECTOR>"
}
Parameter
Description
encrypted_flow_data
The encrypted request payload.
encrypted_aes_key
The encrypted 128-bit AES key.
initial_vector
The 128-bit initialization vector.
You can then proceed to decrypt this data using the encrypted_aes_key. You can find a detailed example with sample code on this page of Meta's developer documentation.
After encryption the data will have the following format:
{
version: "<VERSION>",
user_locale: "<LOCALE>",
action: "<ACTION_NAME>", // init | back | data_exchange | ping
screen: "<SCREEN_NAME>",
data: { // JSON of values from screen
prop_1: "value_1",
...
prop_n: "value_n"
},
flow_token: "<FLOW-TOKEN>”
}
After processing decrypted request you can process the data end craft a response object, that will be encrypted and returned back to the Flow. A sample response payload before encryption would follow the format:
{
eg_textfield_variable_1: "sample_Value_1",
eg_textfield_variable_2: "sample_Value_2"
}
The response payload needs to be encrypted using the AES key received in the request and can afterwards be send back as Base64 string. An example on how to encrypt the data can be found in Meta's developer documentation via this link.

Implement Endpoint Logic

Your endpoint will receive requests int he following cases:
  1. 1.
    User opens the Flow
  2. 2.
    User submits the screen
  3. 3.
    User presses the back button on the screen
  4. 4.
    Error notification request (in case your endpoint returned with invalid content on the previous request)
  5. 5.
    Periodical health check from WhatsApp
For a detailed explanation of all cases and how to process the individual requests, please refer to Meta's developer documentation for implementing endpoint logic.

Webhook Response

Once the Flow closes you will receive a message payload through your messages webhook URL, which will look like the followig example:
{
"messages": [{
"context": {
"from": "16315558151",
"id": "gBGGEiRVVgBPAgm7FUgc73noXjo"
},
"from": "<USER_ACCOUNT_NUMBER>",
"id": "<MESSAGE_ID>",
"type": "interactive",
"interactive": {
"type": "nfm_reply",
"nfm_reply": {
"name": "galaxy_message",
"response_json": {
"flow_token": "<FLOW_TOKEN>",
"optional_param1": "<value1>",
"optional_param2": "<value2>"
}
}
},
"timestamp": "<MESSAGE_SEND_TIMESTAMP>"
}]
}
Parameter
Description
context
object
Context of the message that the user replied to. Context object contains message_id of flows request message and sender number.
context.from
string
User's WhatsApp account number
context.id
string
Message ID
context.type
string
Always interactive
interactive.type
string
Always nfm_reply
interactive.nfm_reply.name
string
galaxy_message
interactive.nfm_reply.response_json
string
String that can be parsed to JSON that contains the params business sent like flow_token
timestamp
string
Time of flow response message

Send Flow as Message

You can send a Message with a Flow in a user-initiated conversation using a Message with a Call To Action (CTA). You send this message either through the On-Prem client or Cloud API with information specific to the Flow. The Flow is triggered when the user taps the CTA button.
To send a message with a Flow, you can use the new type of the Interactive Object named flow with the following properties:

Interactive message parameters

Parameter
Description
interactiveobject
The interactive message configuration
type(required) string
Value must be "flow".
action(required) object
Parameter
Description
name(required) string
Value must be "flow".
parametersobject
modestring
The Flow can be in either draft or published mode.(Default value: published)
flow_message_versionstring
Value must be "3".
flow_tokenstring
Flow token that is generated by the business to serve as an identifier.
flow_idstring
Unique ID of the Flow provided by WhatsApp.
flow_ctastring
Text on the CTA button. For example: "Signup" Character limit - 20 characters (no emoji).
flow_actionstring
navigate or data_exchange.(Default value: navigate)
flow_action_payloadstring
Required if flow_action is navigate. Should be omitted otherwise.
Parameter
Description
screenstring
The ID of the first Screen.
dataobject
Optional input data for the first Screen of the Flow. If provided, this must be a non-empty object.
Parameter
Description
screenstring
The ID of the first Screen.
dataobject
Optional input data for the first Screen of the Flow. If provided, this must be a non-empty object.
Parameter
Description
name(required) string
Value must be "flow".
parametersobject
modestring
The Flow can be in either draft or published mode.(Default value: published)
flow_message_versionstring
Value must be "3".
flow_tokenstring
Flow token that is generated by the business to serve as an identifier.
flow_idstring
Unique ID of the Flow provided by WhatsApp.
flow_ctastring
Text on the CTA button. For example: "Signup" Character limit - 20 characters (no emoji).
flow_actionstring
navigate or data_exchange.(Default value: navigate)
flow_action_payloadstring
Required if flow_action is navigate. Should be omitted otherwise.
Parameter
Description
screenstring
The ID of the first Screen.
dataobject
Optional input data for the first Screen of the Flow. If provided, this must be a non-empty object.
Send a POST call to the /messages endpoint.
Sample Request for Cloud API
{
"recipient_type": "individual",
"messaging_product": "whatsapp",
"to": "whatsapp-id",
"type": "interactive",
"interactive": {
"type": "flow",
"header": {
"type": "text",
"text": "Flow message header"
},
"body": {
"text": "Flow message body"
},
"footer": {
"text": "Flow message footer"
},
"action": {
"name": "flow",
"parameters": {
"flow_message_version": "3",
"flow_token": "AQAAAAACS5FpgQ_cAAAAAD0QI3s.",
"flow_id": "1",
"flow_cta": "Book!",
"flow_action": "navigate",
"flow_action_payload": {
"screen": "<SCREEN_NAME>",
"data": {
"product_name": "name",
"product_description": "description",
"product_price": 100
}
}
}
}
}
}
Sample Response
{
"contacts": [
{
"Input": "+447385946746",
"wa_id": "47385946746"
}
],
"messages": [
{
"id": "gHTRETHRTHTRTH-av4Y"
}
],
"meta": {
"api_status": "stable",
"version": "2.44.0.27"
}
}

Send Flow Template Message

You can send a template message with a WhatsApp Flow. Only published Flows can be sent this way. Use the new button type called FLOW. Use this type to specify the Flow to be sent with the template message.
First you need to create a template. Here is an example request:
{
"name": "example_template_name",
"language": "en_US",
"category": "MARKETING",
"components": [
{
"type": "body",
"text": "This is a flows as template demo"
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "FLOW",
"text": "Open flow!",
"flow_id": "<flow-id>",
"navigate_screen": "Flows Json screen name",
"flow_action": "navigate"
}
]
}
]
}'
Parameter
Description
buttons
flow_idstring
The unique ID of a Flow.
nagivate_screenstring
Required if flow_action is navigate. The unique ID of the Screen in the Flow.
flow_actionstring
Either navigate or data_exchange.(Default value: navigate)
Sample Response
{
"id": "<template-id>",
"status": "PENDING",
"category": "MARKETING"
}
Ensure that your template passes all required reviews so that status is APPROVED instead of PENDING. See Template Statuses.
Now you can send a template message with a Flow using the following request:
Sample Request for Cloud API
{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "PHONE_NUMBER",
"type": "template",
"template": {
"name": "TEMPLATE_NAME",
"language": {
"code": "LANGUAGE_AND_LOCALE_CODE"
},
"components": [
{
"type": "button",
"sub_type": "flow",
"index": "0",
"parameters": [
{
"type": "action",
"action": {
"flow_token": "FLOW_TOKEN", //optional, default is "unused"
"flow_action_data": {
...
} // optional, json object with the data payload for the first screen
}
}
]
}
]
}
}
Sample Response
{
"messaging_product": "whatsapp",
"contacts": [
{
"input": "<phone-number>",
"wa_id": "<phone-number>"
}
],
"messages": [
{
"id": "<message-id>"
}
]
}

Sending a WhatsApp Flow as user initiated message

{
"recipient_type": "individual",
"to": "whatsapp-id",
"type": "interactive",
"interactive": {
"type": "galaxy_message",
"header": {
"type": "text",
"text": "Flow message header"
},
"body": {
"text": "Flow message body"
},
"footer": {
"text": "Flow message footer"
},
"action": {
"name": "",
"parameters": {
"flow_message_version": "3",
"flow_token": "AQAAAAACS5FpgQ_cAAAAAD0QI3s.",
"flow_id": "1",
"flow_cta": "Book!",
"mode": "draft"
}
}
}
}

Receiving Flow Response

Upon flow completion a response message will be sent to the WhatsApp chat. You will receive it in the same way as you receive all other messages from the user - via message webhook. response_json field will contain flow-specific data.
Example Payload
{
"messages": [{
"context": {
"from": "16315558151",
"id": "gBGGEiRVVgBPAgm7FUgc73noXjo"
},
"from": "<USER_ACCOUNT_NUMBER>",
"id": "<MESSAGE_ID>",
"type": "interactive",
"interactive": {
"type": "nfm_reply",
"nfm_reply": {
"name": "flow",
"response_json": {
"flow_token": "<FLOW_TOKEN>",
"optional_param1": "<value1>",
"optional_param2": "<value2>"
}
}
},
"timestamp": "<MESSAGE_SEND_TIMESTAMP>"
}]
}

Best Practices

Here are some guidelines to optimize both the WhatsApp Flows user and developer experience.

Form Flows

Feedback Form Flow

The Feedback Form Flow can be used to collect customer feedback for an experience that includes purchase and delivery of a product. It can also easily be modified to collect other kinds of customer feedback as well.
This example tutorial breaks out the wrapper scaffolding code from the screen code. However, you should combine all the code in these examples using the Form Builder.
Wrapper Code
Include the following code to "wrap" the 2 screens of the Feedback Form (the General Satisfaction Questions screen, and the Rating Questions screen):
{
"version": "2.1",
"screens": [
/* screens we'll define in the next sections */
]
}
General Satisfaction Questions Screen:
{
"id": "screen_feedback_one",
"title": "Feedback 1 of 2",
"layout": {
"type": "SingleColumnLayout",
"children": [
{
"type": "Form",
"name": "form_feedback_one",
"children": [
{
"type": "TextSubheading",
"text": "Would you recommend us to a friend?"
},
{
"type": "RadioButtonsGroup",
"label": "Choose one:",
"name": "would_recommend",
"data-source": [
{
"id": "1",
"title": "Yes"
},
{
"id": "0",
"title": "No"
}
],
"required": true
},
{
"type": "TextSubheading",
"text": "How could we do better?"
},
{
"type": "TextArea",
"label": "Leave a comment",
"required": false,
"name": "how_to_do_better_comment"
},
{
"type": "Footer",
"label": "Continue",
"on-click-action": {
"name": "navigate",
"next": {
"type": "screen",
"name": "screen_feedback_two"
},
"payload": {
"would_recommend": "${form.would_recommend}",
"how_to_do_better": "${form.how_to_do_better_comment}"
}
}
}
]
}
]
}
}
The Form component organizes all the input fields on the page and sets the footer CTA to be disabled until all required fields have been completed.
RadioButtonsGroup and TextArea components are used to get input from the user.
You can decide for each input field whether it is required or not using the boolean “required” field.
Additional items to note here are the navigate action which is designed to navigate to the chosen screen. This example also passes a payload to the next screen - the form collects the would_recommend and how_to_do_better payload so it can be received as part of the user’s response.

Rating Questions Screen

Once you have the first screen in the Builder and modified as needed, the following can be pasted below it:
{
"id": "screen_feedback_two",
"title": "Feedback 2 of 2",
"data": {
"would_recommend": {
"type": "string",
"__example__": "1"
},
"how_to_do_better": {
"type": "string",
"__example__": "Have more color options"
}
},
"terminal": true,
"layout": {
"type": "SingleColumnLayout",
"children": [
{
"type": "Form",
"name": "form_feedback_two",
"children": [
{
"type": "TextSubheading",
"text": "Rate the following: "
},
{
"type": "Dropdown",
"label": "Purchase experience",
"required": true,
"name": "purchase_experience_dropdown",
"data-source": [
{
"id": "5",
"title": "★★★★★• Excellent (5/5)"
},
{
"id": "4",
"title": "★★★★☆• Good (4/5)"
},
{
"id": "3",
"title": "★★★☆☆• Average (3/5)"
},
{
"id": "2",
"title": "★★☆☆☆• Poor (2/5)"
},
{
"id": "1",
"title": "★☆☆☆☆• Very Poor (1/5)"
}
]
},
{
"type": "Dropdown",
"label": "Delivery and setup",
"required": true,
"name": "delivery_dropdown",
"data-source": [
{
"id": "5",
"title": "★★★★★• Excellent (5/5)"
},
{
"id": "4",
"title": "★★★★☆• Good (4/5)"
},
{
"id": "3",
"title": "★★★☆☆• Average (3/5)"
},
{
"id": "2",
"title": "★★☆☆☆• Poor (2/5)"
},
{
"id": "1",
"title": "★☆☆☆☆• Very Poor (1/5)"
}
]
},
{
"type": "Dropdown",
"label": "Customer service",
"required": true,
"name": "customer_service_dropdown",
"data-source": [
{
"id": "5",
"title": "★★★★★• Excellent (5/5)"
},
{
"id": "4",
"title": "★★★★☆• Good (4/5)"
},
{
"id": "3",
"title": "★★★☆☆• Average (3/5)"
},
{
"id": "2",
"title": "★★☆☆☆• Poor (2/5)"
},
{
"id": "1",
"title": "★☆☆☆☆• Very Poor (1/5)"
}
]
},
{
"type": "Footer",
"label": "Done",
"on-click-action": {
"name": "complete",
"payload": {
"purchase_experience_rating": "${form.purchase_experience_dropdown}",
"delivery_rating": "${form.delivery_dropdown}",
"customer_service_rating": "${form.customer_service_dropdown}",
"would_recommend": "${data.would_recommend}",
"how_to_do_better": "${data.how_to_do_better}"
}
}
}
]
}
]
}
}
The input data for the screen matches the payload of the navigate action from the previous screen, and the complete action terminates the flow (the screen is defined as terminal).
For each dropdown, the id of the option that the user selects is sent as part of the complete action payload. In addition, the inputs from the first screen, propagated through the second screen’s data, is also sent as part of the complete action payload. This way, the business will receive the entire feedback response from the user on the webhook.

Testing

Now that Flow development is complete, you should test it prior to publishing.
To test the Flow before publishing you can:
  • Use the interactive preview in the Meta Builder UI
Or
  • Send the Flow as a draft message using the Partner API
Once everything has been tested successfully, you can publish the Flow (via the Builder UI or the API) and start sending Feedback Form Flows to users.

Designing your Flow

Call-to-Actions (CTAs)

The CTA should always tell the user what will happen next or what task is being completed on each screen, for example Confirm booking.

Capitalization

Use sentence case on screen titles, headings, and CTAs. Use consistent capitalization throughout each flow.

Emojis

Always consider the context of the content when using emojis, such as: