Leverage Flow JSON to build your user experience.
To visualize the complete user experience, use the Builder. The Builder emulates the entire Flow experience and can be updated on the fly. To navigate to the Builder:
See this list of templates you can build with Flow JSON.
Flow JSON enables businesses to create workflows in WhatsApp by accessing the features of WhatsApp Flows using a custom JSON object.
These workflows are initiated and run entirely inside WhatsApp. It can include multiple screens, data flows, and response messages.
Flow JSON consists of the following sections:
Flow JSON Section | Description |
---|---|
Screen Data Model | Commands to define static types that power the screen. |
Screens | Used to compose layouts using standard UI library components. |
Components | Individual building blocks that make up a screen (text fields, buttons, and so on). |
Routing Model | Defines the rules for the screen by limiting the possible state transition. For example, developers can define that from Screen 1 you can only transition to Screen 2 and 3, but not Screens 4 and 5. These rules are used during server / client side payload validations. |
Actions | Special type of syntax to invoke pre-defined logic on the client. All actions are either "navigate" or "complete." |
Flow JSON defines the following:
Flow JSON has several required and optional properties that are used in the process of compilation and validation of the Flow.
version
- represents the version of Flow JSON to use during the compilation. Please refer to the list of versions for more details.
screens
- represents an array or screen as part of the user experience. This is like a set of different pages on your website.
data_api_version
- represents the version to use during communication with the WhatsApp Flows Data Endpoint. Currently, it is 3.0.
If the Flow uses the data-channel capability, the validation system will ask to provide this property. { "version": "2.1", "screens": [...] }
Screens are the main unit of a Flow. Each screen represents a single node in the state machine you define. These properties then make up the Flows screen property model:
"screen" : { "id": string, "terminal": ?boolean, "title": ?string, "refresh_on_back": ?boolean, "data": ?object, "layout": object }
id
- unique identifier of the screen which works as a page url. SUCCESS is a reserved keyword and should not be used as a screen id.
layout
- associated screen UI Layout that is shown to the user. Layout can be predefined or it can represent a container with fully customizable content built using WhatsApp Flows Library.
terminal
(optional) - the business flow is the end state machine. It means that each Flow should have a terminal state where we terminate the experience and have the Flow completed. Multiple screens can be marked as terminal.
data
(optional) - declaration of dynamic data that fills the components field in the Flow JSON. It uses JSON Schema to define the structure and type of the properties. Below you can find the simple example.
{ "data": { "first_name": { "type": "string", "__example__": "John" } } }
title
(optional) - screen level attribute that is rendered in the top navigation bar.refresh_on_back
(optional) - By default, it is always set to false
Layout represents screen UI Content. It can be predefined by the WhatsApp Flows team, or the business can use empty containers and build custom experience using the WhatsApp Flows Library.
Layout has the following properties:
type
- the layout identifier that’s used in the template. In the current version of Flow JSON, there is only one layout available - "SingleColumnLayout"
which represents a vertical flexbox container.
children
- represents an array of components from the WhatsApp Flows Library.
Routing model configuration is needed only when you use an Endpoint to power your Flow.
You can define the routing model, which is a directed graph, as each screen can go to multiple other screens. There can be up to a maximum of 10 "branches", or connections, within the routing model.
Consider the following:
The following routing model can be built:
Welcome screen => [Select car model]
Select car mode => [Pick date]
Pick date => [Select car mode, Confirm test drive]
Confirm test drive => []
If you don't use an Endpoint, you don't need to define a routing model, it will be generated automatically.
However, if you want to use a server to power your Flow, you'll have to provide a routing_model
in your Flow JSON.
Routes are defined per screen via the routing_model
property. It is a map of screen ids to an array of other screen ids it can transition to. The terminal screen is defined with terminal=true
.
Route cannot be the current screen, but the route can be "refreshed" for validation purposes.
If there is an edge between two screens, then the user can go back and forth between them using the BACK button.
Only forward routes should be specified in the routing model. For example, if you have specified an edge from Screen_A to Screen_B then you shouldn't specify another edge from Screen_B to Screen_A.
Routes can be empty for a screen if there is no forward route from it.
All routes must end at the terminal screen.
In the example below, there is a simple 3-screen Flow that uses an Endpoint. It is expected that the server will return the next screen with a response to data_exchange
action. The server has to comply with defined routing_model
in the Flow JSON:
{ "version": "2.1", "data_api_version": "3.0", "data_channel_uri": "https://whatsapp.com", "routing_model": { "MY_FIRST_SCREEN": [ "MY_SECOND_SCREEN" ], "MY_SECOND_SCREEN": [ "MY_THIRD_SCREEN" ] }, "screens": [ { "id": "MY_FIRST_SCREEN", "title": "First Screen", "layout": { "type": "SingleColumnLayout", "children": [ { "type": "Footer", "label": "Continue", "on-click-action": { "name": "data_exchange", "payload": {} } } ] } }, { "id": "MY_SECOND_SCREEN", "title": "Second Screen", "data": {}, "layout": { "type": "SingleColumnLayout", "children": [ { "type": "Footer", "label": "Continue", "on-click-action": { "name": "data_exchange", "payload": {} } } ] } }, { "id": "MY_THIRD_SCREEN", "title": "Third Screen", "terminal": true, "data": {}, "layout": { "type": "SingleColumnLayout", "children": [ { "type": "Footer", "label": "Continue", "on-click-action": { "name": "data_exchange", "payload": {} } } ] } } ] }
Properties can be static
or dynamic
. In Flow JSON the property is static if it is not a type binded to a data
or form
object.
Static properties are simple. You set static properties once and they never change.
Here is an example (see text
and label
properties of TextHeading
and Footer
components). Static properties is the simplest way to start building your Flow. You can always replace them later with dynamic content.
Dynamic properties enables you to set the content dynamically based on the server / screen data via the dynamic data reference mechanism, like so: "${data.username}"
. If you attempt to use the dynamic and static variant of the property together, you will get a compilation error. The dynamic data reference mechanism works with the following data types:
You can dynamically reference these data types in all the components of Flow JSON. There are two types of dynamic properties:
"${form.field_name}"
(data entered by the user in input fields). This is used to provide access to information that the user entered on the screen. "${data.field_name}"
(data provided for the screen). This is used to provide access to information that is passed down by the server or the navigate
action from the previous screen.If a screen expects dynamic data, declare it inside the data
property. Data declaration uses the standard JSON Schema. A simple Flow example would replace text
with dynamic data coming from the message payload:
A few things have been added:
Inside MY_FIRST_SCREEN
we declared a data field
Inside the data field we declared hello_world_text
. This is the data that we expect to receive for screen.
hello_world_text
follows the JSON Schema specification to declare the expected type, in this example it is a string.__example__
field serves as mock data for the template, which is useful while you’re developing your template without WhatsApp Flows Data Endpoint integration. This field is mandatory.In TextHeading
we’ve referenced the data via dynamic data reference syntax. ${data} represents an object that came from the WhatsApp Flows Data Endpoint or navigate
actions in case of Flow without endpoint. You can treat it as a screen state that was set after the response is received.
Property of the state can be accessed using the following pattern - "${data.property_name}"
If you want to power the screen by endpoint data, the example above will slightly change.
data_api_version
, routing_model
and data_channel_uri
to indicate that the Flow is connected to serverhello_world_text
field.To get and submit the data entered from users, Flow JSON uses a straightforward concept from HTML - Forms.
HTML Form example:
<form> <label for="first_name">First name</label><br> <input type="text" id="first_name" name="first_name"><br> <label for="last_name">Last name</label><br> <input type="text" id="last_name" name="last_name"> <input type="radio" id="html" name="fav_language" value="HTML"> <label for="html">HTML</label><br> <input type="radio" id="css" name="fav_language" value="CSS"> <label for="css">CSS</label><br> <input type="radio" id="javascript" name="fav_language" value="JavaScript"> <label for="javascript">JavaScript</label> </form>
This form can also be implemented in Flow JSON as follows:
Using the example above, we can reference form properties using a "${form.field_name}"
binding. This type of binding uses the name
property of the interactive inputs to reference its value. You can use form values to submit the data to a Flow data server or pass it to another screen.
{ "type": "Footer", "label": "Submit data", "on-click-action": { "name": "navigate", "next": { "type": "screen", "name": "NEXT_SCREEN" }, "payload": { "name": "${form.first_name}", "lang": "${form.favourite_language}" } } }
{ "type": "Footer", "label": "Submit data", "on-click-action": { "name": "data_exchange", "payload": { "name": "${form.first_name}", "lang": "${form.favourite_language}" } } }
In order to build Forms in Flow JSON you need to use Form components then provide the name
and children
properties
Children properties must be an array of Form components
Each Form component has its own property model, however the name
property is required in all of them
Interactive components cannot be used outside forms.
Component | Can Be Used Outside Forms? |
---|---|
Text (TextHeading, TextSubheading, TextCaption, TextBody) | ✅ |
TextInput | ❌ |
TextArea | ❌ |
CheckboxGroup | ❌ |
RadioButtonsGroup | ❌ |
Footer | ✅ |
OptIn | ❌ |
Dropdown | ❌ |
EmbeddedLink | ✅ |
DatePicker | ❌ |
Initial values of inputs can be initiased using init-values
property. error-messages
property allows you to set custom error for input. This is useful when you use Flow Data Endpoint to receive user data and you want to indicate that certain fields are incorrect.
Attribute | Description |
---|---|
init-values | <key, value> object where key - Field Name in Component value - Field Initial Value type - String, Array< String > or Dynamic |
| <key, value> object where key - Field Name in Component value - Error Message type - String or Dynamic |
You set init-values
by specifying the field name in the respective component, then mapping it to your desired value.
The data type for init-values
must match that of the component as outlined below.
Component | init-values data type
|
---|---|
CheckboxGroup | Array of Strings |
RadioButtonsGroup | String |
Text Entry | String |
Dropdown | String |
For example, if you have the field first_name
in one TextInput
component, the field second_name
in another TextInput
component you would set the init-values
like so:
Flow JSON provides a generic way to trigger asynchronous actions handled by a client through interactive UI elements. A limited set of actions are described below:
Flow JSON Reference | Description | Payload Type |
---|---|---|
| Sending Data to WhatsApp Flows Data Endpoint | Customizable JSON payload on data exchanges { [key:string]: any } |
| Triggers the next screen with the payload as its input. The CTA button will be disabled until the payload with data required for the next screen is supplied. When | Static JSON payload |
| Accepts the termination parameters upon completion of a Flow | Static JSON payload |
navigate
actionThis action is a primary way to navigate between the screens on the Flow. The data that's passed as payload
of this action will be available on the next screen through dynamic data referencing - ${data.field_name}
{ "type": "Footer", "label": "Submit data", "on-click-action": { "name": "navigate", "next": { "type": "screen", "name": "NEXT_SCREEN" }, "payload": { "name": "${form.first_name}", "lang": "${form.favourite_language}" } } }
complete
actionTerminates 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. More information can be found here.
terminal
screen as a last interaction of the user. Once triggered, the Flow will be terminated and entered data will be submitted via webhook. { "type": "Footer", "label": "Submit data", "on-click-action": { "name": "complete", "payload": { "discount_code": "${data.discount_code}", "items": "${form.selected_items}" } } }
navigate
and complete
actionsdata_exchange
actionSends data to WhatsApp Flows Data Endpoint.
{ "type": "Footer", "label": "Submit data", "on-click-action": { "name": "data_exchange", "payload": { "discount_code": "${data.discount_code}", "items": "${form.selected_items}" } } }
A comprehensive list of components with code examples is available here.
Flow JSON content string is limited and cannot exceed 10 MB.