Updated on October 29, 2026
Interactive messages are sends with type: "interactive" that show the user reply buttons (up to 3), a list (up to 10 options), a link button, a location request, a carousel or a WhatsApp Flow; when the user taps, the webhook returns the id you defined. They are service messages, so they can only be sent while the 24-hour window is open. To start a conversation with buttons or a Flow, use a template.
Below are the payloads for each type, the character limits, the webhook reply format and a Node.js router to handle them all. The examples use Vectalk (https://apis.vectalk.com.br/api/messages), which accepts the same body as Meta’s WhatsApp Cloud API.
Overview of the types
| Type | interactive.type | Main limits | What comes back in the webhook |
|---|---|---|---|
| Reply buttons | button | Up to 3 buttons; 20-character label; 1,024-character body | interactive.button_reply with id and title |
| List | list | Up to 10 sections and 10 rows in total; 20-character button | interactive.list_reply with id, title and description |
| Link button | cta_url | 1 button; 20-character label | Nothing (opens the browser) |
| Location request | location_request_message | 1,024-character body | A type: "location" message |
| Media carousel | carousel | 2 to 10 cards with image or video | Depends on the button used |
| Flow | flow | flow_message_version equal to "3" | interactive.nfm_reply with response_json |
| Product and multi-product | product, product_list | Tied to a catalog | An order (type: "order") |
Plain media (image, video, document, audio) and fixed locations aren’t interactive, but they usually show up in the same flows and are covered in this guide too.
Reply buttons
The most common format for confirming, choosing among a few options or routing to an agent.
curl -X POST https://apis.vectalk.com.br/api/messages \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"to": "5584999999999",
"type": "interactive",
"interactive": {
"type": "button",
"header": { "type": "text", "text": "Tomorrow's appointment" },
"body": { "text": "Hi Marina. Your appointment is booked for Oct 21 at 9 a.m. Can you confirm?" },
"footer": { "text": "Example Clinic" },
"action": {
"buttons": [
{ "type": "reply", "reply": { "id": "confirm_8812", "title": "Confirm" } },
{ "type": "reply", "reply": { "id": "reschedule_8812", "title": "Reschedule" } },
{ "type": "reply", "reply": { "id": "agent", "title": "Talk to someone" } }
]
}
}
}'
Meta’s rules for this type:
- up to 3 buttons, with unique labels of up to 20 characters;
idof up to 256 characters;- body of up to 1,024 characters and footer of up to 60;
- optional header of text, image, video or document.
Use the id to carry context. In the example, confirm_8812 already says the action and the appointment, so the webhook doesn’t depend on the button text. When the user taps, you get:
"interactive": {
"type": "button_reply",
"button_reply": { "id": "confirm_8812", "title": "Confirm" }
}
Want to try it while you read? Create a Vectalk account and send your first message through the official API. Create free account →
Interactive list
For more than three options: menus, time slots, departments, plans. The user taps the button and a panel with the rows opens.
{
"messaging_product": "whatsapp",
"to": "5584999999999",
"type": "interactive",
"interactive": {
"type": "list",
"header": { "type": "text", "text": "Available times" },
"body": { "text": "Pick a time for your technical visit." },
"footer": { "text": "Brasília time" },
"action": {
"button": "See times",
"sections": [
{
"title": "Wednesday, Oct 22",
"rows": [
{ "id": "slot_2210_09", "title": "09:00", "description": "Technician available" },
{ "id": "slot_2210_14", "title": "14:00", "description": "Technician available" }
]
},
{
"title": "Thursday, Oct 23",
"rows": [
{ "id": "slot_2310_10", "title": "10:00" }
]
}
]
}
}
}
| Field | Limit |
|---|---|
action.button | 20 characters, one button |
| Sections | Up to 10, title up to 24 characters |
| Rows | Up to 10 across all sections |
rows[].title | 24 characters |
rows[].description | 72 characters, optional |
rows[].id | 200 characters |
| Body | 4,096 characters |
| Header | Text only, 60 characters |
| Footer | 60 characters, optional |
The 10-row limit is the total for the message, not per section. If you have more options, split them into steps (first the day, then the time). The choice comes back as list_reply:
"interactive": {
"type": "list_reply",
"list_reply": { "id": "slot_2210_14", "title": "14:00", "description": "Technician available" }
}
Link button (CTA URL)
Hides a long URL behind a button. Useful for payment slips, tracking and customer portals. (In Brazil, a common use is the boleto, a bank payment slip customers pay by barcode.)
{
"messaging_product": "whatsapp",
"to": "5584999999999",
"type": "interactive",
"interactive": {
"type": "cta_url",
"body": { "text": "Your duplicate invoice is ready." },
"action": {
"name": "cta_url",
"parameters": {
"display_text": "Open invoice",
"url": "https://customer.example.com/invoices/7731?source=whatsapp"
}
}
}
}
The label can be up to 20 characters and the body up to 1,024. The tap opens the browser and doesn’t generate a user message, so it doesn’t renew the 24-hour window. To measure clicks, use parameters in the URL itself.
Location: sending and requesting
To send a fixed address (store, pickup point), use type: "location". Latitude and longitude are required; name and address are optional.
{
"messaging_product": "whatsapp",
"to": "5584999999999",
"type": "location",
"location": {
"latitude": -5.7945,
"longitude": -35.2110,
"name": "Example Store Downtown",
"address": "Av. Exemplo, 100 - Natal, RN"
}
}
To ask for the user’s location (delivery, field service), use the interactive message with the send location button:
{
"messaging_product": "whatsapp",
"to": "5584999999999",
"type": "interactive",
"interactive": {
"type": "location_request_message",
"body": { "text": "To calculate shipping, tap the button and send your location." },
"action": { "name": "send_location" }
}
}
The reply arrives as a regular location message, with latitude, longitude and, if the user chooses to share them, name and address. The context.id field points to the request message, which helps tie the reply to the right request.
Media carousel
The carousel shows 2 to 10 horizontally scrolling cards. Each card needs an image or video header, can have a body of up to 160 characters (with up to 2 line breaks) and must have either a link button or quick reply buttons, with the same button type and count on every card. It works well for showing product variations. The full structure is on the carousel page of Meta’s documentation; to start a conversation with cards, see the WhatsApp carousel template.
WhatsApp Flows
Flows are multi-screen forms inside WhatsApp: sign-ups, bookings, surveys, credit simulations. The screen content is defined in a JSON file (Flow JSON), and the result comes back to you in the webhook. For a broader introduction, see WhatsApp Flows.
Meta lists two prerequisites: a verified business and consistently high message quality.
1. Create, upload the JSON and publish
With Vectalk, the cycle runs via API with an instance token:
# Create the Flow as a draft
curl -X POST https://apis.vectalk.com.br/api/flows \
-H "Authorization: Bearer YOUR_INSTANCE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "visit_booking", "categories": ["APPOINTMENT_BOOKING"] }'
# Upload flow.json (multipart)
curl -X POST https://apis.vectalk.com.br/api/flows/FLOW_ID/assets \
-H "Authorization: Bearer YOUR_INSTANCE_TOKEN" \
-F "file=@flow.json" -F "name=flow.json" -F "asset_type=FLOW_JSON"
# Publish
curl -X POST https://apis.vectalk.com.br/api/flows/FLOW_ID/publish \
-H "Authorization: Bearer YOUR_INSTANCE_TOKEN"
The upload response includes validation_errors. Fix everything before publishing: a published Flow can’t be deleted, only deprecated.
2. Send the Flow inside the window
{
"messaging_product": "whatsapp",
"to": "5584999999999",
"type": "interactive",
"interactive": {
"type": "flow",
"header": { "type": "text", "text": "Book a visit" },
"body": { "text": "Pick a date, time and address in a few steps." },
"footer": { "text": "Takes about 1 minute" },
"action": {
"name": "flow",
"parameters": {
"flow_message_version": "3",
"flow_id": "FLOW_ID",
"flow_token": "bk_5584999999999_20261020",
"flow_cta": "Book",
"flow_action": "navigate",
"flow_action_payload": { "screen": "START", "data": { "name": "Marina" } }
}
}
}
}
The flow_token is yours: use it to identify the session when the reply comes back. Meta recommends a CTA of 30 characters or less, with no emoji. To start a conversation with a Flow outside the window, create a template with a FLOW button, as explained in the guide to WhatsApp API message templates.
3. Receive the reply
When the user finishes, you get an interactive message of type nfm_reply. The response_json is a string with the data defined in the Flow’s completion action:
"interactive": {
"type": "nfm_reply",
"nfm_reply": {
"name": "flow",
"body": "Sent",
"response_json": "{\"flow_token\":\"bk_5584999999999_20261020\",\"date\":\"2026-10-22\",\"time\":\"14:00\"}"
}
}
Keep an eye on the Flow’s health. If the Flow’s endpoint becomes unstable, Meta changes the status to THROTTLED (sending limited to 10 messages per hour) and, if it gets worse, to BLOCKED, when it can be neither sent nor opened. The changes arrive through the flows webhook.
Media: image, video, document and audio
Media goes by link (a public URL) or by id (obtained on upload). With Vectalk, the upload is done at /api/media and returns the media_id. More examples are in how to send media with the WhatsApp API.
{
"messaging_product": "whatsapp",
"to": "5584999999999",
"type": "document",
"document": {
"link": "https://files.example.com/contracts/7731.pdf",
"filename": "contract-7731.pdf",
"caption": "Here is the contract for signature."
}
}
Media IDs returned by the upload are valid for 30 days, so you can reuse the same id across sends instead of uploading the file again. For media headers on buttons, Meta advises using the uploaded media id instead of a link. To handle the files customers send you, see how to download media from the WhatsApp webhook.
Routing replies in the webhook
A simple router for the types above, taking advantage of the fact that Vectalk delivers Meta’s payload unchanged:
function route(msg) {
if (msg.type === "location") {
return calculateShipping(msg.from, msg.location.latitude, msg.location.longitude, msg.context?.id);
}
if (msg.type !== "interactive") return handleFreeText(msg);
const i = msg.interactive;
switch (i.type) {
case "button_reply":
if (i.button_reply.id === "agent") return handOffToHuman(msg.from);
return runAction(msg.from, i.button_reply.id);
case "list_reply":
return bookSlot(msg.from, i.list_reply.id);
case "nfm_reply": {
const data = JSON.parse(i.nfm_reply.response_json);
return saveBooking(data.flow_token, data);
}
default:
return logUnhandled(msg);
}
}
Replies to template buttons (quick reply) arrive with type: "button", a different format from button_reply; handle both. The guide to WhatsApp API webhooks shows the full server with a queue and deduplication, the Node.js WhatsApp bot tutorial builds one from scratch, and the article on an AI chatbot with the WhatsApp API shows how to combine buttons with AI-generated replies.
How to choose the right type
The choice depends on what you need back:
| Need | Recommended type | Why |
|---|---|---|
| Yes or no, confirm or reschedule | Reply buttons | One tap, a structured reply and a renewed window |
| Choose among 4 to 10 options | List | Fits in a single message, with a description per row |
| Send to payment, tracking or a portal | Link button | Hides the URL and keeps the text clean |
| Delivery or visit address | Location request | Exact coordinates, no typing |
| Collect several fields (name, date, address, preferences) | Flow | Screens with validation, no back-and-forth in the chat |
| Show product variations | Carousel or catalog messages | Side-by-side visual comparison |
If the conversation needs more than two questions in a row, a Flow is usually shorter for the user than several buttons in sequence.
Best practices
- Short labels with a verb. “Confirm” and “Reschedule” work better than sentences. The 20-character limit cuts off the rest.
- Always a way out to a person. The WhatsApp Business Messaging Policy requires prompt, clear and direct escalation paths in automated experiences, such as handing off to a human agent. A “Talk to someone” button solves it.
- Stable IDs. Treat the
idas a contract with your backend. Changing the button text shouldn’t break routing. - Accept free text. Users may ignore the buttons and type. The router needs to understand “yes”, “2 p.m. works” or a voice note.
- Don’t count on delivery order. Meta warns that the delivery order of a sequence of messages isn’t guaranteed. If order matters, wait for the previous message’s
deliveredstatus before sending the next. - One question per message. Three interactive messages in a row confuse people and lead to replies to the wrong message. The webhook’s
context.idshows which message the user replied to.
Frequently asked questions
Can I send buttons to a customer who hasn’t talked to me today?
Not as an interactive message. Outside the 24-hour window, use a template with quick reply, link or Flow buttons.
Can I add more than 3 reply buttons?
Not in an interactive message. Use a list (up to 10 options). In templates, the limit is up to 10 buttons, with rules per type.
Are interactive messages charged?
They are service messages. Since October 1, 2026, Meta gives each number 1,000 free delivered service messages per month and charges after that, except inside the free entry point window opened by an ad. Rates vary by the recipient’s country; see WhatsApp API pricing by country.
Do I need my own endpoint to use Flows?
Not for simple Flows, where the data lives in the Flow JSON itself and comes back in nfm_reply. Flows that fetch real-time data (available times, prices) use the data_exchange action and require an endpoint of your own.
Build interactive conversations with Vectalk
With Vectalk, buttons, lists, location, media and Flows use the same payload as the Cloud API, and replies arrive in the webhook in the format of Meta’s documentation. One token covers all your accounts, coexistence with the WhatsApp Business app is available, and billing works with a monthly fee plus a prepaid balance: Vectalk puts its own line of credit on your number and each delivered message is deducted from the balance, with no card registered with Meta. Create your account at apps.vectalk.com.br/register, see the examples at docs.vectalk.com.br or talk to the team in the Help Center.
Sources checked on October 29, 2026: Reply button messages, List messages, CTA URL messages, Location request messages, Media carousel messages, Sending a Flow, Flow health and monitoring, Media.
Try the official WhatsApp API today
Create your account, generate a token and send your first message with the quick-start guide. Same payloads and webhooks as Meta's Cloud API.
