WhatsApp Interactive Messages: Buttons, Lists and Limits
Typed replies are messy. A customer asked to "choose a delivery slot" will answer "tmrw pm?", "after 5" or a voice note, and someone on your team has to interpret each one. Buttons and lists fix that: the customer taps, and your system receives a clean ID it can act on.
WhatsApp interactive messages are the Cloud API's family of tappable formats: reply buttons, list menus, URL buttons, location requests, carousels, Flows and a few commerce types. Each has its own character limits, its own payload shape, and one rule that catches nearly everyone: they only go out inside an open customer service window.
This guide gives the limits Meta publishes as of October 2026, working payloads for the two most used types, and how to handle the taps.
The window rule comes first
Interactive messages are service messages. You can send them only while a customer service window is open, meaning the customer messaged or called you in the last 24 hours. Outside the window the API rejects them, and your only option is a template, which can carry its own buttons.
That gives you two different button systems:
| Interactive message | Template with buttons | |
|---|---|---|
| When it can be sent | Only inside an open 24-hour window | Any time, to opted-in customers |
| Approval | None | Meta review before first use |
| Button types | Reply buttons, list rows, one URL button, location, Flow | Quick reply, URL, phone, copy code, Flow, catalog |
| Max buttons | 3 reply buttons, or 10 list rows | Up to 10 |
| Webhook on tap | interactive with button_reply or list_reply | button with payload and text |
| Billing | Service message | The template's category rate |
A common pattern: a utility template with quick replies ("Confirm" or "Reschedule") opens the conversation, the customer taps, the window opens, and from there you use free interactive messages to finish the job.
Every interactive type and its limits
Meta documents each type on its own page. The core limits, from Meta's message reference:
Type (interactive.type) | What the customer sees | Key limits |
|---|---|---|
button | Up to 3 reply buttons | Title 20 characters, unique; ID 256 characters; body 1,024; footer 60; header text, image, video or document |
list | A button that opens a menu of rows | Button label 20; up to 10 sections and 10 rows in total; row title 24, description 72, ID 200; section title 24; body 4,096; text header 60; footer 60 |
cta_url | One button that opens a URL | Label 20 characters; body 1,024; header text (60), image, video or document; footer 60 |
location_request_message | A "Send location" button | Body 1,024; the reply arrives as a location message |
carousel | 2 to 10 swipeable media cards | Image or video header per card; card body 160 characters; one URL button or matching quick replies on every card |
flow | A button that opens a multi-screen form | Covered in our WhatsApp Flows guide |
product, product_list, catalog_message | Items from your Meta catalog | Requires a connected product catalog |
Meta also documents address messages, which collect a delivery address in a native form, but only for businesses based in India messaging Indian customers, and interactive voice call buttons tied to the Calling API.
Pick by job:
- Two or three clear choices: reply buttons. "Confirm", "Reschedule", "Cancel".
- Four to ten choices: a list. Delivery slots, branches, product categories.
- Send them somewhere: a CTA URL button instead of a raw tracking link in the text.
- Need an address or pickup point: a location request.
- Show several products side by side: a carousel. For promotions outside the window, use a carousel template.
- Collect several fields: a Flow, rather than five rounds of questions.
Sending reply buttons
The examples use v23.0; use the current Graph API version from Meta's changelog. This sends a two-button confirmation with a text header:
curl -X POST "https://graph.facebook.com/v23.0/PHONE_NUMBER_ID/messages" \
-H "Authorization: Bearer $WHATSAPP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "+919811122233",
"type": "interactive",
"interactive": {
"type": "button",
"header": { "type": "text", "text": "Your appointment" },
"body": { "text": "Hi Priya, you are booked with Dr Rao tomorrow at 4 pm. Can you still make it?" },
"footer": { "text": "Reply anytime to talk to the front desk" },
"action": {
"buttons": [
{ "type": "reply", "reply": { "id": "appt_8812_confirm", "title": "Yes, see you" } },
{ "type": "reply", "reply": { "id": "appt_8812_reschedule", "title": "Reschedule" } }
]
}
}
}'
Put meaning in the id, not the title. Encoding the record and the action (appt_8812_confirm) means your webhook handler doesn't need to remember what it asked.
Sending a list
Lists suit choices that won't fit in three buttons. The 10-row cap applies across all sections combined, so plan your sections around that total.
{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "+919811122233",
"type": "interactive",
"interactive": {
"type": "list",
"header": { "type": "text", "text": "Choose a delivery slot" },
"body": { "text": "Order A-48213 is ready. When should we deliver it?" },
"footer": { "text": "Slots shown in your local time" },
"action": {
"button": "See slots",
"sections": [
{
"title": "Tomorrow",
"rows": [
{ "id": "slot_tue_am", "title": "Morning", "description": "9 am to 12 pm" },
{ "id": "slot_tue_pm", "title": "Afternoon", "description": "12 pm to 4 pm" }
]
},
{
"title": "Wednesday",
"rows": [
{ "id": "slot_wed_am", "title": "Morning", "description": "9 am to 12 pm" },
{ "id": "slot_wed_eve", "title": "Evening", "description": "4 pm to 8 pm" }
]
}
]
}
}
}
List headers are text only. If you want an image above the choices, use reply buttons, which accept media headers.
Handling the tap
When a customer taps, your messages webhook receives a message of type interactive. Reply buttons arrive as button_reply, list choices as list_reply, and both include the ID you set plus a context object pointing at the message they answered. Quick replies on templates are different: they arrive as type button with a payload.
// Express handler fragment: route taps from interactive messages and templates.
app.post("/webhooks/whatsapp", express.json(), (req, res) => {
res.sendStatus(200); // acknowledge fast, process after
const value = req.body.entry?.[0]?.changes?.[0]?.value;
for (const msg of value?.messages ?? []) {
let choice = null;
if (msg.type === "interactive") {
choice = msg.interactive.button_reply?.id ?? msg.interactive.list_reply?.id ?? null;
} else if (msg.type === "button") {
choice = msg.button.payload; // quick reply tapped on a template
}
if (choice) handleChoice(msg.from, choice, msg.context?.id);
}
});
In production, verify the X-Hub-Signature-256 header against the raw body before trusting any of this. Our guide to receiving WhatsApp messages by API covers the full payload and signature check.
Two habits make interactive flows sturdier. First, accept typed answers too: some customers will type "reschedule" instead of tapping, so match on text as a fallback. Second, make each tap idempotent. Customers double-tap, and webhooks can repeat, so check whether appt_8812_confirm has already been processed before you act on it again.
Design tips that hold up
- Keep titles short and distinct. 20 characters for buttons, 24 for list rows, and reply button titles must be unique within a message.
- Write the body so it stands alone. On a lock screen the customer may only see the text, not the buttons.
- Prefer a CTA URL button over a raw link for long or tracking-heavy URLs.
- Don't nest decisions too deep. If you need three rounds of buttons to collect information, a Flow is usually the better tool.
- Mark messages as read when you process a tap, so customers know you received it.
For a map of every message format, including the non-interactive ones, see our guide to WhatsApp API message types.
Where Tenashi fits
Interactive messages are built for automated, structured exchanges on the official API. If what your team really needs is people answering customers quickly, from one shared place, buttons are optional.
Tenashi connects the WhatsApp number you already use by QR scan, the way WhatsApp Web does, instead of going through Meta's official platform. Its inbox and API send text, media and quoted replies, not buttons, lists, carousels or Flows. Teams that want structured answers save "Reply 1 to confirm or 2 to reschedule" as a canned reply, and every answer lands in a shared inbox with notes, assignment and a CRM under the chat, so a teammate can act on it. It works in WhatsApp groups too, and pricing is flat from $18/mo. If tappable buttons are a hard requirement, or you need them for thousands of automated sessions, use the Cloud API. Our complete guide to the WhatsApp API lays out both options.
Read the API docs · Get started
FAQ
How many buttons can a WhatsApp interactive message have?
A reply button message supports up to 3 buttons, each with a title of up to 20 characters. A list message has one button that opens up to 10 rows across up to 10 sections. A CTA URL message has a single URL button. Templates are separate and can carry up to 10 buttons.
Can I send interactive messages without a template?
Yes, but only inside an open customer service window, which means the customer messaged or called you in the last 24 hours. Interactive messages are service messages and need no approval. Outside the window, you must send an approved template, which can include its own quick reply, URL or phone buttons.
What is the difference between reply buttons and a list message?
Reply buttons show up to three choices directly under the message, so they suit quick yes, no or reschedule decisions. A list message shows one button that opens a menu of up to 10 rows, each with a title and optional description, which suits slots, branches or categories. Lists only support text headers.
How do I know which button a customer tapped?
Your messages webhook receives a message of type interactive. A reply button tap includes a button_reply object, and a list choice includes a list_reply object, each with the ID you assigned plus a context field linking to the original message. Template quick replies arrive as type button with a payload.
Are interactive messages charged?
They are billed as service messages. Under Meta's pricing from October 1, 2026, each business phone number gets 1,000 free service messages per month, after which service messages are charged per delivered message at the market's rate. Templates with buttons are billed at the template's own category rate.
Buttons make answers clean; people make conversations work. For the human side, get started with Tenashi.