Flows API
Create, manage, and deploy WhatsApp Flows - interactive forms that enable rich data collection experiences within WhatsApp conversations.
Official Documentation: WhatsApp Flows API
Overview
WhatsApp Flows allow you to build multi-screen interactive experiences for data collection:
- Create Flows: Define interactive form structures
- Upload JSON: Define flow screens and components
- Publish Flows: Make flows available for use
- Manage Lifecycle: Update, deprecate, and delete flows
- Migrate Flows: Transfer flows between WABAs
Endpoints
GET /{WABA_ID}/flowsPOST /{WABA_ID}/flowsGET /{FLOW_ID}?fields&date_formatPOST /{FLOW_ID}GET /{FLOW_ID}/assetsPOST /{FLOW_ID}/assetsPOST /{FLOW_ID}/publishPOST /{FLOW_ID}/deprecateDELETE /{FLOW_ID}POST /{DESTINATION_WABA_ID}/migrate_flowsImportant Notes
Quick Start
import WhatsApp, { FlowCategoryEnum } from 'meta-cloud-api';
const client = new WhatsApp({ accessToken: process.env.CLOUD_API_ACCESS_TOKEN!, phoneNumberId: Number(process.env.WA_PHONE_NUMBER_ID), businessAcctId: process.env.WA_BUSINESS_ACCOUNT_ID!,});
// Create a flowconst flow = await client.flows.createFlow('WABA_ID', { name: 'customer_survey', categories: [FlowCategoryEnum.Survey], endpoint_uri: 'https://example.com/flow-webhook', publish: false,});
// Upload flow JSONconst flowJson = { version: '5.0', screens: [ { id: 'QUESTION', title: 'Customer Survey', data: {}, layout: { type: 'SingleColumnLayout', children: [ { type: 'TextInput', name: 'feedback', label: 'How was your experience?', required: true, }, ], }, }, ],};
const jsonFile = new File( [JSON.stringify(flowJson)], 'flow.json', { type: 'application/json' });
await client.flows.uploadFlowJson(flow.id, jsonFile);
// Publish the flowawait client.flows.publishFlow(flow.id);Create Flow
Create a new flow in draft mode.
Basic Flow Creation
import { FlowCategoryEnum } from 'meta-cloud-api/enums';
const flow = await client.flows.createFlow('WABA_ID', { name: 'appointment_booking', categories: [FlowCategoryEnum.Appointment], endpoint_uri: 'https://api.example.com/flows/appointment', publish: false, // Keep as draft});Flow with Multiple Categories
const flow = await client.flows.createFlow('WABA_ID', { name: 'multi_purpose_form', categories: [ FlowCategoryEnum.LeadGeneration, FlowCategoryEnum.Survey, ], endpoint_uri: 'https://api.example.com/flows/handler', publish: false,});List Flows
Retrieve all flows for your WABA.
const flows = await client.flows.listFlows('WABA_ID');
console.log(flows.data);// [// { id: '...', name: 'customer_survey', status: 'PUBLISHED' },// { id: '...', name: 'order_form', status: 'DRAFT' }// ]Get Flow Details
Retrieve detailed information about a specific flow.
Get Basic Info
const flow = await client.flows.getFlow('FLOW_ID');
console.log(flow);// {// id: 'FLOW_ID',// name: 'customer_survey',// status: 'PUBLISHED',// categories: ['SURVEY'],// validation_errors: []// }Get Specific Fields
const flow = await client.flows.getFlow('FLOW_ID', { fields: ['name', 'status', 'validation_errors', 'json_version'],});Update Flow Metadata
Update flow name, categories, or endpoint.
await client.flows.updateFlowMetadata('FLOW_ID', { name: 'updated_survey_form', categories: [FlowCategoryEnum.Survey, FlowCategoryEnum.Other], endpoint_uri: 'https://api.example.com/new-endpoint',});Upload Flow JSON
Define flow screens and components using JSON.
Create Flow JSON File
const flowDefinition = { version: '5.0', data_api_version: '3.0', routing_model: {}, screens: [ { id: 'CONTACT_FORM', title: 'Contact Information', data: {}, terminal: true, layout: { type: 'SingleColumnLayout', children: [ { type: 'TextInput', name: 'name', label: 'Full Name', 'input-type': 'text', required: true, }, { type: 'TextInput', name: 'email', label: 'Email Address', 'input-type': 'email', required: true, }, { type: 'TextArea', name: 'message', label: 'Your Message', required: true, }, { type: 'Footer', label: 'Submit', 'on-click-action': { name: 'complete', payload: { name: '${form.name}', email: '${form.email}', message: '${form.message}', }, }, }, ], }, }, ],};
// Convert to Fileconst jsonFile = new File( [JSON.stringify(flowDefinition)], 'flow.json', { type: 'application/json' });
// Uploadawait client.flows.uploadFlowJson('FLOW_ID', jsonFile);Multi-Screen Flow
const multiScreenFlow = { version: '5.0', screens: [ { id: 'WELCOME', title: 'Welcome', data: {}, layout: { type: 'SingleColumnLayout', children: [ { type: 'TextHeading', text: 'Welcome to our survey!', }, { type: 'Footer', label: 'Start', 'on-click-action': { name: 'navigate', next: { type: 'screen', name: 'QUESTIONS' }, }, }, ], }, }, { id: 'QUESTIONS', title: 'Questions', data: {}, terminal: true, layout: { type: 'SingleColumnLayout', children: [ { type: 'RadioButtonsGroup', name: 'rating', label: 'How would you rate us?', 'data-source': [ { id: '5', title: 'Excellent' }, { id: '4', title: 'Good' }, { id: '3', title: 'Average' }, { id: '2', title: 'Poor' }, ], required: true, }, { type: 'Footer', label: 'Submit', 'on-click-action': { name: 'complete', payload: { rating: '${form.rating}' }, }, }, ], }, }, ],};
const file = new File( [JSON.stringify(multiScreenFlow)], 'flow.json', { type: 'application/json' });
await client.flows.uploadFlowJson('FLOW_ID', file);Get Flow Assets
Retrieve uploaded flow JSON.
const assets = await client.flows.getFlowAssets('FLOW_ID');
console.log(assets);// {// data: [// {// name: 'flow.json',// asset_type: 'FLOW_JSON',// download_url: 'https://...'// }// ]// }Publish Flow
Make a draft flow available for use in messages.
await client.flows.publishFlow('FLOW_ID');
// Verify publicationconst flow = await client.flows.getFlow('FLOW_ID');console.log(flow.status); // 'PUBLISHED'Deprecate Flow
Mark a published flow as deprecated (still usable but not recommended).
await client.flows.deprecateFlow('FLOW_ID');Delete Flow
Permanently delete a flow.
await client.flows.deleteFlow('FLOW_ID');Migrate Flows
Copy flows from one WABA to another.
await client.flows.migrateFlows('DESTINATION_WABA_ID', { flow_ids: ['FLOW_ID_1', 'FLOW_ID_2'],});Send Flow Messages
Use flows in interactive messages.
await client.messages.interactive({ to: '15551234567', type: 'flow', body: { text: 'Please fill out our survey', }, action: { name: 'flow', parameters: { flow_message_version: '3', flow_token: 'UNIQUE_FLOW_TOKEN', flow_id: 'FLOW_ID', flow_cta: 'Start Survey', flow_action: 'navigate', flow_action_payload: { screen: 'WELCOME', }, }, },});Handle Flow Webhooks
Process flow data submissions on your webhook endpoint.
// Express.js exampleapp.post('/flow-webhook', async (req, res) => { const { flow_token, action, data } = req.body;
if (action === 'ping') { // Health check return res.json({ version: '3.0' }); }
if (action === 'INIT') { // Initialize flow with data return res.json({ version: '3.0', data: { // Initial data for the flow }, }); }
if (action === 'data_exchange') { // Process form submission const formData = data;
// Save to database await saveFormData(formData);
return res.json({ version: '3.0', data: { // Updated data }, }); }
res.status(400).json({ error: 'Unknown action' });});Response Formats
Create Flow Response
{ id: 'FLOW_ID', name: 'customer_survey', status: 'DRAFT', categories: ['SURVEY'], validation_errors: []}List Flows Response
{ data: [ { id: 'FLOW_ID_1', name: 'survey', status: 'PUBLISHED' }, { id: 'FLOW_ID_2', name: 'booking', status: 'DRAFT' } ]}Error Handling
try { await client.flows.publishFlow('FLOW_ID');} catch (error) { if (error.response) { const { code, message } = error.response.data.error;
switch (code) { case 100: console.error('Flow has validation errors'); break; case 2388153: console.error('Webhook endpoint unreachable'); break; default: console.error(`Error ${code}: ${message}`); } }}Best Practices
-
Test in Draft Mode: Thoroughly test flows before publishing
// Keep publish: false during developmentconst flow = await client.flows.createFlow('WABA_ID', {name: 'test_flow',publish: false,}); -
Use Unique Flow Tokens: Generate unique tokens for tracking
import { v4 as uuidv4 } from 'uuid';const flowToken = uuidv4(); // Unique per conversation -
Validate JSON Before Upload: Check flow JSON structure
const flowJson = JSON.parse(flowString);if (!flowJson.version || !flowJson.screens) {throw new Error('Invalid flow JSON structure');} -
Handle Webhook Errors: Implement proper error handling
app.post('/flow-webhook', async (req, res) => {try {const result = await processFlowData(req.body);res.json(result);} catch (error) {console.error('Flow webhook error:', error);res.status(500).json({ error: 'Processing failed' });}});
Flow Categories
SIGN_UP: User registrationSIGN_IN: User authenticationAPPOINTMENT: Booking appointmentsLEAD_GENERATION: Collecting leadsCONTACT_US: Contact formsCUSTOMER_SUPPORT: Support requestsSURVEY: Feedback collectionOTHER: General purpose
Related Documentation
- Messages API - Send flow messages
- Interactive Messages - Build interactive experiences
- Webhooks - Handle flow submissions
- Flow Builder - Official Flow documentation
Source Code
View the source code on GitHub: FlowApi.ts