The WhatsApp Cloud API has a documentation problem. Not because the information is missing – but because it's scattered across 47 subpages, contradicts itself, and hides the most important stuff (like how to get a token that lasts longer than 24 hours) somewhere deep in the Business Manager.
This guide is the opposite: One document. Everything included. Working code for Python, Node.js, and PHP. The pricing hacks for 2026. And the one thing Meta doesn't show you on the landing page – but that can get you banned instantly if you ignore it.
The Basics: What You Actually Need
Before you write a single line of code, you need four things:
Meta Developer Account – Free at developers.facebook.com
WhatsApp Business Account (WABA) – Created automatically during app setup
Verified Phone Number – Cannot already be used for personal WhatsApp
Permanent Access Token – This is where 80% of developers fail on their first attempt
The first three are click-click-done. The token is not.
Creating a Permanent Access Token (Step-by-Step)
The temporary token from the API setup lasts 24 hours. Then your bot is dead. For production, you need a System User token without an expiration date.
Here's how:
Open the Meta Business Manager
Go to Business Settings → Users → System Users
Click Add and create a new system user with the role Admin
Select the created user and click Assign Assets
Under "Apps," select your WhatsApp app and grant Full Control
Click Generate Token
Select these permissions:
whatsapp_business_messaging
whatsapp_business_management
Copy the token – it's only shown once
This token never expires. Treat it like a password: Never in the frontend, never in a Git repository, always as an environment variable.
The Messages Endpoint
All messages – text, images, documents, buttons – go through a single endpoint:
POST https://graph.facebook.com/v21.0/{PHONE_NUMBER_ID}/messages
The current API version is v21.0 (as of February 2026). You'll find your PHONE_NUMBER_ID in the Meta Developer Dashboard under WhatsApp → API Setup.
Authentication
Authorization: Bearer {YOUR_PERMANENT_TOKEN}
Content-Type: application/json
Session Messages vs. Template Messages
This is the biggest stumbling block for developers. WhatsApp strictly distinguishes between:
Session messages can only be sent if the customer has messaged you within the last 24 hours. Within this window, anything goes: free text, images, documents, buttons.
Template messages are predefined templates that Meta must approve first (24–48h review time). This is the only way to contact customers outside the 24h window.
The Utility Hack for 2026
Here's where it gets interesting: Utility templates (order confirmations, shipping updates, appointment reminders) are free when sent within an open 24h window.
This means: Customer messages you → You respond with a utility template instead of free text → Free instead of paid.
Sounds like a small detail? At 10,000 messages per month, this saves several hundred euros depending on the region. Our WhatsApp Business API Guide explains the exact cost structure in detail.
Code Examples: Sending a Text Message
cURL (for testing)
curl -X POST "https://graph.facebook.com/v21.0/PHONE_NUMBER_ID/messages" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "14155551234",
"type": "text",
"text": {
"preview_url": false,
"body": "Your order #12345 is on its way!"
}
}'
The phone number must be in E.164 format: country code without +, no spaces, no leading zero (so 14155551234, not +1 (415) 555-1234).
Python
import requests
import os
def send_whatsapp_message(phone_number: str, message: str) -> dict:
"""
Sends a WhatsApp text message via the Cloud API.
Args:
phone_number: Recipient in format 14155551234
message: The message text (max 4096 characters)
Returns:
API response with message_id on success
"""
url = f"https://graph.facebook.com/v21.0/{os.environ['WA_PHONE_NUMBER_ID']}/messages"
headers = {
"Authorization": f"Bearer {os.environ['WA_ACCESS_TOKEN']}",
"Content-Type": "application/json"
}
payload = {
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": phone_number,
"type": "text",
"text": {
"preview_url": False,
"body": message
}
}
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status() # Raises exception on 4xx/5xx
return response.json()
# Example
result = send_whatsapp_message("14155551234", "Your order is on its way!")
print(f"Message sent: {result['messages'][0]['id']}")
Node.js
const axios = require('axios');
async function sendWhatsAppMessage(phoneNumber, message) {
const url = `https://graph.facebook.com/v21.0/${process.env.WA_PHONE_NUMBER_ID}/messages`;
const payload = {
messaging_product: 'whatsapp',
recipient_type: 'individual',
to: phoneNumber,
type: 'text',
text: {
preview_url: false,
body: message
}
};
const response = await axios.post(url, payload, {
headers: {
'Authorization': `Bearer ${process.env.WA_ACCESS_TOKEN}`,
'Content-Type': 'application/json'
}
});
return response.data;
}
// Example
sendWhatsAppMessage('14155551234', 'Your order is on its way!')
.then(res => console.log('Sent:', res.messages[0].id))
.catch(err => console.error('Error:', err.response?.data || err.message));
PHP
<?php
function sendWhatsAppMessage(string $phoneNumber, string $message): array
{
$url = 'https://graph.facebook.com/v21.0/' . getenv('WA_PHONE_NUMBER_ID') . '/messages';
$payload = [
'messaging_product' => 'whatsapp',
'recipient_type' => 'individual',
'to' => $phoneNumber,
'type' => 'text',
'text' => [
'preview_url' => false,
'body' => $message
]
];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('WA_ACCESS_TOKEN'),
'Content-Type: application/json'
],
CURLOPT_RETURNTRANSFER => true
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 400) {
throw new Exception("API Error: $response");
}
return json_decode($response, true);
}
// Example
$result = sendWhatsAppMessage('14155551234', 'Your order is on its way!');
echo "Sent: " . $result['messages'][0]['id'];
Interactive Messages: Buttons & Lists
This is where it gets interesting. Buttons massively increase interaction rates – users prefer tapping a button over typing a response. WhatsApp offers two types:
Reply Buttons (max 3 buttons)
payload = {
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "14155551234",
"type": "interactive",
"interactive": {
"type": "button",
"header": {
"type": "text",
"text": "Order Status"
},
"body": {
"text": "Your order #12345 is out for delivery. What would you like to do?"
},
"footer": {
"text": "Powered by Chatarmin"
},
"action": {
"buttons": [
{
"type": "reply",
"reply": {
"id": "track_order",
"title": "📍 Track Shipment"
}
},
{
"type": "reply",
"reply": {
"id": "contact_support",
"title": "💬 Contact Support"
}
},
{
"type": "reply",
"reply": {
"id": "change_address",
"title": "📝 Change Address"
}
}
]
}
}
}
List Messages (max 10 options)
Perfect for selection menus with more options:
payload = {
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "14155551234",
"type": "interactive",
"interactive": {
"type": "list",
"header": {
"type": "text",
"text": "Help & Support"
},
"body": {
"text": "How can we help you? Select a topic:"
},
"action": {
"button": "Select Topic",
"sections": [
{
"title": "Orders",
"rows": [
{"id": "order_status", "title": "Order Status", "description": "Where is my package?"},
{"id": "order_cancel", "title": "Cancellation", "description": "Cancel my order"},
{"id": "order_return", "title": "Returns", "description": "Return an item"}
]
},
{
"title": "Products",
"rows": [
{"id": "product_info", "title": "Product Advice", "description": "Help choosing"},
{"id": "product_stock", "title": "Availability", "description": "Is item X in stock?"}
]
}
]
}
}
}
The button ID (track_order, order_status, etc.) is returned as a webhook payload when the user clicks. This is how you control your bot flow. Our Chatbot API Guide shows how to set up webhooks and process responses.
Sending Template Messages
For messages outside the 24h window, you need an approved template:
payload = {
"messaging_product": "whatsapp",
"to": "14155551234",
"type": "template",
"template": {
"name": "order_shipped",
"language": {
"code": "en"
},
"components": [
{
"type": "body",
"parameters": [
{"type": "text", "text": "John"}, # {{1}} = Customer name
{"type": "text", "text": "12345"}, # {{2}} = Order number
{"type": "text", "text": "FedEx Express"} # {{3}} = Shipping method
]
}
]
}
}
You create templates in WhatsApp Manager under Account Tools → Message Templates. Expect 24–48 hours review time from Meta.
Messaging Limits: Why Your Messages Suddenly Stop
New WhatsApp Business numbers start with a limit of 250 messages per 24 hours. That's enough for testing – but not for production.
Here's how to scale:
| Tier | Limit/24h | Requirement |
|---|---|---|
| Tier 1 | 1,000 | Phone number verified |
| Tier 2 | 10,000 | Business verified + good quality rating |
| Tier 3 | 100,000 | Stable high quality over several weeks |
| Unlimited | ∞ | Consistently high quality, no spam feedback |
You can see your quality rating in WhatsApp Manager under Phone Numbers → Quality Rating. If it drops to "Low," your limit is automatically reduced or your number gets suspended.
AI Policy 2026: What You Need to Know
Warning: Since January 15, 2026, Meta prohibits so-called "General Purpose AI Assistants" on WhatsApp. This affects open ChatGPT wrappers without a clear business purpose – bots that answer any arbitrary question.
Allowed are task-specific bots with a defined scope:
Customer service bots (FAQ, ticket status)
Sales bots (product advice, orders)
Booking bots (appointments, reminders)
Prohibited are:
"Ask me anything" bots without business context
Bots that impersonate human employees
At Chatarmin, we focus precisely on this compliance: Every bot has a clear purpose, defined flows, and transparent bot labeling. Our WhatsApp Automation Guide shows what this looks like in practice.
Error Handling: The Most Common Error Codes
| Code | Meaning | Solution |
|---|---|---|
| 131030 | Invalid recipient number | Check E.164 format (14155551234) |
| 131031 | No active 24h window | Use a template message |
| 131047 | Re-engagement required | Customer must reply first (opt-in) |
| 131053 | Media URL not reachable | Make URL publicly accessible |
| 130429 | Rate limit reached | Throttle send rate to max 80 msg/sec |
| 131026 | Message undeliverable | Number not on WhatsApp or blocked |
| 368 | Temporary suspension | Wait 24h, then improve quality |
For persistent errors: Check WhatsApp Manager → Insights → Logs.
Real-World Usage: How Companies Use the API
The code snippets above are just the beginning. In practice, there's a system behind it that automatically decides: Which message? To whom? When? With which template?
Waterdrop processes thousands of WhatsApp inquiries daily – automated first responses, intelligent routing to the right teams, escalation for complex cases.
Luxusbetten24 uses the API for the complete customer journey: Order confirmation → Shipping update → Delivery notification → Review request. All automated, all measurable.
Cusbclo shows how welcome flows work: New customer writes → Automatic greeting with buttons → Qualification questions → Direct routing to the matching product.
Build vs. Buy: An Honest Assessment
You can build everything yourself. Set up servers, configure webhooks, implement retry logic for failed messages, status tracking, template management with Meta sync, GDPR-compliant opt-in documentation.
Realistically: That's 3–6 months of development time for a team doing it for the first time. Plus ongoing maintenance with API updates (Meta releases a new version roughly every 3 months).
At Chatarmin, we solve exactly this: The technical complexity disappears behind a UI. Flow builder instead of code for every message type. Template management with direct Meta integration. Analytics that show which messages convert and which don't.
That doesn't mean API knowledge is useless – quite the opposite. Those who understand what's happening under the hood make better decisions. But there's a significant difference in time and budget between "understanding" and "having to maintain it yourself."
FAQ: The 10 Most Common Questions
Is the WhatsApp Business API free?
The API itself has no base fee, but Meta charges per conversation (conversation-based pricing). However, the first 1,000 service conversations per month are free.
What's the difference between Cloud API and On-Premise API?
The Cloud API is hosted by Meta and scales automatically. The On-Premise API must be operated on your own servers (Docker). For 99% of companies, the Cloud API is the standard in 2026 – Meta itself recommends migrating.
How do I get a permanent access token?
In Meta Business Manager under System Users: Create a new admin user, assign the app, generate a token with the permissions whatsapp_business_messaging and whatsapp_business_management. This token doesn't expire.
What are WhatsApp Messaging Limits?
New numbers start with 250 or 1,000 messages per 24 hours. Through business verification and high quality ratings, this limit automatically scales to 10k, 100k, or unlimited.
Are AI chatbots allowed on WhatsApp?
Yes, as long as they have a clear business purpose (support, sales, booking). Since January 2026, pure "General Purpose AI" bots without a specific focus are prohibited according to Meta's guidelines.
Can I send WhatsApp newsletters without opt-in?
No. This leads to immediate suspension of your number. Every recipient must have explicitly consented to receiving messages – documented and GDPR-compliant.
How much does a WhatsApp Business message cost in 2026?
Costs vary by country and category. Marketing messages to the US cost approximately $0.025, utility messages around $0.015. Service replies within the 24h window are free.
What happens when the 24-hour window expires?
You can no longer send free-text messages. To reopen the conversation, you need an approved template – which then costs depending on the category.
Do I need my own phone number for the API?
Yes. The number cannot be simultaneously linked to the personal WhatsApp app or WhatsApp Business app on a phone. One number = one channel.
Why are my WhatsApp templates being rejected?
Most common reasons: Promotional content in utility templates, missing variable examples, violation of commerce policies (e.g., alcohol, tobacco), or the template name contains special characters.
Conclusion
The WhatsApp Cloud API isn't technically complicated – one POST request, JSON payload, done. The complexity lies elsewhere: Token management, the 24h window, messaging limits, template approval, AI policy.
For a quick test, the code snippets above are enough. For production with thousands of customers, you need more: Monitoring, error handling, compliance checks, retry logic.
Next step: Test the API with your test number in the Meta Developer Dashboard. Once the first message arrives, decide: Build it yourself or buy it ready-made?








