WhatsApp Flows over 8x8 API
This API reference provides complete technical documentation for developers implementing WhatsApp Flows programmatically.
Overview
The WhatsApp Flows API enables you to create, manage, publish, and send interactive forms within WhatsApp conversations. Use the API when you need:
- Automated deployments from CI/CD pipelines
- Programmatic flow management across multiple channels
- Version control integration for Flow definitions
- Custom tooling and workflow automation
- Bulk operations at scale
For non-developers: If you prefer a visual, no-code interface, see the WhatsApp Flows in 8x8 Connect.
Prerequisites
Before using the API, ensure you have:
- Active WhatsApp Channel in 8x8 Connect
- Channel ID - Find in 8x8 Connect Portal under Channels
- API Key - Generate in 8x8 Connect Portal under API Keys
- Subaccount ID - Required for sending messages
- Webhook endpoint - Configured to receive Flow submissions
Authentication
All API requests require Bearer token authentication using your API Key.
Header:
Authorization: Bearer YOUR_API_KEY
Example:
curl -X GET "https://chatapps.8x8.com/config/v1/channels/whatsapp/{channelId}/flows" \
-H "Authorization: Bearer YOUR_API_KEY"
Flow Management APIs
Base URL: https://chatapps.8x8.com/config/v1/channels/whatsapp/{channelId}/flows
API Endpoints Overview
| Action | Method | Endpoint | Description |
|---|---|---|---|
| List Flows | GET | /flows | Retrieves all Flows for a specified channel |
| Create Flow | POST | /flows | Creates a new Flow in DRAFT status |
| Get Flow | GET | /flows/{flowId} | Retrieves Flow details and configuration |
| Update Flow | PUT | /flows/{flowId} | Updates a Flow (DRAFT only) |
| Delete Flow | DELETE | /flows/{flowId} | Deletes a Flow (DRAFT only) |
| Publish Flow | POST | /flows/{flowId}/publish | Publishes Flow for use in messages |
| Deprecate Flow | POST | /flows/{flowId}/deprecate | Marks Flow as deprecated |
| Preview Flow | GET | /flows/{flowId}/preview | Generates preview URL for testing |
Create Flow
Upload a new Flow definition to your WhatsApp channel.
Endpoint: POST /config/v1/channels/whatsapp/{channelId}/flows
Request Body:
{
"name": "Appointment Booking Flow",
"categories": ["APPOINTMENT_BOOKING"],
"flowJson": "{\"version\":\"7.3\",\"screens\":[{\"id\":\"WELCOME\",\"title\":\"Book Appointment\",\"terminal\":true,\"success\":true,\"layout\":{\"type\":\"SingleColumnLayout\",\"children\":[{\"type\":\"TextHeading\",\"text\":\"Schedule Your Visit\"},{\"type\":\"DatePicker\",\"name\":\"appointment_date\",\"label\":\"Preferred Date\",\"required\":true},{\"type\":\"Footer\",\"label\":\"Confirm\",\"on-click-action\":{\"name\":\"complete\",\"payload\":{\"date\":\"${form.appointment_date}\"}}}]}}]}"
}
Parameters:
name(string, required): Human-readable Flow name for your referencecategories(array, required): Flow categories. Options:["OTHER"]- General purpose["LEAD_GENERATION"]- Lead capture["CONTACT_US"]- Contact forms["CUSTOMER_SUPPORT"]- Support requests["SIGN_UP"]- Registration["APPOINTMENT_BOOKING"]- Appointments["SURVEY"]- Feedback collection
flowJson(string, required): Stringified JSON of your Flow definition
Important: The flowJson field must be a JSON string, not a JSON object. Use JSON.stringify() to properly escape your Flow definition.
Response (201 Created):
{
"id": "1234567890123456",
"name": "Appointment Booking Flow",
"status": "DRAFT",
"categories": ["APPOINTMENT_BOOKING"],
"validation_errors": []
}
Flow Status Values:
DRAFT- Editable, not usable in messagesPUBLISHED- Live, immutable, usable in messagesDEPRECATED- Published but no longer recommended
Example with curl:
curl -X POST "https://chatapps.8x8.com/config/v1/channels/whatsapp/YOUR_CHANNEL_ID/flows" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Appointment Booking Flow",
"categories": ["APPOINTMENT_BOOKING"],
"flowJson": "{\"version\":\"7.3\",\"screens\":[...]}"
}'
Update Flow
Modify an existing Flow definition. Only works for Flows in DRAFT status.
Endpoint: PUT /config/v1/channels/whatsapp/{channelId}/flows/{flowId}
Request Body: Same structure as Create Flow
Response (200 OK): Same as Create Flow response
Note Published Flows cannot be updated. To modify a published Flow:
- Create a new Flow with the updated definition
- Publish the new Flow
- Optionally deprecate the old Flow
- Update templates to use the new Flow ID
Get Flow
Retrieve details and JSON definition of a specific Flow.
Endpoint: GET /config/v1/channels/whatsapp/{channelId}/flows/{flowId}
Response (200 OK):
{
"id": "1234567890123456",
"name": "Appointment Booking Flow",
"status": "PUBLISHED",
"categories": ["APPOINTMENT_BOOKING"],
"validation_errors": [],
"flowJson": "{\"version\":\"7.3\",\"screens\":[...]}"
}
Use Cases:
- Verify Flow status before sending
- Export Flow JSON for version control
- Audit existing Flows
Delete Flow
Permanently delete a Flow. Only works for Flows in DRAFT status.
Endpoint: DELETE /config/v1/channels/whatsapp/{channelId}/flows/{flowId}
Response (204 No Content)
Important:
- Cannot delete published Flows
- Cannot delete Flows currently in use by templates
- Deletion is permanent and cannot be undone
Publish Flow
Publish a Flow to make it available for use in messages. This action is irreversible.
Endpoint: POST /config/v1/channels/whatsapp/{channelId}/flows/{flowId}/publish
Response (200 OK):
{
"id": "1234567890123456",
"name": "Appointment Booking Flow",
"status": "PUBLISHED"
}
After Publishing:
- Flow becomes immutable - no edits allowed
- Flow can be used in templates and interactive messages
- Flow status changes from
DRAFTtoPUBLISHED
Deprecate Flow
Mark a Flow as deprecated. It remains functional but is flagged as no longer recommended.
Endpoint: POST /config/v1/channels/whatsapp/{channelId}/flows/{flowId}/deprecate
Response (200 OK):
{
"id": "1234567890123456",
"name": "Appointment Booking Flow",
"status": "DEPRECATED"
}
Use Cases:
- Phasing out old Flow versions
- Migrating users to improved Flows
- Maintaining backward compatibility while promoting new versions
Preview Flow
Generate a preview URL to test the Flow before publishing.
Endpoint: GET /config/v1/channels/whatsapp/{channelId}/flows/{flowId}/preview?refresh=false
Query Parameters:
refresh(boolean):false- Use cached preview (faster)true- Generate new preview with latest changes
Response (200 OK):
{
"preview_url": "https://business.facebook.com/wa/manage/flows/1234567890123456/preview/",
"expires_at": "2026-02-05T18:00:00Z"
}
Preview URL:
- Opens WhatsApp Business Manager
- Shows interactive Flow preview
- Expires after set period (usually 48 hours)
- Use
refresh=trueto generate new URL after expiry
Sending Flow Messages
Flows can be sent via two methods:
- Template messages - Business-initiated (proactive outreach)
- Interactive messages - User-initiated (within 24-hour window)
Via Template Message (Business-Initiated)
Template messages with Flow buttons allow you to initiate conversations or re-open closed service windows.
Use Cases:
- Proactive appointment reminders
- Feedback request campaigns
- Re-engagement after window closes
- Marketing with data collection
Step 1: Create Template with Flow Button
Template Structure:
Templates must be created and approved by WhatsApp before use. See Template Message API Library for complete documentation.
Flow Button in Template:
{
"language": "en_US",
"name": "appointment_reminder",
"category": "UTILITY",
"components": [
{
"type": "BODY",
"text": "Hi {{1}}, your appointment is coming up. Need to reschedule?"
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "FLOW",
"text": "Reschedule",
"flowId": "1234567890123456"
}
]
}
]
}
Step 2: Send Template Message
Endpoint: POST https://chatapps.8x8.com/api/v1/subaccounts/{subAccountId}/messages
Request:
{
"user": {
"msisdn": "+15551234567"
},
"type": "template",
"content": {
"template": {
"language": "en_US",
"name": "appointment_reminder",
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"text": "John"
}
]
},
{
"type": "button",
"subType": "flow",
"index": 0,
"parameters": [
{
"type": "action"
}
]
}
]
}
}
}
Key Points:
flowIdis configured in the template, not in the send request- Flow button uses
subType: "flow"in the button component - User taps button to open the Flow
Via Interactive Message (User-Initiated)
Interactive Flow messages are used within the 24-hour customer service window after a user sends you a message.
Use Cases:
- Response to customer inquiry
- Agent-initiated data collection during support chat
- Follow-up after template interaction
Endpoint: POST https://chatapps.8x8.com/api/v1/subaccounts/{subAccountId}/messages
Interactive Flow Structure:
{
"user": {
"msisdn": "+15551234567"
},
"type": "Interactive",
"content": {
"interactive": {
"type": "flow",
"header": {
"type": "text",
"text": "Schedule Your Appointment"
},
"body": {
"text": "Please select your preferred date and time for your visit."
},
"footer": {
"text": "Available Mon-Fri 9AM-5PM"
},
"action": {
"parameters": {
"flowId": "1234567890123456",
"flowCta": "Book Now"
}
}
}
}
}
Parameters:
-
flowId(required): The published Flow ID -
flowCta(required): Button text (max 20 characters) -
flowToken(optional): Custom token passed to webhook -
mode(optional):"draft"for testing unpublished flows -
flowMessageVersion(optional):"3"for latest features -
flowActionData(optional): Pre-populate Flow data"flowActionData": {
"screen": "SCREEN_ID",
"data": {
"field_name": "pre-filled value"
}
}
Service Window Requirement:
- Can only send within 24 hours after user's last message
- Outside window, use Template message instead
- See WhatsApp Conversation Windows
Complete Example:
curl -X POST "https://chatapps.8x8.com/api/v1/subaccounts/YOUR_SUBACCOUNT_ID/messages" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"user": {
"msisdn": "+15551234567"
},
"type": "Interactive",
"content": {
"interactive": {
"type": "flow",
"body": {
"text": "Complete our quick survey to help us improve."
},
"action": {
"parameters": {
"flowId": "1234567890123456",
"flowCta": "Start Survey"
}
}
}
}
}'
Webhook Integration
When users complete a Flow, submissions are delivered to your webhook as inbound messages.
Webhook Configuration
Setup in 8x8 Connect Portal:
- Navigate to Messaging Apps > Webhooks
- Add your endpoint URL (must be HTTPS)
- Select event type:
inbound_message_received - Save configuration
Complete webhook setup guide: WhatsApp Webhooks Documentation
Flow Submission Payload
Flow submissions arrive as inbound_message_received events with type nfmReply.
Identifying Flow Submissions:
if (payload.type === "Interactive" &&
payload.content.interactive.type === "nfmReply") {
// This is a Flow submission
}
Complete Payload Structure:
{
"namespace": "ChatApps",
"eventType": "inbound_message_received",
"payload": {
"umid": "unique-message-id-abc123",
"subAccountId": "your-subaccount-id",
"timestamp": "2026-02-03T10:30:00.000Z",
"user": {
"msisdn": "+15551234567",
"channelUserId": "15551234567"
},
"recipient": {
"channel": "whatsapp",
"channelId": "your-channel-id"
},
"type": "Interactive",
"content": {
"interactive": {
"type": "nfmReply",
"nfmReply": {
"responseJson": "{\"appointment_date\":\"2026-03-15\",\"service_type\":\"plumbing\",\"flow_token\":\"<TOKEN>\"}"
}
}
}
}
}
Parsing Submission Data
The responseJson field contains a JSON string (not a parsed object). You must parse it to access form data.
Step 1: Extract the JSON String
const responseJsonString = payload.content.interactive.nfmReply.responseJson;
Step 2: Parse to Object
const submissionData = JSON.parse(responseJsonString);
Step 3: Validate and Process
// Validate required fields
if (!submissionData.appointment_date || !submissionData.service_type) {
console.error('Missing required fields');
return res.status(200).send('OK'); // Always return 200 to acknowledge receipt
}
// Process the data
await createAppointment({
customerPhone: payload.user.msisdn,
appointmentDate: submissionData.appointment_date,
serviceType: submissionData.service_type,
flowToken: submissionData.flow_token,
umid: payload.umid // For idempotency tracking
});
// Send confirmation message
await sendMessage({
user: { msisdn: payload.user.msisdn },
type: 'text',
content: {
text: `Appointment confirmed for ${submissionData.appointment_date}. We'll send a reminder 24 hours before your visit.`
}
});
// Return 200 OK within 5 seconds
res.status(200).send('OK');
Webhook Response Requirements
Your webhook endpoint must:
- Respond with 200 OK within 5 seconds
- Process asynchronously if operations take longer
- Handle duplicates using
umidfor idempotency - Validate input before processing
- Log errors without throwing exceptions
Example Webhook Handler:
const express = require('express');
const app = express();
app.post('/webhook', express.json(), async (req, res) => {
const { payload } = req.body;
// Acknowledge receipt immediately
res.status(200).send('OK');
// Process asynchronously
try {
if (payload.type === 'Interactive' &&
payload.content.interactive.type === 'nfmReply') {
// Parse Flow submission
const data = JSON.parse(payload.content.interactive.nfmReply.responseJson);
// Check for duplicate using umid
if (await isProcessed(payload.umid)) {
console.log('Duplicate submission, skipping');
return;
}
// Mark as processed
await markProcessed(payload.umid);
// Process submission
await handleFlowSubmission(data, payload);
}
} catch (error) {
console.error('Error processing webhook:', error);
// Log error but don't throw - webhook already acknowledged
}
});
app.listen(3000);
Response Handling Best Practices
- Validate input: Always validate submission data schema
- Handle errors gracefully: Log parsing errors without crashing
- Respond quickly: Acknowledge within 5 seconds
- Store metadata: Save
umid,subAccountId,flow_tokenfor traceability - Implement idempotency: Check
umidto prevent duplicate processing - Send confirmation: Reply to user confirming successful submission
- Monitor performance: Track webhook response times and error rates
Error Handling
HTTP Error Codes
| Code | Meaning | Common Causes |
|---|---|---|
| 400 Bad Request | Invalid request format | Malformed JSON, missing required fields, invalid flowJson structure |
| 401 Unauthorized | Authentication failed | Invalid or expired API key |
| 404 Not Found | Resource doesn't exist | Invalid channelId or flowId |
| 409 Conflict | Operation not allowed | Trying to update/delete published Flow |
| 422 Unprocessable Entity | Validation failed | Invalid Flow JSON schema, unsupported components |
| 429 Too Many Requests | Rate limit exceeded | Slow down request rate |
| 500 Internal Server Error | Server error | Temporary issue, retry with exponential backoff |
Common API Issues
| Error Message | Cause | Solution |
|---|---|---|
Flow not found | Invalid or deleted Flow ID | Verify Flow exists with GET /flows/{flowId} |
Flow must be in DRAFT status | Attempting to modify published Flow | Create new Flow version instead |
Invalid flowJson | JSON structure doesn't match schema | Validate against WhatsApp Flows schema |
flowJson must be a string | Passed JSON object instead of string | Use JSON.stringify() to escape |
Flow is not published | Trying to send unpublished Flow | Publish Flow first with POST /publish |
Outside service window | Interactive message sent >24hrs | Use Template message instead |
Debugging API Errors
1. Validate Flow JSON Structure
Before uploading, validate your Flow definition:
// Validate structure
const flowDef = {
version: "7.3",
screens: [
{
id: "SCREEN1",
title: "Welcome",
terminal: true,
success: true,
layout: { /* ... */ }
}
]
};
// Test parsing
try {
const flowJson = JSON.stringify(flowDef);
JSON.parse(flowJson); // Should succeed
console.log('Flow JSON is valid');
} catch (error) {
console.error('Invalid JSON:', error);
}
2. Check Flow Status
Verify Flow is in correct state:
curl -X GET "https://chatapps.8x8.com/config/v1/channels/whatsapp/{channelId}/flows/{flowId}" \
-H "Authorization: Bearer YOUR_API_KEY"
Expected status progression: DRAFT → PUBLISHED
3. Test with Preview URL
Use preview endpoint to test before publishing:
curl -X GET "https://chatapps.8x8.com/config/v1/channels/whatsapp/{channelId}/flows/{flowId}/preview?refresh=true" \
-H "Authorization: Bearer YOUR_API_KEY"
4. Log Complete Request/Response
When debugging, log full API interactions:
const axios = require('axios');
try {
const response = await axios.post(
`https://chatapps.8x8.com/config/v1/channels/whatsapp/${channelId}/flows`,
{
name: 'Test Flow',
categories: ['OTHER'],
flowJson: flowJsonString
},
{
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
}
);
console.log('Success:', response.data);
} catch (error) {
console.error('Error:', {
status: error.response?.status,
statusText: error.response?.statusText,
data: error.response?.data,
request: error.config
});
}
Rate Limits
Flow Configuration API:
- 100 requests per minute per API key
- Applies to all Flow management endpoints (create, update, delete, publish)
Messaging API:
- 1000 messages per second per WhatsApp Business Account
- Applies to both template and interactive messages
Best Practices:
- Implement exponential backoff on 429 errors
- Batch operations where possible
- Cache Flow IDs to avoid repeated GET requests
- Use webhooks instead of polling for submission status
Testing
Testing Checklist
Complete end-to-end API testing:
- Create Flow: POST /flows returns 201 with Flow ID
- Verify Status: GET /flows/{flowId} shows
DRAFTstatus - Generate Preview: GET /preview returns valid URL
- Test Flow: Open preview URL and complete Flow
- Publish Flow: POST /publish changes status to
PUBLISHED - Send Message: Interactive or Template message delivers successfully
- Webhook Received: Webhook endpoint receives
nfmReplypayload - Parse Data: All submitted fields parse correctly from
responseJson - Send Confirmation: Confirmation message sent back to user
Testing Tools
Postman Collection:
- Import 8x8 Connect API collection
- Set environment variables (channelId, API key)
- Test all Flow endpoints
cURL Examples:
# Create Flow
curl -X POST "https://chatapps.8x8.com/config/v1/channels/whatsapp/{channelId}/flows" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d @flow-definition.json
# Publish Flow
curl -X POST "https://chatapps.8x8.com/config/v1/channels/whatsapp/{channelId}/flows/{flowId}/publish" \
-H "Authorization: Bearer $API_KEY"
# Send Interactive Message
curl -X POST "https://chatapps.8x8.com/api/v1/subaccounts/{subAccountId}/messages" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d @interactive-message.json
Webhook Testing:
- Use tools like ngrok to expose local endpoint
- Use RequestBin to inspect webhook payloads
- Monitor webhook logs for
nfmReplyevents
Code Examples
Complete end-to-end examples are available in the WhatsApp Flows Examples documentation, including:
- Product Return & Exchange Flow
- Loan Application with KYC
- Service Appointment Booking
- Healthcare Appointment Scheduling
- CPaaS Lead Generation
Each example includes:
- Complete Flow JSON definition
- API requests for creating and publishing
- Template creation
- Webhook handling code
- Best practices
Related Resources
- WhatsApp Flows in 8x8 Connect - No-code Flow creation interface
- WhatsApp Flows Examples - Industry-specific Flow templates
- WhatsApp Webhooks - Webhook setup and event types
- Template Message API - Complete template syntax
- Interactive Message API - Interactive message reference
- WhatsApp Official Docs - Meta's Flow documentation