WhatsApp API & Developers

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 messageTemplate with buttons
When it can be sentOnly inside an open 24-hour windowAny time, to opted-in customers
ApprovalNoneMeta review before first use
Button typesReply buttons, list rows, one URL button, location, FlowQuick reply, URL, phone, copy code, Flow, catalog
Max buttons3 reply buttons, or 10 list rowsUp to 10
Webhook on tapinteractive with button_reply or list_replybutton with payload and text
BillingService messageThe 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 seesKey limits
buttonUp to 3 reply buttonsTitle 20 characters, unique; ID 256 characters; body 1,024; footer 60; header text, image, video or document
listA button that opens a menu of rowsButton 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_urlOne button that opens a URLLabel 20 characters; body 1,024; header text (60), image, video or document; footer 60
location_request_messageA "Send location" buttonBody 1,024; the reply arrives as a location message
carousel2 to 10 swipeable media cardsImage or video header per card; card body 160 characters; one URL button or matching quick replies on every card
flowA button that opens a multi-screen formCovered in our WhatsApp Flows guide
product, product_list, catalog_messageItems from your Meta catalogRequires 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.