Skip to main content

Designing & Approving WhatsApp Templates

Business-initiated WhatsApp messages must use a pre-approved template. This guide shows how to structure templates, pick the right category, and avoid the most common rejection reasons.

🚀 Quick Start​

curl -X POST https://api.afriroute.ai/api/v1/whatsapp/templates \
-H "Authorization: Bearer $AFRIROUTE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "order_shipped",
"category": "UTILITY",
"language": "en",
"body": "Hi {{1}}, your order {{2}} has shipped and arrives {{3}}."
}'

🗂️ Categories​

WhatsApp routes and prices templates by category. Choosing wrong is the top cause of rejection.

CategoryUse forExamples
UTILITYTransaction follow-upsOrder updates, OTP, appointment reminders
MARKETINGPromotionsOffers, newsletters, re-engagement
AUTHENTICATIONOne-time passcodesLogin codes, verification

Putting promotional content in a UTILITY template will get it rejected or recategorized.

🔤 Variables​

Variables use {{1}}, {{2}} numbered placeholders, filled at send time.

await fetch('https://api.afriroute.ai/api/v1/whatsapp/send', {
method: 'POST',
headers: {
'Authorization': 'Bearer $AFRIROUTE_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
to: '+254712345678',
template: 'order_shipped',
language: 'en',
variables: ['Amina', '#A1029', 'Friday']
})
});
import requests
requests.post(
'https://api.afriroute.ai/api/v1/whatsapp/send',
headers={'Authorization': 'Bearer $AFRIROUTE_API_KEY'},
json={
'to': '+234803000000',
'template': 'order_shipped',
'language': 'en',
'variables': ['Bola', '#A1030', 'Monday']
}
)

Rules that cause rejection:

  • No two variables adjacent: {{1}} {{2}} is fine, {{1}}{{2}} is not.
  • A template cannot be only variables — it needs fixed text.
  • Variables cannot start or end the message body.

🧱 Components​

A template can include a header (text, image, or document), a body, a footer, and buttons.

{
"name": "appointment_reminder",
"category": "UTILITY",
"language": "en",
"header": { "type": "text", "text": "Appointment Reminder" },
"body": "Hi {{1}}, your visit is on {{2}} at {{3}}.",
"footer": "Reply CANCEL to reschedule",
"buttons": [
{ "type": "QUICK_REPLY", "text": "Confirm" },
{ "type": "QUICK_REPLY", "text": "Reschedule" }
]
}

💡 Best Practices​

  • Match content to category — the most common rejection reason.
  • Add sample variable values on submission so reviewers see real intent.
  • Keep body concise — long, salesy UTILITY templates get downgraded.
  • Pre-translate and submit a template per language you support.
  • Re-use approved templates instead of creating near-duplicates.

⚠️ Handling Rejections​

{
"name": "order_shipped",
"status": "REJECTED",
"reason": "INVALID_FORMAT",
"detail": "Body ends with a variable"
}

Fix the flagged issue and resubmit — name reuse is allowed once the prior version is rejected.


Last Updated: May 2026