Implementing Structured Message Templates with Quick Reply Buttons for WhatsApp Business
What This Guide Covers
You are implementing WhatsApp Business API message templates with interactive Quick Reply buttons through Genesys Cloud’s Open Messaging framework. When complete, your outbound proactive notifications will use WhatsApp-approved template messages with up to three Quick Reply buttons, enabling customers to respond to appointment reminders, order updates, and support follow-ups with a single tap-and having those responses route back through Genesys Cloud as inbound interactions for bot or agent handling. This pattern dramatically increases response rates compared to plain-text messages and enables automation of common customer journeys like appointment confirmation, callback scheduling, and satisfaction rating.
Prerequisites, Roles & Licensing
- Genesys Cloud: CX 2 or 3 with Digital channels.
- WhatsApp: An approved WhatsApp Business Account (WABA) via a BSP (Business Solution Provider) such as Infobip, Vonage, or Twilio.
- Permissions required:
Messaging > Integration > Edit
- Infrastructure:
- An Open Messaging integration configured in Genesys Cloud pointing to your BSP webhook.
- Pre-approved WhatsApp message templates (submitted via Meta Business Manager and approved - typically 1-3 business days).
The Implementation Deep-Dive
1. Why Templates + Quick Replies Beat Plain Messages
WhatsApp enforces a strict 24-hour session window policy: you can only send free-form messages to customers within 24 hours of their last message to you. Outside that window (which is most proactive outreach scenarios), you must use pre-approved templates - standardized message formats that Meta reviews before your account can send them.
Quick Reply buttons convert a passive notification into an interactive micro-conversation:
Without buttons:
“Your appointment is tomorrow at 2 PM. Please reply to confirm.”
→ Customer must type a reply → low response rate, inconsistent text parsing
With Quick Reply buttons:
“Your appointment is tomorrow at 2 PM.”
[✓ Confirm] [✗ Cancel] [↻ Reschedule]
→ Customer taps one button → structured response, 3-5× higher completion rate
2. Template Categories and Approval Requirements
WhatsApp categorizes templates into three types - select correctly to avoid rejection:
| Category | Use Case | Approval Likelihood |
|---|---|---|
UTILITY |
Transactional: order updates, appointment reminders, account alerts | High |
MARKETING |
Promotional content, offers, product announcements | Medium (requires opt-in proof) |
AUTHENTICATION |
OTP delivery, 2FA codes | High |
Templates are submitted per WABA account with the following components:
Template Name: appointment_reminder_v2
Category: UTILITY
Language: en_US
Header: Your appointment is confirmed ✓
Body: Hi {{1}}, your {{2}} appointment is scheduled for {{3}} at {{4}}.
Please confirm your attendance or let us know if you need to reschedule.
Footer: Reply any time to reach support
Buttons:
- Quick Reply: "Confirm ✓"
- Quick Reply: "Cancel ✗"
- Quick Reply: "Reschedule ↻"
Variables {{1}}, {{2}}, {{3}}, {{4}} are filled at send time with customer-specific data.
3. Sending the Template via BSP API
Most BSPs expose a REST API that wraps the WhatsApp Business API. Here’s an implementation using Infobip:
import requests
import json
INFOBIP_BASE_URL = "https://api.infobip.com"
INFOBIP_API_KEY = "your-infobip-api-key"
def send_appointment_reminder(
customer_phone: str, # E.164 format: +66812345678
customer_name: str,
service_type: str,
appointment_date: str,
appointment_time: str
) -> dict:
"""
Sends a WhatsApp template message with Quick Reply buttons for appointment confirmation.
"""
payload = {
"messages": [
{
"from": "your-waba-number", # Your WhatsApp Business number
"to": customer_phone,
"messageId": f"appt-{customer_phone}-{appointment_date}",
"content": {
"templateName": "appointment_reminder_v2",
"templateData": {
"body": {
"placeholders": [
customer_name, # {{1}}
service_type, # {{2}}
appointment_date, # {{3}}
appointment_time # {{4}}
]
},
"buttons": [
{"type": "QUICK_REPLY", "parameter": "CONFIRM"},
{"type": "QUICK_REPLY", "parameter": "CANCEL"},
{"type": "QUICK_REPLY", "parameter": "RESCHEDULE"}
]
},
"language": "en"
},
"callbackData": json.dumps({
"customerId": customer_phone,
"appointmentDate": appointment_date,
"interactionType": "APPOINTMENT_REMINDER"
}),
"notifyUrl": "https://your-domain.com/webhooks/whatsapp/status"
}
]
}
resp = requests.post(
f"{INFOBIP_BASE_URL}/whatsapp/1/message/template",
headers={
"Authorization": f"App {INFOBIP_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json"
},
json=payload,
timeout=10
)
return resp.json()
4. Receiving Quick Reply Responses via Genesys Open Messaging
When a customer taps a Quick Reply button, WhatsApp delivers the response as an inbound message. Your BSP forwards it to your Genesys Cloud Open Messaging webhook endpoint. Your webhook server processes it and injects it into the Genesys conversation:
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
GENESYS_API_BASE = "https://api.mypurecloud.com"
OPEN_MESSAGING_INTEGRATION_ID = "your-integration-id"
@app.route('/webhooks/whatsapp/inbound', methods=['POST'])
def handle_whatsapp_inbound():
"""
Receives inbound WhatsApp messages (including Quick Reply button presses)
from the BSP and routes them into Genesys Cloud via Open Messaging.
"""
payload = request.json
# Parse the Quick Reply button press
message = payload.get('results', [{}])[0]
sender_phone = message.get('from')
button_payload = message.get('message', {}).get('buttonReply', {}).get('id', '')
message_text = message.get('message', {}).get('text', button_payload)
# Determine intent from button payload
intent_map = {
"CONFIRM": "appointment_confirmed",
"CANCEL": "appointment_cancelled",
"RESCHEDULE": "appointment_reschedule_requested"
}
intent = intent_map.get(button_payload, "unknown")
# Inject into Genesys Cloud as Open Messaging inbound
genesys_payload = {
"channel": {
"messageId": message.get('messageId'),
"time": message.get('receivedAt'),
"from": {
"nickname": sender_phone,
"id": sender_phone,
"idType": "Phone",
"image": ""
},
"to": {
"id": OPEN_MESSAGING_INTEGRATION_ID
}
},
"type": "Text",
"text": message_text,
"originatingEntity": "Human",
"metadata": {
"customAttributes": {
"whatsappButtonIntent": intent,
"buttonPayload": button_payload
}
}
}
access_token = get_genesys_token()
requests.post(
f"{GENESYS_API_BASE}/api/v2/conversations/messages/inbound/open",
headers={"Authorization": f"Bearer {access_token}", "Content-Type": "application/json"},
json=genesys_payload
)
return jsonify({"status": "accepted"}), 200
5. Architect Flow: Routing by Button Intent
In your Genesys Architect inbound message flow, read the whatsappButtonIntent participant attribute to route automatically:
[Inbound WhatsApp Message]
|
v
[Get Participant Data: whatsappButtonIntent]
|
|-- "appointment_confirmed" --> [Update Appointment Status to Confirmed] --> [Send Confirmation Text] --> [Disconnect]
|-- "appointment_cancelled" --> [Update Appointment Status to Cancelled] --> [Offer Reschedule Bot]
|-- "appointment_reschedule_requested" --> [Transfer to Scheduling Queue]
|-- default --> [Transfer to General Support Queue]
Validation, Edge Cases & Troubleshooting
Edge Case 1: Template Rejected by Meta
Meta may reject a template for being “too promotional” even if submitted under the UTILITY category, or for containing variables in the header without a header example.
Solution: When submitting templates, always provide sample values for all variables in the “template preview” field. Review Meta’s Message Template Guidelines before submission. If rejected, edit the template to remove subjective language and resubmit - the approval process restarts from scratch.
Edge Case 2: Customer Texts Free-Form Instead of Tapping Button
A customer types “yes” or “ok” instead of tapping the “Confirm ✓” button. The button_payload will be empty and message_text will be “yes”.
Solution: Implement a lightweight intent classifier in your webhook handler. If button_payload is empty, run the message text through a simple keyword matcher ("yes"|"confirm"|"ok" → CONFIRM) before passing to Genesys. Log unmatched text for review.
Edge Case 3: Template Message Delivered Outside 24-Hour Window on a Re-engagement Campaign
Your CRM triggers a re-engagement campaign for customers inactive 30+ days. All messages are template-based (correct for this case), but some customers’ WhatsApp accounts are deactivated - deliveries fail silently.
Solution: Check the message delivery status webhook (notifyUrl callbacks). If status is REJECTED or UNDELIVERABLE, update the CRM record to flag the WhatsApp channel as invalid and fall back to SMS or email.