Skip to content

Guide · For developers

WhatsApp interactive messages on the Cloud API: reply buttons, lists, CTA links and location requests in JSON

A developer guide to every common interactive message type on the Cloud API, with Meta's limits, copy-paste JSON, webhook replies, the 24-hour window rule and template buttons for everything outside it.

By Broadchat teamPublished Updated 12 min read

हिन्दी में पढ़ें

In short

WhatsApp interactive messages are sent with type "interactive" to the Cloud API messages endpoint: reply buttons (up to 3, titles up to 20 characters), list messages (up to 10 rows in total, row titles up to 24 characters), a CTA URL button and a location request. They are free-form service messages, so you can only send them inside the 24-hour customer service window; outside it, use an approved template with quick-reply or URL buttons. Read the customer's tap from the webhook: button_reply, list_reply, location, or type "button" for template quick replies.

Key takeaways

  • All interactive types use the same request shape: type "interactive", then interactive.type set to button, list, cta_url or location_request_message.
  • Reply buttons: up to 3, title up to 20 characters, id up to 256. List: up to 10 rows across up to 10 sections, row title 24, description 72, button label 20.
  • Interactive messages are service messages and need an open 24-hour window; a send outside it fails, so start conversations with a template.
  • Branch on the id you set, never on the visible title: it arrives as button_reply.id or list_reply.id in the webhook.
  • Template quick-reply taps arrive differently, as type "button" with button.payload and button.text.

Send WhatsApp interactive messages with the Cloud API

Time needed: 1 h. You will need: A WhatsApp Business Platform phone number ID and access token, curl, Node.js or any HTTP client, A webhook endpoint subscribed to messages.

  1. Open a 24-hour window on your test phone

    Send any message from your own phone to the business number. Interactive messages are free-form service messages, so the API only delivers them while the customer service window is open.

  2. Send reply buttons

    POST a message with type interactive and interactive.type button, a body of up to 1,024 characters and up to three reply buttons, each with a unique id and a title of up to 20 characters.

  3. Send a list message

    Use interactive.type list with a button label of up to 20 characters and sections of rows. Keep the total at 10 rows or fewer, row titles at 24 characters and descriptions at 72.

  4. Send a CTA URL button

    Use interactive.type cta_url with action.name cta_url, a display_text of up to 20 characters and the https link you want the customer to open.

  5. Request the customer's location

    Use interactive.type location_request_message with a body and action.name send_location. The customer's pin arrives as a normal location message in the webhook.

  6. Handle the replies in your webhook

    Read messages[0].type and the nested interactive.type, branch on button_reply.id or list_reply.id, and handle template quick replies, which arrive as type button.

WhatsApp interactive messages let a customer tap an answer instead of typing one. On the Cloud API they are ordinary message requests with "type": "interactive", and the four you will use most are reply buttons, list messages, a call-to-action (CTA) URL button and a location request. This guide gives the exact limits from Meta's documentation, a complete JSON payload for each, the webhook you get back, and the one rule that trips most teams: interactive messages only go out inside the 24-hour customer service window.

It is written for developers integrating directly with the Cloud API or through a platform that exposes the same API. If you would rather build these with a visual builder, see the last section on doing it without code in Broadchat.

Before you start: the endpoint and the 24-hour rule

Every example below is a POST to the messages endpoint of your business phone number:

curl -X POST "https://graph.facebook.com/<API_VERSION>/<PHONE_NUMBER_ID>/messages" \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  -d @payload.json

Interactive messages are service messages: free-form messages that need no template approval. Meta allows service messages only while the customer service window is open, which starts when the customer messages (or calls) you and lasts 24 hours from their latest message. Once it closes you can only send approved message templates. Error 131047 means you tried to send a free-form message outside the window; see the 24-hour window explained.

Cost: an interactive reply inside the window is a service message, not a template. Since 1 Oct 2026, Meta gives each business phone number 1,000 free service messages a month and charges after that; current rates are on WhatsApp API pricing.

For testing, send "hi" from your own phone to the business number first. That opens a window for your test number.

The limits at a glance

Typeinteractive.typeMain limits (Meta docs)Header
Reply buttonsbuttonUp to 3 buttons; title 20 characters; button id 256 characters; body 1,024; footer 60Text, image, video or document
List messagelistUp to 10 sections, 10 rows in total; row title 24, row description 72, row id 200; section title 24; button label 20; body 4,096; footer 60Text only, 60 characters
CTA URL buttoncta_urlOne button; display text 20; body 1,024; footer 60Text (60), image, video or document
Location requestlocation_request_messageBody only, up to 1,024 charactersNone
WhatsApp FlowsflowFlow must be created first; sent as published (default) or draftSee Flows docs
Product messagesproduct, product_listNeed a catalog linked to the WhatsApp account; multi-product: up to 30 products in up to 10 sectionsText header required for multi-product

