Introduction
WhatsApp has over 2 billion active users worldwide, making it one of the most important communication channels for businesses. The WhatsApp Business API enables companies to send and receive messages programmatically, automate customer interactions, and integrate WhatsApp into their existing systems.
This guide covers everything you need to know to build WhatsApp automation: from initial setup through message templates, webhook handling, and building a functional customer support bot.
Understanding WhatsApp Business API
The WhatsApp Business API is different from the WhatsApp Business app. The app is for small businesses to manage conversations manually. The API is for businesses that need to automate and scale WhatsApp communication.
Key Concepts
- Business Solution Provider (BSP): Meta doesn’t provide direct API access. You use a BSP like Twilio, 360dialog, or MessageBird as an intermediary
- Phone Number: You need a dedicated phone number registered with WhatsApp Business
- Message Templates: Pre-approved message formats for initiating conversations
- Session Messages: Messages sent within a 24-hour window after a customer messages you
- Webhooks: HTTP endpoints that receive incoming messages and status updates
Pricing Model
WhatsApp charges per conversation, not per message:
- Business-initiated conversations: You send a template message to start the conversation
- User-initiated conversations: The customer messages you first
- Pricing varies by country: Different rates for different regions
Setting Up WhatsApp Business API
Step 1: Choose a Business Solution Provider
Popular BSPs include:
- Twilio: Well-documented API, good developer experience
- 360dialog: Direct Meta partner, competitive pricing
- MessageBird: Multi-channel platform with WhatsApp support
- Meta Cloud API: Meta’s own cloud-hosted API (simplest setup)
Step 2: Register Your Phone Number
# Example using Meta Cloud API
import requests
GRAPH_API_URL = "https://graph.facebook.com/v18.0"
ACCESS_TOKEN = "your-access-token"
PHONE_NUMBER_ID = "your-phone-number-id"
# Register phone number
response = requests.post(
f"{GRAPH_API_URL}/{PHONE_NUMBER_ID}/register",
headers={"Authorization": f"Bearer {ACCESS_TOKEN}"},
json={
"messaging_product": "whatsapp",
"pin": "123456" # 6-digit PIN you chose
}
)
Step 3: Configure Webhooks
Set up a webhook endpoint to receive incoming messages:
from fastapi import FastAPI, Request, Response
import json
app = FastAPI()
VERIFY_TOKEN = "your_verify_token"
@app.get("/webhook")
async def verify_webhook(hub_mode: str, hub_challenge: str, hub_verify_token: str):
"""Verify webhook with Meta."""
if hub_mode == "subscribe" and hub_verify_token == VERIFY_TOKEN:
return Response(content=hub_challenge, status_code=200)
return Response(status_code=403)
@app.post("/webhook")
async def receive_webhook(request: Request):
"""Handle incoming WhatsApp messages."""
body = await request.json()
# Process the webhook
if body.get("object") == "whatsapp_business_account":
for entry in body.get("entry", []):
for change in entry.get("changes", []):
if change.get("field") == "messages":
handle_message(change.get("value"))
return Response(status_code=200)
Message Templates
You cannot send arbitrary messages to customers outside the 24-hour session window. You must use pre-approved message templates.
Creating a Template
def create_message_template():
"""Create a message template for approval."""
template = {
"name": "order_confirmation",
"language": "en_US",
"category": "UTILITY",
"components": [
{
"type": "BODY",
"text": "Hi {{1}}, your order {{2}} has been confirmed. "
"Expected delivery: {{3}}. Track your order at {{4}}."
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "QUICK_REPLY",
"text": "Track Order"
},
{
"type": "QUICK_REPLY",
"text": "Contact Support"
}
]
}
]
}
response = requests.post(
f"{GRAPH_API_URL}/{PHONE_NUMBER_ID}/message_templates",
headers={"Authorization": f"Bearer {ACCESS_TOKEN}"},
json=template
)
return response.json()
Sending a Template Message
def send_template_message(phone_number: str, order_data: dict):
"""Send an order confirmation template message."""
message = {
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": phone_number,
"type": "template",
"template": {
"name": "order_confirmation",
"language": {"code": "en_US"},
"components": [
{
"type": "body",
"parameters": [
{"type": "text", "text": order_data["customer_name"]},
{"type": "text", "text": order_data["order_id"]},
{"type": "text", "text": order_data["delivery_date"]},
{"type": "text", "text": order_data["tracking_url"]}
]
}
]
}
}
response = requests.post(
f"{GRAPH_API_URL}/{PHONE_NUMBER_ID}/messages",
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json"
},
json=message
)
return response.json()
Handling Incoming Messages
Parsing Message Types
WhatsApp supports text, image, audio, document, and location messages:
def handle_message(value: dict):
"""Process incoming WhatsApp message."""
messages = value.get("messages", [])
if not messages:
# Could be a status update (sent, delivered, read)
handle_status(value.get("statuses", []))
return
message = messages[0]
sender_phone = message["from"]
message_type = message["type"]
if message_type == "text":
text = message["text"]["body"]
handle_text_message(sender_phone, text)
elif message_type == "interactive":
# Handle button clicks and list selections
interactive = message["interactive"]
if interactive["type"] == "button_reply":
button_id = interactive["button_reply"]["id"]
handle_button_response(sender_phone, button_id)
elif message_type == "image":
image_id = message["image"]["id"]
caption = message["image"].get("caption", "")
handle_image_message(sender_phone, image_id, caption)
elif message_type == "location":
latitude = message["location"]["latitude"]
longitude = message["location"]["longitude"]
handle_location_message(sender_phone, latitude, longitude)
Sending Reply Messages
Within the 24-hour session window, you can send free-form messages:
def send_text_message(phone_number: str, text: str):
"""Send a text message to a customer."""
message = {
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": phone_number,
"type": "text",
"text": {"body": text}
}
response = requests.post(
f"{GRAPH_API_URL}/{PHONE_NUMBER_ID}/messages",
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json"
},
json=message
)
return response.json()
Building a Customer Support Bot
Let’s build a bot that handles common customer queries using AI and routes complex issues to human agents.
Bot Architecture
from dataclasses import dataclass
from enum import Enum
class MessageType(Enum):
TEXT = "text"
INTERACTIVE = "interactive"
@dataclass
class CustomerContext:
phone_number: str
name: str = ""
conversation_state: str = "initial"
order_id: str = ""
issue_type: str = ""
class WhatsAppBot:
def __init__(self, llm_client, database):
self.llm = llm_client
self.db = database
self.contexts = {} # In production, use Redis
def get_context(self, phone_number: str) -> CustomerContext:
if phone_number not in self.contexts:
self.contexts[phone_number] = CustomerContext(phone_number=phone_number)
return self.contexts[phone_number]
def process_message(self, phone_number: str, text: str):
context = self.get_context(phone_number)
# Route based on conversation state
if context.conversation_state == "initial":
return self.handle_initial(context, text)
elif context.conversation_state == "awaiting_order_id":
return self.handle_order_lookup(context, text)
elif context.conversation_state == "in_support":
return self.handle_support(context, text)
Intent Detection with AI
def detect_intent(self, text: str) -> str:
"""Use LLM to detect user intent."""
prompt = f"""Classify this customer message into one of these categories:
- order_status: Asking about order status
- product_inquiry: Asking about products
- complaint: Filing a complaint
- general: General question
- human: Requesting human agent
Message: "{text}"
Respond with only the category name."""
intent = self.llm.invoke(prompt).strip().lower()
return intent
def handle_initial(self, context: CustomerContext, text: str):
intent = self.detect_intent(text)
if intent == "order_status":
context.conversation_state = "awaiting_order_id"
return "Please enter your order ID (e.g., ORD-12345)"
elif intent == "product_inquiry":
return self.handle_product_inquiry(text)
elif intent == "complaint":
context.conversation_state = "in_support"
context.issue_type = "complaint"
return "I'm sorry to hear you're having an issue. " \
"Please describe the problem in detail."
elif intent == "human":
return self.escalate_to_human(context)
else:
return self.handle_general_query(text)
AI-Powered Responses
def handle_general_query(self, text: str) -> str:
"""Generate AI response using knowledge base."""
# Retrieve relevant context from knowledge base
relevant_docs = self.db.search_knowledge_base(text, top_k=3)
context = "\n".join([doc.content for doc in relevant_docs])
prompt = f"""You are a helpful customer support assistant.
Use the following knowledge base to answer the customer's question.
If you don't know the answer, say you'll connect them with a human agent.
Knowledge Base:
{context}
Customer Question: {text}
Answer:"""
response = self.llm.invoke(prompt)
return response
Interactive Messages with Buttons
def send_interactive_message(phone_number: str, body_text: str, buttons: list):
"""Send a message with quick reply buttons."""
message = {
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": phone_number,
"type": "interactive",
"interactive": {
"type": "button",
"body": {"text": body_text},
"action": {
"buttons": [
{
"type": "reply",
"reply": {"id": btn["id"], "title": btn["title"]}
}
for btn in buttons
]
}
}
}
response = requests.post(
f"{GRAPH_API_URL}/{PHONE_NUMBER_ID}/messages",
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json"
},
json=message
)
return response.json()
# Usage
send_interactive_message(
phone_number="+1234567890",
body_text="How can I help you today?",
buttons=[
{"id": "track_order", "title": "Track Order"},
{"id": "product_info", "title": "Product Info"},
{"id": "support", "title": "Support"}
]
)
Best Practices
-
Respect the 24-hour rule: You can only send free-form messages within 24 hours of the customer’s last message. Use templates for business-initiated conversations.
-
Get user opt-in: Always get explicit consent before sending marketing messages. WhatsApp can suspend accounts that send unsolicited messages.
-
Implement rate limiting: WhatsApp has throughput limits. Space out messages to avoid being rate-limited.
-
Handle media downloads: When receiving images or documents, download and store them promptly. Media URLs expire.
-
Log all conversations: Keep records for customer service, compliance, and analytics.
-
Provide human escalation: Always give customers a path to reach a human agent. AI bots should recognize when they can’t help and escalate.
Conclusion
WhatsApp Business API automation opens up a powerful channel for customer engagement. The combination of message templates for proactive outreach, session messages for real-time conversation, and AI for intelligent responses creates a robust customer communication system.
Start with a simple bot that handles the most common queries, then gradually add AI capabilities and more sophisticated routing. The key is to always provide value to the customer while respecting WhatsApp’s rules and limitations. With proper implementation, WhatsApp automation can significantly reduce support costs while improving customer satisfaction.