Interactive Messages
Interactive messages add tappable buttons, selectable lists, and call-to-action links to your conversations. They drive higher engagement than plain text and make webhook handling deterministic through predictable reply IDs. Interactive messages require an open 24-hour window.
📡 Endpoint
POST /v1/whatsapp/messages
All interactive messages use "type": "interactive" with an interactive object whose type is one of button, list, or cta_url.
🔘 Quick Reply Buttons
Up to 3 buttons. Button titles must be 20 characters or fewer.
{
"to": "+251911234567",
"type": "interactive",
"interactive": {
"type": "button",
"body": { "text": "How can we help you today?" },
"action": {
"buttons": [
{ "type": "reply", "reply": { "id": "support", "title": "Contact Support" } },
{ "type": "reply", "reply": { "id": "track", "title": "Track Order" } }
]
}
}
}
📋 List Messages
Use lists for 4–10 options grouped into sections.
{
"to": "+251911234567",
"type": "interactive",
"interactive": {
"type": "list",
"header": { "type": "text", "text": "Support" },
"body": { "text": "Choose a department:" },
"footer": { "text": "We reply within 5 minutes" },
"action": {
"button": "View Options",
"sections": [
{
"title": "Departments",
"rows": [
{ "id": "sales", "title": "Sales", "description": "Pricing and plans" },
{ "id": "support", "title": "Support", "description": "Technical help" },
{ "id": "billing", "title": "Billing", "description": "Invoices and payments" }
]
}
]
}
}
}
🔗 Call-to-Action URL
{
"to": "+251911234567",
"type": "interactive",
"interactive": {
"type": "cta_url",
"body": { "text": "Your order has shipped." },
"action": {
"name": "cta_url",
"parameters": { "display_text": "Track Package", "url": "https://example.com/track/12345" }
}
}
}
💻 Code Samples
await fetch('https://api.afriroute.ai/api/v1/whatsapp/messages', {
method: 'POST',
headers: {
'Authorization': 'Bearer $AFRIROUTE_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
to: '+251911234567',
type: 'interactive',
interactive: {
type: 'button',
body: { text: 'Confirm your appointment?' },
action: {
buttons: [
{ type: 'reply', reply: { id: 'confirm', title: 'Confirm' } },
{ type: 'reply', reply: { id: 'reschedule', title: 'Reschedule' } }
]
}
}
})
});
import requests
requests.post(
'https://api.afriroute.ai/api/v1/whatsapp/messages',
headers={'Authorization': 'Bearer $AFRIROUTE_API_KEY'},
json={
'to': '+251911234567',
'type': 'interactive',
'interactive': {
'type': 'button',
'body': {'text': 'Confirm your appointment?'},
'action': {'buttons': [
{'type': 'reply', 'reply': {'id': 'confirm', 'title': 'Confirm'}},
{'type': 'reply', 'reply': {'id': 'reschedule', 'title': 'Reschedule'}}
]}
}
}
)
📥 Handling Replies
A tap arrives on your webhook as an interactive message. Read the selected id:
const interactive = message.interactive;
const selectedId =
interactive.button_reply?.id || interactive.list_reply?.id;
// route on selectedId: 'support', 'track', 'sales', ...
💡 Best Practices
- Limit to 3 buttons; switch to a list for 4+ options.
- Keep button titles under 20 characters and list titles under 24.
- Use descriptive IDs (
track_order, notb1) for clean routing. - Add a footer to set expectations (e.g. response time).
- Always handle unknown IDs gracefully in your webhook.
⚠️ Error Handling
| Code | Description | Solution |
|---|---|---|
| 131009 | Parameter value invalid | Check button/list structure |
| 131047 | 24-hour window expired | Send a template first |
| 100 | Invalid interactive object | Validate against schema |
📚 Related Resources
Need help? Contact Support → | Last Updated: May 2026