As interactive message
Send a flow in a user-initiated conversation (within the 24-hour window).
import { WhatsAppClient } from '@kapso/whatsapp-cloud-api';
const client = new WhatsAppClient({
baseUrl: 'https://api.kapso.ai/meta/whatsapp',
kapsoApiKey: process.env.KAPSO_API_KEY!
});
await client.messages.sendInteractiveFlow({
phoneNumberId: '647015955153740',
to: '15551234567',
bodyText: 'Book your appointment',
parameters: {
flowId: '1197715005513101',
flowCta: 'Book now',
flowAction: 'navigate',
flowActionPayload: {
screen: 'BOOKING',
data: { available_dates: ['2024-01-15', '2024-01-16'] }
}
}
});
As template message
Send a flow outside the 24-hour window using a message template with a FLOW button.
Create template
await client.templates.create({
businessAccountId: '123456789',
name: 'booking_flow',
category: 'UTILITY',
language: 'en_US',
components: [
{
type: 'BODY',
text: 'Ready to book your appointment?'
},
{
type: 'BUTTONS',
buttons: [
{
type: 'FLOW',
text: 'Book now',
flowId: '1197715005513101',
flowAction: 'navigate',
navigateScreen: 'BOOKING'
}
]
}
]
});
Send template
await client.messages.sendTemplate({
phoneNumberId: '647015955153740',
to: '15551234567',
template: {
name: 'booking_flow',
language: { code: 'en_US' },
components: [
{
type: 'button',
subType: 'flow',
index: '0',
parameters: [
{
type: 'action',
action: {
flowToken: 'user_123_booking',
flowActionData: {
available_dates: ['2024-01-15', '2024-01-16']
}
}
}
]
}
]
}
});
Parameters
| Parameter | Description |
|---|
flowId | The unique ID of your published flow |
flowCta | Button text (1-20 characters) |
flowAction | navigate (default) or data_exchange |
flowActionPayload | Initial screen and data to pass to the flow |
flowToken | Identifier for this flow session - see below |
Flow token
The flowToken is a correlation ID. Meta sends it back with every data endpoint request and when the user completes the flow.
Default behavior: If not provided, Kapso uses the flowId as the token. This is required for Kapso to automatically collect and store flow responses.
If you use a custom flowToken (different from the flowId), Kapso won’t be able to link responses to the flow. Only use a custom token if you’re handling responses yourself via the data endpoint.
When to customize:
- You’re using a data endpoint and need to pass context (e.g., user ID, order ID)
- You’re handling response tracking yourself
- The token is available in every data endpoint request via
data_exchange.flow_token
Receiving responses
When a user completes a flow, you receive a whatsapp.message.received webhook. The message has type: interactive with interactive.type: nfm_reply.
{
"message": {
"id": "wamid.ABC123...",
"from": "15551234567",
"timestamp": "1704067200",
"type": "interactive",
"interactive": {
"type": "nfm_reply",
"nfm_reply": {
"name": "flow",
"body": "Sent",
"response_json": "{\"flow_token\":\"1197715005513101\",\"appointment_date\":\"2024-01-15\",\"appointment_time\":\"10:00\"}"
}
},
"kapso": {
"flow_response": {
"flow_token": "1197715005513101",
"appointment_date": "2024-01-15",
"appointment_time": "10:00"
},
"flow_token": "1197715005513101",
"flow_name": "flow"
}
}
}
The interactive.nfm_reply.response_json contains the raw JSON string from the flow’s final screen. Kapso parses this and adds:
| Field | Description |
|---|
kapso.flow_response | Parsed response data from the flow |
kapso.flow_token | The flow token (matches flowId by default) |
kapso.flow_name | Always "flow" for WhatsApp Flows |
To receive flow responses, subscribe to the whatsapp.message.received webhook event and filter for interactive.type === "nfm_reply".