Complete Guide to Interactive Messages
Introduction to Interactive Messages
Interactive messages allow you to create rich, engaging conversations with your customers by providing structured choices through list menus or reply buttons. Instead of free-form text responses, customers can simply tap to select their choice, making conversations more efficient and reducing friction.
This guide covers the two types of interactive messages supported by the Coext External API:
- Interactive List Messages: Display a menu button that opens a list of options organized in sections
- Interactive Button Messages: Display 1-3 clickable reply buttons directly in the message
When to Use Interactive Messages
| Use Case | Recommended Type | Why |
|---|---|---|
| Product catalog browsing | List Message | Can show many options organized by category |
| Appointment slot selection | List Message | Display time slots grouped by day |
| Order confirmation (Yes/No) | Button Message | Simple binary choice, always visible |
| Rating feedback (1-3) | Button Message | Up to 3 quick options |
| Main menu navigation | List Message | Many options, clean presentation |
โ
Permission Required: Interactive messages require the send_text permission on your API key. Ensure this permission is enabled in your API key configuration.
Interactive List Messages
List messages display a button that, when tapped, opens a menu of options organized into sections. This is ideal when you have multiple choices that don’t fit comfortably as buttons.
List Message Structure
- Header (optional): Text displayed at the top of the message
- Body (required): Main message text explaining what to do
- Footer (optional): Small text at the bottom
- Button (required): Text on the button that opens the list
- Sections (required): Groups of selectable options
API Endpoint
POST /api/v1/external/messages/interactive/list
Request Body Schema
| Field | Type | Required | Limits | Description |
|---|---|---|---|---|
to | string | Yes | E.164 format | Recipient’s phone number |
header | string | No | Max 60 chars | Header text at the top |
body | string | Yes | Max 1024 chars | Main message text |
footer | string | No | Max 60 chars | Footer text at the bottom |
button | string | Yes | Max 20 chars | Text on the menu button |
sections | array | Yes | 1-10 sections | Array of section objects |
reference | string | No | Max 100 chars | Your tracking reference |
Section Object Schema
| title | Optional, max 24 characters |
| rows | Required, array of 1-10 row objects |
Row Object Schema
| id | Required, max 200 chars, unique identifier |
| title | Required, max 24 characters |
| description | Optional, max 72 characters |
Example 1: Product Catalog
Display a product catalog with categories:
curl -X POST "https://your-domain.com/api/v1/external/messages/interactive/list"
-H "Content-Type: application/json"
-H "X-API-Key: pk_live_your_api_key_here"
-d '{
"to": "+919876543210",
"header": "๐๏ธ Our Products",
"body": "Browse our popular products and select one to learn more!",
"footer": "Tap View Catalog to see options",
"button": "View Catalog",
"sections": [
{
"title": "Electronics",
"rows": [
{
"id": "prod_laptop_001",
"title": "Laptop Pro 15",
"description": "High-performance laptop - $999"
},
{
"id": "prod_phone_001",
"title": "SmartPhone X",
"description": "Latest flagship phone - $799"
}
]
},
{
"title": "Accessories",
"rows": [
{
"id": "prod_headphones_001",
"title": "Wireless Headphones",
"description": "Noise-canceling - $299"
}
]
}
],
"reference": "catalog-browse-001"
}'
Example 2: Appointment Scheduling
Let customers select an appointment slot:
curl -X POST "https://your-domain.com/api/v1/external/messages/interactive/list"
-H "Content-Type: application/json"
-H "X-API-Key: pk_live_your_api_key_here"
-d '{
"to": "+919876543210",
"header": "๐
Book Appointment",
"body": "Please select your preferred time slot for the consultation.",
"footer": "All times in IST",
"button": "Select Time",
"sections": [
{
"title": "Monday, Dec 25",
"rows": [
{ "id": "slot_mon_10", "title": "10:00 AM", "description": "1 hour session" },
{ "id": "slot_mon_14", "title": "2:00 PM", "description": "1 hour session" }
]
},
{
"title": "Tuesday, Dec 26",
"rows": [
{ "id": "slot_tue_09", "title": "9:00 AM", "description": "1 hour session" },
{ "id": "slot_tue_11", "title": "11:00 AM", "description": "1 hour session" }
]
}
],
"reference": "booking-123"
}'
JavaScript (Node.js) Example
const API_KEY = process.env.COEXT_API_KEY;
const BASE_URL = 'https://your-domain.com/api/v1/external';
async function sendListMessage(to, body, button, sections, options = {}) {
const response = await fetch(`${BASE_URL}/messages/interactive/list`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
},
body: JSON.stringify({
to,
body,
button,
sections,
header: options.header,
footer: options.footer,
reference: options.reference
})
});
return response.json();
}
// Usage - Support Menu
const result = await sendListMessage(
'+919876543210',
'How can we help you today?',
'Get Help',
[
{
title: 'Orders',
rows: [
{ id: 'track_order', title: 'Track My Order', description: 'Check order status' },
{ id: 'return_order', title: 'Return/Exchange', description: 'Start a return' }
]
},
{
title: 'Support',
rows: [
{ id: 'talk_agent', title: 'Talk to Agent', description: 'Live chat support' }
]
}
],
{ reference: 'support-menu-001' }
);
console.log('Message ID:', result.data.message_id);
Interactive Button Messages
Button messages display 1-3 reply buttons directly in the message, making them immediately visible and tappable without opening a menu.
Button Message Structure
- Header (optional): Can be text, image, video, or document
- Body (required): Main message text
- Footer (optional): Small text at the bottom
- Buttons (required): 1-3 reply buttons
API Endpoint
POST /api/v1/external/messages/interactive/button
Request Body Schema
| Field | Type | Required | Description |
|---|---|---|---|
to | string | Yes | Recipient phone number (E.164) |
header | object | No | Header with type and content |
body | string | Yes | Message text (max 1024 chars) |
footer | string | No | Footer text (max 60 chars) |
buttons | array | Yes | Array of 1-3 button objects |
reference | string | No | Your tracking reference |
Header Object Types
| Text Header | { "type": "text", "text": "Your Header" } |
| Image Header | { "type": "image", "image": { "link": "https://..." } } |
| Video Header | { "type": "video", "video": { "link": "https://..." } } |
| Document Header | { "type": "document", "document": { "link": "https://...", "filename": "file.pdf" } } |
Button Object Schema
| id | Required, max 256 chars, unique identifier |
| title | Required, max 20 characters |
Example 1: Order Confirmation
Ask for order confirmation with a simple Yes/No:
curl -X POST "https://your-domain.com/api/v1/external/messages/interactive/button"
-H "Content-Type: application/json"
-H "X-API-Key: pk_live_your_api_key_here"
-d '{
"to": "+919876543210",
"body": "Your order #12345 is ready to be shipped!nnTotal: $99.99nDelivery: Standard (3-5 days)nnWould you like to confirm?",
"footer": "Reply within 24 hours",
"buttons": [
{ "id": "confirm_order_12345", "title": "โ
Confirm" },
{ "id": "cancel_order_12345", "title": "โ Cancel" }
],
"reference": "order-confirm-12345"
}'
Example 2: With Text Header
curl -X POST "https://your-domain.com/api/v1/external/messages/interactive/button"
-H "Content-Type: application/json"
-H "X-API-Key: pk_live_your_api_key_here"
-d '{
"to": "+919876543210",
"header": {
"type": "text",
"text": "๐ Special Offer!"
},
"body": "Get 20% off your next purchase with code SAVE20.nnValid until December 31, 2025.",
"footer": "Terms and conditions apply",
"buttons": [
{ "id": "shop_now", "title": "Shop Now" },
{ "id": "save_later", "title": "Save for Later" },
{ "id": "no_thanks", "title": "No Thanks" }
]
}'
Example 3: With Image Header
curl -X POST "https://your-domain.com/api/v1/external/messages/interactive/button"
-H "Content-Type: application/json"
-H "X-API-Key: pk_live_your_api_key_here"
-d '{
"to": "+919876543210",
"header": {
"type": "image",
"image": {
"link": "https://example.com/products/laptop-pro-15.jpg"
}
},
"body": "Laptop Pro 15 is back in stock!nn๐ป 15-inch Retina Displaynโก M3 Pro Chipn๐พ 16GB RAMnnPrice: $999",
"buttons": [
{ "id": "buy_laptop_001", "title": "Buy Now" },
{ "id": "details_laptop_001", "title": "View Details" }
],
"reference": "restock-notify-laptop"
}'
JavaScript (Node.js) Example
const API_KEY = process.env.COEXT_API_KEY;
const BASE_URL = 'https://your-domain.com/api/v1/external';
async function sendButtonMessage(to, body, buttons, options = {}) {
const response = await fetch(`${BASE_URL}/messages/interactive/button`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
},
body: JSON.stringify({
to,
body,
buttons,
header: options.header,
footer: options.footer,
reference: options.reference
})
});
return response.json();
}
// Usage - Customer Satisfaction Survey
const result = await sendButtonMessage(
'+919876543210',
'Thank you for your purchase! How was your experience?',
[
{ id: 'rating_great', title: '๐ Great!' },
{ id: 'rating_okay', title: '๐ Okay' },
{ id: 'rating_poor', title: '๐ Poor' }
],
{
footer: 'Your feedback helps us improve',
reference: 'survey-456'
}
);
console.log('Message ID:', result.data.message_id);
Python Example
import os
import requests
API_KEY = os.environ.get('COEXT_API_KEY')
BASE_URL = 'https://your-domain.com/api/v1/external'
def send_button_message(to: str, body: str, buttons: list, **options) -> dict:
headers = {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
}
payload = {
'to': to,
'body': body,
'buttons': buttons,
**{k: v for k, v in options.items() if v is not None}
}
response = requests.post(
f'{BASE_URL}/messages/interactive/button',
json=payload,
headers=headers
)
return response.json()
# Usage - Order Confirmation
result = send_button_message(
'+919876543210',
'Your order is ready. Confirm shipment?',
[
{'id': 'confirm', 'title': 'Confirm'},
{'id': 'modify', 'title': 'Modify Order'}
],
reference='order-789'
)
print(f"Message ID: {result['data']['message_id']}")
Handling Responses
Success Response
{
"success": true,
"data": {
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "sent"
},
"meta": {
"request_id": "req_abc123def456",
"rate_limit": {
"limit": 60,
"remaining": 58,
"reset_at": "2025-12-23T14:30:00.000Z"
}
}
}
Error Response
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "buttons must have at most 3 items"
},
"meta": {
"request_id": "req_xyz789"
}
}
Common Error Codes
| Code | HTTP | Description |
|---|---|---|
VALIDATION_ERROR | 400 | Request body validation failed |
PERMISSION_DENIED | 403 | API key lacks send_text permission |
INVALID_API_KEY | 401 | API key not found or invalid |
RATE_LIMIT_EXCEEDED | 429 | Too many requests |
MESSAGE_SEND_FAILED | 500 | WhatsApp API error |
Receiving User Replies via Webhooks
When a customer taps a button or selects a list item, their response is sent to your webhook. Configure webhooks in your API key settings.
Button Reply Webhook
{
"event": "message.received",
"timestamp": "2025-12-23T14:35:00.000Z",
"data": {
"message_id": "wamid.xyz123",
"from": "+919876543210",
"to": "+14155551234",
"message": {
"type": "button",
"button": {
"text": "Confirm",
"payload": "confirm_order_12345"
}
}
}
}
List Reply Webhook
{
"event": "message.received",
"timestamp": "2025-12-23T14:35:00.000Z",
"data": {
"message_id": "wamid.abc456",
"from": "+919876543210",
"to": "+14155551234",
"message": {
"type": "interactive",
"interactive": {
"type": "list_reply",
"list_reply": {
"id": "prod_laptop_001",
"title": "Laptop Pro 15"
}
}
}
}
}
Best Practices
Design Guidelines
- Keep titles short: Buttons have 20-char limit, list items have 24-char limit
- Use unique IDs: Always use descriptive, unique IDs for tracking responses
- Add context in body: Explain what each option does before the choices
- Use emojis sparingly: They can help differentiate options but don’t overuse
- Include clear call-to-action: Tell users what action you expect
๐ก Tip: Use Button Messages for 1-3 simple choices that should be immediately visible. Use List Messages for 4+ options or when you need to organize choices into categories.
Technical Recommendations
- Store the message_id: You’ll need it to correlate with webhook responses
- Use the reference field: Include your internal order/transaction ID
- Handle rate limits: Implement exponential backoff on 429 errors
- Validate before sending: Check character limits client-side to avoid errors
- Set up webhooks: Essential for receiving user responses
Quick Reference: Character Limits
List Message Limits
| Element | Limit |
|---|---|
| Header text | 60 characters |
| Body text | 1,024 characters |
| Footer text | 60 characters |
| Button text | 20 characters |
| Sections | 1-10 sections |
| Rows per section | 1-10 rows |
| Row ID | 200 characters |
| Row title | 24 characters |
| Row description | 72 characters |
Button Message Limits
| Element | Limit |
|---|---|
| Header text | 60 characters |
| Body text | 1,024 characters |
| Footer text | 60 characters |
| Number of buttons | 1-3 buttons |
| Button ID | 256 characters |
| Button title | 20 characters |