Button titles must be unique within one message. Meta does not let you style text inside a button title, but emojis are allowed.

Step 1: send reply buttons

Use reply buttons for two or three quick choices. Sunrise Clinic in Jaipur follows up an appointment reminder this way after the patient replies:

{
  "messaging_product": "whatsapp",
  "recipient_type": "individual",
  "to": "919876543210",
  "type": "interactive",
  "interactive": {
    "type": "button",
    "header": {
      "type": "text",
      "text": "Tomorrow's appointment"
    },
    "body": {
      "text": "Hi Meera, your check-up with Dr Rao is at 10:30 am tomorrow. Will you be coming?"
    },
    "footer": {
      "text": "Sunrise Clinic, Jaipur"
    },
    "action": {
      "buttons": [
        {
          "type": "reply",
          "reply": {
            "id": "appt_confirm_8841",
            "title": "Confirm visit"
          }
        },
        {
          "type": "reply",
          "reply": {
            "id": "appt_reschedule_8841",
            "title": "Reschedule"
          }
        },
        {
          "type": "reply",
          "reply": {
            "id": "appt_cancel_8841",
            "title": "Cancel"
          }
        }
      ]
    }
  }
}

The ids carry what your code needs (action plus booking number), so the reply handler does not have to look anything up from the title. Each title starts with a verb and stays under 20 characters: "Reschedule my appointment" would be rejected; "Reschedule" fits.

Swap the text header for an image by setting "header": { "type": "image", "image": { "link": "https://…" } } or an uploaded media id. A video or document header works the same way.

Step 2: send a list message

Use a list for four to ten options of equal weight: product categories, departments, branches, FAQ topics. Rangoli Home, a home décor brand, sends this when a customer writes "catalogue":

{
  "messaging_product": "whatsapp",
  "recipient_type": "individual",
  "to": "919876543210",
  "type": "interactive",
  "interactive": {
    "type": "list",
    "header": {
      "type": "text",
      "text": "Rangoli Home"
    },
    "body": {
      "text": "What would you like to browse today, Priya? Tap the button to pick a category."
    },
    "footer": {
      "text": "Mon to Sat, 10 am to 7 pm"
    },
    "action": {
      "button": "See categories",
      "sections": [
        {
          "title": "Living room",
          "rows": [
            {
              "id": "cat_cushions",
              "title": "Cushion covers",
              "description": "Block-printed cotton, from ₹349"
            },
            {
              "id": "cat_throws",
              "title": "Throws and rugs",
              "description": "Handloom, 3 sizes"
            }
          ]
        },
        {
          "title": "Dining",
          "rows": [
            {
              "id": "cat_runners",
              "title": "Table runners"
            },
            {
              "id": "cat_napkins",
              "title": "Napkin sets"
            }
          ]
        },
        {
          "title": "Help",
          "rows": [
            {
              "id": "help_order",
              "title": "Track my order"
            },
            {
              "id": "help_agent",
              "title": "Talk to the team"
            }
          ]
        }
      ]
    }
  }
}

This list has 6 rows in 3 sections. Remember the cap is 10 rows in total, not per section: ten sections with three rows each fails. The button label ("See categories") is what the customer taps to open the sheet, and it is limited to 20 characters. Row descriptions are optional; use them for a price or timing, as above.

Step 3: send a CTA URL button

A reply button cannot open a website. When you want the customer to go to a link, such as a tracking page, a payment page or a booking calendar, send a CTA URL message:

{
  "messaging_product": "whatsapp",
  "recipient_type": "individual",
  "to": "919876543210",
  "type": "interactive",
  "interactive": {
    "type": "cta_url",
    "header": {
      "type": "text",
      "text": "Order #RH-20417"
    },
    "body": {
      "text": "Your order has been handed to the courier. Tap below for the live status."
    },
    "footer": {
      "text": "Rangoli Home"
    },
    "action": {
      "name": "cta_url",
      "parameters": {
        "display_text": "Track order",
        "url": "https://rangolihome.in/track?o=RH-20417"
      }
    }
  }
}

The customer sees a single button labelled Track order instead of a long raw URL. Keep tracking parameters in the URL if you need attribution; the button text stays clean.

Step 4: request the customer's location

