WhatsApp Cloud API
When to Use
Activate this skill when:
- Building or modifying WhatsApp messaging features
- Sending messages (text, media, templates, interactive)
- Processing incoming webhooks from WhatsApp
- Working with template messages or conversation windows
- Handling phone number formatting (E.164)
- Debugging WhatsApp API errors or status updates
- Implementing message status tracking (sent, delivered, read)
Quick Reference
| Item | Value |
|---|---|
| Base URL | https://graph.facebook.com/v21.0 |
| Send Message | POST /{phone-number-id}/messages |
| Upload Media | POST /{phone-number-id}/media |
| Auth | Authorization: Bearer {access-token} |
| Required Field | "messaging_product": "whatsapp" |
| Phone Format | E.164: +{country}{number} (e.g., +18091234567) |
| Rate Limit | 80 messages/second (Cloud API) |
Core API — Send Message
All messages go through a single endpoint:
POST https://graph.facebook.com/v21.0/{phone-number-id}/messages
Authorization: Bearer {access-token}
Content-Type: application/json
Response:
{
"messaging_product": "whatsapp",
"contacts": [{ "input": "+16505555555", "wa_id": "16505555555" }],
"messages": [{ "id": "wamid.HBgL..." }]
}
Message Types
| Type | type Field | Details |
|---|---|---|
| Text | text | Plain text, max 4096 chars, supports URL preview |
| Image | image | JPEG/PNG, max 5MB, optional caption |
| Video | video | MP4, max 16MB, optional caption |
| Audio | audio | AAC/MP3/OGG, max 16MB |
| Document | document | Any format, max 100MB, optional filename |
| Sticker | sticker | WebP, static 100KB / animated 500KB |
| Location | location | latitude, longitude, name, address |
| Contacts | contacts | Structured contact cards |
| Reaction | reaction | Emoji reaction to a message |
| Interactive | interactive | Buttons, lists, products |
| Template | template | Pre-approved message templates |
For full specs and code examples, see references/MESSAGING.md.
Webhooks
Your server receives POST requests for incoming messages and status updates.
Incoming message structure:
{
"object": "whatsapp_business_account",
"entry": [{
"changes": [{
"value": {
"messaging_product": "whatsapp",
"metadata": { "phone_number_id": "ID", "display_phone_number": "NUM" },
"contacts": [{ "profile": { "name": "John" }, "wa_id": "16315551234" }],
"messages": [{
"from": "16315551234",
"id": "wamid.ABC...",
"timestamp": "1683229471",
"type": "text",
"text": { "body": "Hello" }
}]
},
"field": "messages"
}]
}]
}
Status update types: sent → delivered → read | failed
For webhook verification, payload parsing, and all status types, see references/WEBHOOKS.md.
Conversation Window
- When a customer messages you, a 24-hour service window opens
- Inside the window: send any message type freely (service messages are FREE)
- Outside the window: only template messages can be sent (paid per message)
- No API endpoint to "close" a conversation — windows expire automatically
- Template messages open their own 24h window per category (marketing, utility, auth)
For full lifecycle, pricing, and category rules, see references/CONVERSATIONS.md.
Common Patterns
Send a text message
{
"messaging_product": "whatsapp",
"to": "+18091234567",
"type": "text",
"text": { "body": "Hello! How can we help you?" }
}
Send a template message
{
"messaging_product": "whatsapp",
"to": "+18091234567",
"type": "template",
"template": {
"name": "hello_world",
"language": { "code": "en_US" }
}
}
Mark a message as read
{
"messaging_product": "whatsapp",
"status": "read",
"message_id": "wamid.HBgL..."
}
Error Handling
| Code | Error | Action |
|---|---|---|
| 131030 | Recipient not on WhatsApp | Validate number before sending |
| 131047 | Re-engagement required | Send a template message first |
| 131050 | User stopped marketing messages | Respect opt-out, send only service/utility |
| 131056 | Pair rate limit hit | Slow down, implement backoff |
| 130429 | Rate limit exceeded | Queue messages, max 80/sec |
For full error reference and retry strategies, see references/ERROR-CODES.md.
Best Practices
- Always use E.164 phone format —
+{country}{number}, no spaces or dashes - Verify webhooks — Respond to GET challenge with
hub.challengevalue - Return 200 immediately on webhook POST — process asynchronously
- Store
wamidIDs — Needed for replies, reactions, and read receipts - Use template messages to re-engage after the 24h window expires
- Handle idempotency — Webhook may deliver the same event multiple times
- Check
wa_idvs input — The API normalizes phone numbers;wa_idis canonical - Rate limit awareness — 80 msg/sec for Cloud API; implement queue + backoff