Skip to main content

WhatsApp Interactive Messages

Interactive messages allow customers to respond with structured actions -- tapping buttons, selecting from menus, or clicking links -- instead of typing free-form text. This improves response rates, reduces errors, and enables automated workflows.

Interactive Message Types

TypeDescriptionMax OptionsBest For
Reply ButtonsUp to 3 clickable buttons3 buttonsSimple choices (Yes/No, options)
List MessagesScrollable menu with sections10 rows across 10 sectionsComplex selections (categories, products)
CTA URL ButtonsButtons that open a web URL2 buttonsLinking to websites, tracking pages
CTA Phone ButtonsButtons that initiate a phone call1 buttonDirect call support
note

Interactive messages can only be sent within a 24-hour session window (session messages) or as part of a template. See Session Messages for session details.

Reply Buttons

Reply buttons present up to 3 quick-action options. When a customer taps a button, the button text is sent as their response.

Specifications

PropertyLimit
Body textRequired, max 1,024 characters
HeaderOptional (text, image, video, document)
FooterOptional, max 60 characters
Buttons1-3 buttons
Button titleMax 20 characters each
Button IDMax 256 characters (used in webhook callback)

API Example

curl -u '<api_key>:<api_token>' -X POST "https://api.in.exotel.com/v2/accounts/<account_sid>/messages" \
-H "Content-Type: application/json" \
-d '{
"from": "+919876500001",
"to": "+919876543210",
"content": {
"type": "interactive",
"interactive": {
"type": "button",
"header": {
"type": "text",
"text": "Order Update"
},
"body": {
"text": "Your order #ORD-12345 has been delivered. How was your experience?"
},
"footer": {
"text": "Tap a button to respond"
},
"action": {
"buttons": [
{
"type": "reply",
"reply": {
"id": "rating_great",
"title": "Great!"
}
},
{
"type": "reply",
"reply": {
"id": "rating_ok",
"title": "It was okay"
}
},
{
"type": "reply",
"reply": {
"id": "rating_bad",
"title": "Not satisfied"
}
}
]
}
}
}
}'

Handling Button Responses

When a customer taps a button, your webhook receives the button ID and title:

{
"from": "+919876543210",
"type": "interactive",
"interactive": {
"type": "button_reply",
"button_reply": {
"id": "rating_great",
"title": "Great!"
}
}
}

List Messages

List messages display a scrollable menu organized into sections. Customers tap a button to open the list, browse options, and select one.

Specifications

PropertyLimit
Body textRequired, max 1,024 characters
HeaderOptional, text only, max 60 characters
FooterOptional, max 60 characters
Button textRequired, max 20 characters (the button that opens the list)
Sections1-10 sections
Rows per section1-10 rows
Total rowsMax 10 across all sections
Row titleMax 24 characters
Row descriptionOptional, max 72 characters
Row IDMax 200 characters

API Example

curl -u '<api_key>:<api_token>' -X POST "https://api.in.exotel.com/v2/accounts/<account_sid>/messages" \
-H "Content-Type: application/json" \
-d '{
"from": "+919876500001",
"to": "+919876543210",
"content": {
"type": "interactive",
"interactive": {
"type": "list",
"header": {
"type": "text",
"text": "Help Center"
},
"body": {
"text": "Welcome to our support center. Please select the topic you need help with:"
},
"footer": {
"text": "Reply AGENT to talk to a human"
},
"action": {
"button": "Select Topic",
"sections": [
{
"title": "Orders",
"rows": [
{
"id": "track_order",
"title": "Track My Order",
"description": "Get real-time delivery status"
},
{
"id": "cancel_order",
"title": "Cancel Order",
"description": "Cancel a pending order"
},
{
"id": "return_order",
"title": "Return or Exchange",
"description": "Initiate a return or exchange"
}
]
},
{
"title": "Payments",
"rows": [
{
"id": "refund_status",
"title": "Refund Status",
"description": "Check your refund progress"
},
{
"id": "payment_issue",
"title": "Payment Failed",
"description": "Help with payment problems"
}
]
},
{
"title": "Account",
"rows": [
{
"id": "update_profile",
"title": "Update Profile",
"description": "Change name, email, or address"
},
{
"id": "reset_password",
"title": "Reset Password",
"description": "Reset your login credentials"
}
]
}
]
}
}
}
}'

Handling List Responses

When a customer selects a list item, your webhook receives:

{
"from": "+919876543210",
"type": "interactive",
"interactive": {
"type": "list_reply",
"list_reply": {
"id": "track_order",
"title": "Track My Order",
"description": "Get real-time delivery status"
}
}
}

CTA URL Buttons

Call-to-action buttons that open a URL in the customer's browser.

API Example

{
"type": "interactive",
"interactive": {
"type": "cta_url",
"body": {
"text": "Your order #ORD-12345 has been shipped! Click below to track your delivery."
},
"action": {
"name": "cta_url",
"parameters": {
"display_text": "Track Order",
"url": "https://example.com/track/ORD-12345"
}
}
}
}

Building Conversational Flows

Interactive messages are most powerful when chained together to create multi-step flows:

Step 1: List Message
"How can we help you today?"
→ Customer selects "Track My Order"

Step 2: Text Message
"Please enter your order number."
→ Customer types "ORD-12345"

Step 3: Reply Buttons
"Order #ORD-12345 is out for delivery. ETA: 3 PM.
Need anything else?"
[Yes, another question] [No, thanks]
→ Customer taps "No, thanks"

Step 4: Text Message
"Thank you! Have a great day."

Implementation Example

async function handleInbound(message) {
const { from, type, interactive, text } = message;
const session = await getSession(from);

if (type === 'interactive') {
const reply = interactive.button_reply || interactive.list_reply;

switch (reply.id) {
case 'track_order':
await sendTextMessage(from, 'Please enter your order number.');
await updateSession(from, { step: 'awaiting_order_id' });
break;

case 'cancel_order':
await sendReplyButtons(from,
'Are you sure you want to cancel your order?',
[
{ id: 'confirm_cancel', title: 'Yes, Cancel' },
{ id: 'keep_order', title: 'No, Keep It' }
]
);
break;

case 'confirm_cancel':
await cancelOrder(session.orderId);
await sendTextMessage(from, 'Your order has been cancelled.');
break;
}
} else if (type === 'text' && session.step === 'awaiting_order_id') {
const orderStatus = await getOrderStatus(text.body);
await sendReplyButtons(from,
`Order #${text.body}: ${orderStatus}. Need anything else?`,
[
{ id: 'help_more', title: 'Yes, another question' },
{ id: 'done', title: 'No, thanks' }
]
);
}
}

Design Best Practices

  1. Keep button labels short -- 20 characters maximum; use clear, action-oriented text.
  2. Use list messages for 4+ options -- Reply buttons work for 2-3 choices; lists work for more.
  3. Add descriptions to list items -- Help customers understand each option before selecting.
  4. Organize lists into sections -- Group related items for easier navigation.
  5. Provide a fallback -- Always offer a "Talk to Agent" or "Other" option.
  6. Chain messages logically -- Guide customers through a clear, step-by-step flow.
  7. Handle unexpected responses -- Customers may type text instead of tapping buttons; handle both.
warning

Interactive messages are not supported on WhatsApp Web (desktop) for some older versions. Ensure your customer base primarily uses mobile WhatsApp for the best experience.

Next Steps