What the WhatsApp Cloud API is
Cloud API replaced the older "On-Premises API" in 2022. Differences:
| Cloud API (current) | On-Premises (legacy) |
|---|
| Hosted by Meta | You host it |
|---|---|
| Setup in hours | Setup in weeks |
| No servers to maintain | VPS + Docker + maintenance |
| Same performance | More complex |
| Recommended for 99% of cases | Only specific enterprise cases |
Prerequisites
Before you start:
Path 1: Direct via Meta (complex)
If you want to go direct via Meta without a BSP:
That path works, but you still need to:bashcurl -X POST \ "https://graph.facebook.com/v18.0/<PHONE_NUMBER_ID>/messages" \ -H "Authorization: Bearer <ACCESS_TOKEN>" \ -H "Content-Type: application/json" \ -d '{ "messaging_product": "whatsapp", "to": "14155551234", "type": "template", "template": { "name": "hello_world", "language": { "code": "en_US" } } }'
- Approve templates manually in Meta Business Suite
- Handle token renewal
- Implement signed webhooks
- Manage errors and retries
- Migrate numbers if Meta limits you
Path 2: Via official BSP (recommended)
Official BSPs solve 90% of the complexity. Here's how with Zavu:
1. Create free account
zavu.dev/en β no credit card.2. Connect Meta Business
In the dashboard, click Add WhatsApp Sender and follow the OAuth flow. You're redirected to Meta, authorize Zavu as BSP, return. Takes 5 minutes.
3. Install SDK
bash# Node.js npm install @zavudev/sdk
bash# Python pip install zavudev
4. Send first message
Node.js / TypeScript:Python:typescriptimport Zavu from "@zavudev/sdk" const zavu = new Zavu({ apiKey: process.env.ZAVU_API_KEY }) const result = await zavu.messages.send({ to: "+14155551234", channel: "whatsapp", text: "Hi! First message via Zavu π" }) console.log(result.message.id, result.message.status)
pythonfrom zavudev import Zavu import os zavu = Zavu(api_key=os.environ["ZAVU_API_KEY"]) result = zavu.messages.send( to="+14155551234", channel="whatsapp", text="Hi! First message via Zavu π" ) print(result.message.id, result.message.status)
Free messages vs templates
The 24-hour window is central to the Cloud API:
- Within 24h of customer replying: send anything (text, media, buttons)
- Outside 24h: only approved templates
Sending a template
typescriptawait zavu.messages.send({ to: "+14155551234", channel: "whatsapp", messageType: "template", content: { templateId: "tpl_order_confirmed", templateVariables: { "1": "John", "2": "12345", "3": "$89.90" } } })
Template categories (US):
- Utility (transactional) β confirmations, reminders β $0.015
- Authentication (OTP) β codes β $0.005
- Marketing (promotional) β offers β $0.025
Rich media
typescript// Image await zavu.messages.send({ to: "+14155551234", channel: "whatsapp", messageType: "image", text: "Check out our new product!", content: { mediaUrl: "https://example.com/product.jpg" } }) // Document (PDF) await zavu.messages.send({ to: "+14155551234", channel: "whatsapp", messageType: "document", content: { mediaUrl: "https://example.com/invoice.pdf", filename: "invoice.pdf" } }) // Interactive buttons await zavu.messages.send({ to: "+14155551234", channel: "whatsapp", messageType: "buttons", text: "How can we help?", content: { buttons: [ { id: "buy", title: "I want to buy" }, { id: "question", title: "Ask a question" }, { id: "tracking", title: "Track order" } ] } })
Receiving messages (webhooks)
Configure an HTTPS endpoint in the Zavu Dashboard. Every incoming message generates a POST:
typescript// app/api/whatsapp-webhook/route.ts (Next.js) export async function POST(req: Request) { const event = await req.json() const signature = req.headers.get("x-zavu-signature") if (!verifySignature(signature, event)) { return new Response("Unauthorized", { status: 401 }) } switch (event.type) { case "message.inbound": console.log(Customer ${event.message.from}: ${event.message.text}) break case "message.delivered": console.log(Message ${event.message.id} delivered) break case "message.read": console.log(Message ${event.message.id} read) break } return new Response("ok") }
Common errors
131056 - Number not registered: number isn't enabled in WhatsApp Business. Check Meta Business Suite.131005 - Access denied: token wrong or expired. Use System User token (permanent) in production.132000 - Template not approved: template still in review. Wait for approval or use free text within the 24h window.Message doesn't arrive: destination may not have WhatsApp, or blocked your number. Check via message.failed webhook.Webhook doesn't respond in 2s: Meta retries up to 3 times. Structure your endpoint to respond fast and process async.WhatsApp Cloud API costs (US, 2026)
Meta charges per initiated 24h conversation, not per message:
| Category | Meta cost |
|---|
| Utility (transactional) | ~$0.015 |
|---|---|
| Authentication (OTP) | ~$0.005 |
| Marketing | ~$0.025 |
| Service (customer initiates) | Free |
Related resources
- How WhatsApp Business works
- WhatsApp Business auto-reply
- Build a WhatsApp AI agent with FastAPI
- Technical documentation
- Zavu vs Twilio for WhatsApp