Skip to content

Reference · Reviewed September 2026

WhatsApp Business API documentation: the practical map

Meta's documentation is complete but spread across dozens of pages. This is the condensed version for developers who need to send, receive and track WhatsApp messages: what each part is, the exact requests, and where the traps are.

How the documentation is organised

Everything runs through the Graph API. The base URL is https://graph.facebook.com/v22.0/ and the version is part of the path. Meta’s docs split into:

  • Cloud API: sending messages, media, and receiving webhooks. You will live here.
  • Business Management API: templates, phone numbers, business profile, analytics.
  • Flows: structured forms inside chats.
  • Reference: error codes, webhook payloads, rate limits, and the changelog per version.

If you only read one Meta page, read the Cloud API “Send Messages” reference. Everything below is arranged in the order you actually hit it.

Authentication

Every request carries a bearer token in the Authorization header. There are two kinds and the difference costs people a day.

Access token types for the WhatsApp Business API
TokenLifetimeUse it for
Temporary user token24 hoursTrying the API from the dashboard. Nothing else.
System user tokenNever expires (or 60 days if you choose)Production. Create a system user in Business Settings, assign the WhatsApp app and WABA, generate the token with whatsapp_business_messaging and whatsapp_business_management permissions.
Authorization: Bearer EAAG...your_system_user_token
Error code 190 means the token expired or was revoked. If you see it on day two, you shipped the temporary token.

The IDs you need

The API never takes your phone number as an identifier. It takes IDs. All of them are in the WhatsApp section of your app dashboard.

Identifiers used by the WhatsApp Business API
IDWhat it identifiesWhere it appears
WABA IDYour WhatsApp Business Account, which owns numbers and templatesTemplate and analytics endpoints
Phone number IDOne registered number on that accountEvery send-message and media request
App ID and App secretYour Meta developer appWebhook signature verification
wa_idA customer, as the digits of their number without a plus signWebhook payloads and the contacts array in send responses
wamidOne messageReturned on send, then used in status webhooks and for marking read

Core endpoints

These cover almost every integration. Replace the placeholders with your IDs.

Core WhatsApp Business API endpoints
Method and pathPurpose
POST /{PHONE_NUMBER_ID}/messagesSend any message: text, template, media, interactive, reaction, location, contacts. Also marks messages as read.
POST /{PHONE_NUMBER_ID}/mediaUpload media, get a media ID.
GET /{MEDIA_ID}Get a short-lived download URL for media a customer sent.
DELETE /{MEDIA_ID}Delete uploaded media.
GET /{WABA_ID}/message_templatesList templates with status and category.
POST /{WABA_ID}/message_templatesCreate a template for review.
DELETE /{WABA_ID}/message_templates?name=…Delete a template by name.
GET /{WABA_ID}/phone_numbersList numbers with quality rating and messaging limit.
POST /{PHONE_NUMBER_ID}/registerRegister a number on the Cloud API with a 6-digit PIN.
POST /{PHONE_NUMBER_ID}/whatsapp_business_profileUpdate address, description, email, website and profile picture.
GET /{WABA_ID}?fields=analytics…Sent and delivered counts by day, and conversation analytics for billing.

Message types

The type field in a send request selects the payload shape. Free-form types only work inside the 24-hour window after a customer’s message.

WhatsApp message types and when they can be sent
typeWhat it sendsOutside 24-hour window?
templateAn approved template with filled variablesYes
textPlain text, optional link previewNo
image, video, audio, document, stickerMedia by ID or public URL, with optional captionNo
interactiveReply buttons (up to 3), list menus (up to 10 rows), CTA URL buttons, FlowsNo
reactionAn emoji reaction to a message IDNo
locationA pin with name and addressNo
contactsOne or more vCardsNo

Sending a message

One endpoint, three languages. Text first, then a template.

curl

curl -X POST "https://graph.facebook.com/v22.0/$PHONE_NUMBER_ID/messages" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "messaging_product": "whatsapp",
    "recipient_type": "individual",
    "to": "919876543210",
    "type": "text",
    "text": { "preview_url": false, "body": "Your order is out for delivery today." }
  }'

Node.js

const res = await fetch(
  `https://graph.facebook.com/v22.0/${PHONE_NUMBER_ID}/messages`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      messaging_product: "whatsapp",
      to: "919876543210",
      type: "template",
      template: {
        name: "order_shipped",
        language: { code: "en" },
        components: [
          {
            type: "body",
            parameters: [
              { type: "text", text: "Priya" },
              { type: "text", text: "BA-10428" },
            ],
          },
        ],
      },
    }),
  },
);
const data = await res.json(); // data.messages[0].id is the wamid

Python

import requests

url = f"https://graph.facebook.com/v22.0/{PHONE_NUMBER_ID}/messages"
headers = {"Authorization": f"Bearer {TOKEN}"}
payload = {
    "messaging_product": "whatsapp",
    "to": "919876543210",
    "type": "interactive",
    "interactive": {
        "type": "button",
        "body": {"text": "Confirm your appointment for Tuesday 4pm?"},
        "action": {
            "buttons": [
                {"type": "reply", "reply": {"id": "yes", "title": "Confirm"}},
                {"type": "reply", "reply": {"id": "no", "title": "Reschedule"}},
            ]
        },
    },
}
r = requests.post(url, json=payload, headers=headers, timeout=10)
r.raise_for_status()
print(r.json()["messages"][0]["id"])

The response is the same for every type: a contacts array with the resolved wa_id, and a messages array with the wamid. Store the wamid. Delivery and read receipts refer to it.

Media