A location request shows the body text and a Send location button. Tapping it opens WhatsApp's location-sharing screen. A diagnostics lab collecting samples at home might send:

{
  "messaging_product": "whatsapp",
  "recipient_type": "individual",
  "to": "919876543210",
  "type": "interactive",
  "interactive": {
    "type": "location_request_message",
    "body": {
      "text": "Please share your location so our phlebotomist reaches the right address for the home sample collection."
    },
    "action": {
      "name": "send_location"
    }
  }
}

The answer comes back as an ordinary location message with latitude and longitude. The name and address fields appear only if the customer chooses to share them, so never make them required in your code.

Step 5: handle the replies in your webhook

Every tap arrives on your webhook as a messages change. For a reply button tap, the full payload looks like this:

{
  "object": "whatsapp_business_account",
  "entry": [
    {
      "id": "<WHATSAPP_BUSINESS_ACCOUNT_ID>",
      "changes": [
        {
          "field": "messages",
          "value": {
            "messaging_product": "whatsapp",
            "metadata": {
              "display_phone_number": "911412345678",
              "phone_number_id": "<PHONE_NUMBER_ID>"
            },
            "contacts": [
              {
                "profile": {
                  "name": "Meera"
                },
                "wa_id": "919876543210"
              }
            ],
            "messages": [
              {
                "from": "919876543210",
                "id": "wamid.HBgM...",
                "timestamp": "1793424000",
                "context": {
                  "from": "911412345678",
                  "id": "wamid.HBgL..."
                },
                "type": "interactive",
                "interactive": {
                  "type": "button_reply",
                  "button_reply": {
                    "id": "appt_reschedule_8841",
                    "title": "Reschedule"
                  }
                }
              }
            ]
          }
        }
      ]
    }
  ]
}

context.id is the id of the message the customer replied to, so you can match a tap to the exact message you sent. The other shapes differ only inside messages[0]:

List selection

{
  "type": "interactive",
  "interactive": {
    "type": "list_reply",
    "list_reply": {
      "id": "cat_cushions",
      "title": "Cushion covers",
      "description": "Block-printed cotton, from ₹349"
    }
  }
}

Shared location

{
  "type": "location",
  "location": {
    "latitude": 26.9124,
    "longitude": 75.7873,
    "name": "Optional, only if the user shares it",
    "address": "Optional, only if the user shares it"
  }
}

Quick-reply button on a template

{
  "type": "button",
  "context": {
    "from": "911412345678",
    "id": "wamid.HBgL..."
  },
  "button": {
    "payload": "Stop offers",
    "text": "Stop offers"
  }
}

Note the difference: interactive replies arrive as type: "interactive" with button_reply or list_reply, while a tap on a template quick-reply button arrives as type: "button" with button.payload and button.text. Handle both, because most real conversations start with a template and continue with interactive messages. A small handler in Node.js:

// Read the customer's choice from a Cloud API "messages" webhook (the value object)
export function readChoice(value) {
  const msg = value.messages?.[0];
  if (!msg) return null; // a status update, not a message
  switch (msg.type) {
    case 'interactive': {
      const it = msg.interactive;
      if (it.type === 'button_reply') return { kind: 'button', id: it.button_reply.id };
      if (it.type === 'list_reply') return { kind: 'list', id: it.list_reply.id };
      return { kind: it.type, raw: it }; // other interactive replies, such as a Flow
    }
    case 'button': // quick-reply button on a template
      return { kind: 'template_button', payload: msg.button.payload, text: msg.button.text, repliedTo: msg.context?.id };
    case 'location':
      return { kind: 'location', lat: msg.location.latitude, lng: msg.location.longitude };
    default:
      return { kind: msg.type, text: msg.text?.body };
  }
}

Two habits save debugging time. Return HTTP 200 quickly and process the message in a background job, because Meta retries webhooks that fail. And de-duplicate on the incoming message id, because a retry can deliver the same tap twice.

Outside the window: templates with buttons

Broadcasts, reminders and anything sent more than 24 hours after the customer's last message must be templates. Templates have their own buttons, approved with the template:

Template buttonLimit
All buttons togetherUp to 10 per template
Quick replyUp to 10; label up to 25 characters
URL (visit website)Up to 2
Phone number (call)Up to 1
Copy codeUp to 1

Quick-reply buttons must be grouped together, separate from the other button types, or the API rejects the template. Meta also notes that templates with four or more buttons, or with quick replies mixed with other buttons, do not display on WhatsApp desktop; the customer is asked to open the message on their phone.