Send media either by a public HTTPS URL or by uploading first and sending the returned ID. Uploading is more reliable and the ID is reusable for 30 days.

curl -X POST "https://graph.facebook.com/v22.0/$PHONE_NUMBER_ID/media" \
  -H "Authorization: Bearer $TOKEN" \
  -F "messaging_product=whatsapp" \
  -F "type=image/jpeg" \
  -F "file=@./invoice.jpg"
# -> { "id": "1234567890" }

Size limits: images 5 MB, video 16 MB, audio 16 MB, documents 100 MB, stickers 500 KB (static). Incoming media arrives in the webhook as an ID; call GET /{MEDIA_ID} to get a download URL that is valid for five minutes, then download it with the same bearer token.

Templates

Templates are created against the WABA, reviewed by Meta, and then referenced by name and language code in send requests.

curl -X POST "https://graph.facebook.com/v22.0/$WABA_ID/message_templates" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "order_shipped",
    "category": "UTILITY",
    "language": "en",
    "components": [
      { "type": "BODY",
        "text": "Hi {{1}}, your order {{2}} has shipped. Track it here: {{3}}",
        "example": { "body_text": [["Priya", "BA-10428", "https://example.com/t/1"]] } },
      { "type": "FOOTER", "text": "Reply STOP to opt out" }
    ]
  }'
  • Categories are MARKETING, UTILITY and AUTHENTICATION. Meta may recategorise a template on review, which changes what it costs.
  • The name is lowercase with underscores, and the name plus language code is the unique key. en and en_US are different templates.
  • Provide examples for every variable or the review fails.
  • Status changes (APPROVED, REJECTED, PAUSED) arrive on the message_template_status_update webhook.

Working examples by category are in the template gallery, and you can draft one with placeholders in the template generator.

Webhooks

Meta pushes events to a URL you register: incoming messages, delivery statuses, template status changes, and account updates. You verify the endpoint once with a GET challenge, then receive POSTs signed with your app secret.

// A status update for a message you sent
{ "object": "whatsapp_business_account",
  "entry": [{ "changes": [{ "field": "messages", "value": {
    "statuses": [{ "id": "wamid.HBg…", "status": "delivered",
                   "timestamp": "1725000000", "recipient_id": "919876543210" }]
  }}]}]}

Subscribe to the messages field for both messages and statuses. Return 200 fast, process in the background, and dedupe on the message ID. The full walkthrough, with verification and signature code, is in the webhook guide.

Errors

Errors come back as HTTP 4xx with a JSON body containing error.code, error.message and often error.error_data.details, which is the useful part.

The WhatsApp API errors developers hit first
CodeMeaningFix
190Token expired or invalidUse a system user token
131030Recipient not in allowed listAdd the test recipient, or move the app to live
131047Re-engagement messageThe 24-hour window closed. Send a template.
132001Template does not existCheck the name and the exact language code
132012Template parameter mismatchSend exactly as many parameters as placeholders
130429Rate limit hitBack off and retry; check the number's messaging limit
131026Message undeliverableRecipient may not be on WhatsApp, or has blocked you

The longer list with explanations is in our error codes reference.

Rate limits and throughput

  • Throughput: 80 messages per second per number by default, upgradable to 1,000 per second automatically for numbers with high volume and good quality.
  • Messaging limits: 250 business-initiated conversations per 24 hours for an unverified number, then 1K, 10K, 100K and unlimited tiers as volume and quality grow.
  • Business Management API: 200 calls per hour per app per WABA. Cache template lists rather than fetching on every send.
  • Pair rate limit: the same business sending the same customer too many messages in a short time gets throttled with code 131056.

Go-live checklist

  1. Permanent system user token in your secrets manager, not the temporary one.
  2. Business verification submitted, so the 250 limit lifts.
  3. Display name approved and profile filled in.
  4. Webhook verified, signature checked, responding in under a second.
  5. Templates approved in every language you send.
  6. Opt-in recorded for every number you will message first.
  7. A payment method on the WABA, or on the platform that bills you.
  8. Status webhooks stored, so you can see delivered and read, not just sent.
If your goal is a team inbox and campaigns rather than a custom integration, every item on that list is already done inside Boldally Chat. The API is the same, at Meta’s rates with no markup, and you keep your WABA if you leave.

Endpoints, limits and payload shapes are defined by Meta and change across API versions. Check Meta for Developers for the current specification before relying on any value here.

Common questions

Where is the official WhatsApp Business API documentation?

On Meta for Developers under WhatsApp, Cloud API. This page is a map of it, written for people who need to ship something, with the parts you will use most and links back to Meta for the full reference. Meta's docs are the source of truth and change with each API version.

Which API version should I use?

The latest Graph API version at the time you build, written into the URL as v22.0 or similar. Meta supports each version for about two years. Pin the version in your code and read the changelog before upgrading.

Can I test without a real business number?

Yes. Every new WhatsApp app in the Meta developer dashboard comes with a test number and a temporary token. You can send to up to five verified recipient numbers. Production needs your own number and a permanent token.

Do I need a Business Solution Provider to use the API?

No. Since the Cloud API launched, any business can register directly with Meta and call the API. A provider or platform saves you building the inbox, template management, webhook handling and reporting on top of it.

Why does my message send but never arrive?

A 200 response means Meta accepted the request, not that it was delivered. Check the status webhook for the message ID. Common causes are an unapproved or paused template, the recipient not being on WhatsApp, a closed 24-hour window for a free-form message, or a number that has hit its messaging limit.

Keep reading

Put your WhatsApp to work.

Plans from ₹999 a month, billed through Razorpay. No setup call to sit through, and you can cancel anytime.