The pattern that works: a utility or marketing template with two or three quick replies starts the conversation. When the customer taps one, the window is open, and your bot continues with interactive buttons and lists. Our guide to sending a template message through the API covers the template side.

Flows and product messages

WhatsApp Flows are multi-screen forms inside the chat, useful when you need several answers in a row, such as name, date and time slot. They are sent as an interactive message of type flow inside the window, or through a template outside it. Create the Flow first; Meta sends the published version by default, and a draft can be sent with draft mode while you test.

Product messages show items from a catalog linked to your WhatsApp account: product for a single item, product_list for a multi-product message of up to 30 products in up to 10 sections, each product referenced by its product_retailer_id together with the catalog_id. Link the catalog to the WhatsApp account before you try them.

Common errors and fixes

SymptomLikely causeFix
Error 131047The 24-hour window is closedSend an approved template; continue with interactive messages after the customer replies
Parameter error on a button messageA fourth button, a title over 20 characters, or two buttons with the same titleKeep 3 buttons with unique titles under 20 characters
Parameter error on a listMore than 10 rows in total, a row title over 24 characters, or a non-text headerCount rows across all sections; shorten titles; use a text header
Bot branches the wrong wayCode matches on the visible title, which you later editedBranch on the id you set
Same tap handled twiceWebhook retryDe-duplicate on the incoming message id
Location reply has no addressThe customer did not share the name or addressUse latitude and longitude only

For other codes, see our WhatsApp API error codes reference.

Doing it without code in Broadchat

In Broadchat's visual chatbot builder (Chatbots → Chatbots), the Ask Question step has an Interactive message type where you choose buttons or a list, name the list button, add sections and rows, and connect each choice to its own branch. The same step offers Location Request, and the Send Message step offers Cta url for link buttons. Template quick-reply buttons can also branch the flow. See build a chatbot and the WhatsApp chatbot builder.

Developers can also send the JSON payloads in this guide as they are: each channel has an access token and a Graph-compatible API address on your panel, which accepts the same /messages requests as the Cloud API for that channel's number. For how interactive messages fit into the wider platform, start with what the WhatsApp Business API is.

Sources

  1. Interactive reply buttons messages — Meta for Developers, checked 31 October 2026
  2. Interactive list messages — Meta for Developers, checked 31 October 2026
  3. Interactive CTA URL button messages — Meta for Developers, checked 31 October 2026
  4. Location request messages — Meta for Developers, checked 31 October 2026
  5. Service messages (customer service window) — Meta for Developers, checked 31 October 2026
  6. Button messages webhook reference — Meta for Developers, checked 31 October 2026
  7. Template components — Meta for Developers, checked 31 October 2026
  8. Sending a Flow — Meta for Developers, checked 31 October 2026
  9. Pricing on the WhatsApp Business Platform — Meta for Developers, checked 31 October 2026

Broadchat team · Product and partner success team

The people who build and support Broadchat, the white-label WhatsApp Business API platform. We write from what we see while onboarding partners and their clients on the official WhatsApp Cloud API, and we check every article against Meta's current documentation.

Last updated:

Frequently asked questions

How many buttons can a WhatsApp interactive message have?

An interactive reply-button message can have up to 3 buttons, each with a title of up to 20 characters. For 4 to 10 options use a list message. Approved templates are different: they can carry up to 10 buttons, with quick-reply labels of up to 25 characters.

Can I send interactive messages in a broadcast?

No. Interactive messages are free-form service messages that need an open 24-hour customer service window. A broadcast goes to people outside the window, so it must use an approved template, which can include quick-reply, URL, call and copy-code buttons.

What is the limit on WhatsApp list messages?

A list can have up to 10 sections but no more than 10 rows in total. Row titles are up to 24 characters, row descriptions up to 72, section titles up to 24 and the menu button label up to 20. Only a text header is allowed.

How do I know which button the customer tapped?

Your webhook receives a messages event with type interactive. For reply buttons read interactive.button_reply.id, and for lists read interactive.list_reply.id. These are the ids you set when sending, so branch on them rather than on the visible title.

Are interactive messages charged?

They are service messages, not templates. Since 1 Oct 2026 each business phone number gets 1,000 free service messages a month from Meta and further service messages are charged. Templates with buttons are charged by their template category.

Can a reply button open a website or make a call?

No. A reply button only sends an answer back to you. To open a link inside the window, send a CTA URL message; outside the window, use a template with a URL or phone-number button.

See Broadchat on a real WhatsApp number

A short walkthrough of campaigns, chatbots, the team inbox and the partner panel, built around your use case.