WhatsApp Cloud API: what it is and how to integrate it (number, token, webhook and first message)
The WhatsApp Cloud API is the official WhatsApp API for businesses, hosted in Meta’s cloud: your system sends messages over HTTP and receives replies and statuses by webhook. A direct integration needs four pieces: a registered number, a permanent token, the first message and a webhook that survives redeliveries. Here is what Meta’s documentation says about each one today, and the traps you meet in production.

What you'll take away
- What the Cloud API is, what has to exist before any code and the new 2026 account model
- Permanent token, number registration, the first message on Graph API v26.0 and the wamid you get back
- Signed webhooks, redeliveries, media, limits, errors and the choice between integrating directly or through a Meta partner
What the WhatsApp Cloud API is
The Cloud API is the part of the WhatsApp Business Platform that sends and receives messages from a system rather than a phone. It uses Meta’s Graph API to send and webhooks to report what comes in and what happened to each message. When people say “WhatsApp API” or talk about the official WhatsApp Business API, this is what they mean.
There used to be a version installed on the business’s own servers, the On-Premises API, but Meta moved the whole platform to the cloud. The sunset page says that since July 1, 2024, new numbers can only be registered on the Cloud API, and that the last On-Premises version, v2.63, expired on October 23, 2025.
The difference from the WhatsApp Business app is that the API has no screen: it’s a set of endpoints, and the conversation shows up wherever your system puts it. In return, it opens up what the app lacks: agents without a device limit, automation, store and CRM integrations and interactive messages such as buttons, lists and WhatsApp Flows forms.
What has to exist before any code
Meta’s getting started guide asks for a developer account, an app on Meta for Developers with the “Connect with customers through WhatsApp” use case, a business portfolio and a WhatsApp Business account. The dashboard creates a test account and a test number on its own, with relaxed limits and template sends that need no payment method on file.
In 2026 the account changed. Since September 23, the new model splits it in two: the WhatsApp account, with the number and the display name, and the Messaging account, with templates, billing and webhooks. The old ID becomes the Messaging account ID, so nothing breaks now. In the first half of 2028, API paths swap the phone number ID for the WhatsApp account ID; store both from day one.
The number has to be yours, with a country and area code, and able to receive a code by SMS or voice call; short codes don’t work. If it’s already on WhatsApp, that account has to be deleted first. The exception is coexistence with the WhatsApp Business app, which keeps the number on the phone and on the API, but comes through the signup flow of a Meta partner or tech provider, not a direct integration.
Business verification isn’t required to start, but it changes the ceilings: with a verified portfolio, the number cap goes from 2 to 20, the messaging limit from 250 to 2,000 people a day, and the template cap from 250 to up to 6,000 once a number has an approved display name.
Permanent token and number registration
The “Generate access token” button in the dashboard creates a user access token, which the documentation says expires every few hours: fine for the first test. In production, the token belongs to a system user, created in the portfolio settings with access to the app and the WhatsApp accounts, long-lived and with three permissions: business_management, whatsapp_business_management and whatsapp_business_messaging.
whatsapp_business_messaging covers sending and the messages webhook; whatsapp_business_management covers numbers, templates and the other fields. If you integrate your own data, you grant both when you generate the token; Meta’s review for advanced access is required of partners. The token lives in a server-side secrets vault, never in the front end or the repository, and an expired one shows up as error 190.
Adding and verifying the number in WhatsApp Manager isn’t enough: registration on the Cloud API only happens through the API, with POST /<PHONE_NUMBER_ID>/register, messaging_product: "whatsapp" and a six-digit pin, the two-step verification PIN (the current one if it exists, or a new one). You get 10 requests per number every 72 hours; go over and it’s error 133016 and a 72-hour block.
The optional data_localization_region parameter turns on local storage, and the list includes Brazil (BR): once processed, message content stays only in data centers in the chosen country. Changing it means deregistering and registering again. And every approved display name change requires a new registration within 14 days.
The first message
The call is POST https://graph.facebook.com/v26.0/<PHONE_NUMBER_ID>/messages, with the token in Authorization: Bearer. The body carries messaging_product: "whatsapp", recipient_type: "individual", the number in to, the type and an object named after the type, such as text. Version v26.0, from July 29, 2026, is the latest Graph API version.
Pin the version in your configuration and change it on purpose. Meta’s getting started guide still shows v23.0, available until October 8, 2027; every version has an expiry date in the Graph API table, and copying the example leaves you on a version with a deadline without noticing.
Free-form text only goes out inside the customer service window, which the documentation says opens when the customer messages or calls you, lasts 24 hours and resets with each new message from them. Outside it, only approved templates: type: "template", with name, language.code and the components for the variables. The post on the WhatsApp 24-hour window has the details.
The response includes contacts, with the number in input and the wa_id, which may differ, and messages[0].id, the wamid. Meta warns that it only confirms the API accepted the request, not delivery, and that delivery order may not match the order of your calls. Save the wamid before anything else: it ties the send to the webhook statuses.
Webhook: verification, signature and subscription
When you register the webhook URL in the dashboard, Meta sends a GET with hub.mode=subscribe, hub.verify_token and hub.challenge. If the verify_token matches the string you chose, reply 200 with the hub.challenge value in the body; any other response leaves the endpoint unverified, and no webhooks arrive. The TLS certificate must be valid: self-signed ones aren’t accepted.
Every POST carries the X-Hub-Signature-256 header, formatted as sha256=<hash>: an HMAC-SHA256 of the body with the app secret as the key. Compute it over the raw request bytes before parsing the JSON, compare in constant time and drop anything that doesn’t match. Meta also supports mTLS and recommends it over IP allowlisting, because its addresses change.
In the dashboard, subscribe to the messages field: it carries customers’ messages and the statuses of the ones you sent. Subscribe the app to each account with POST /<WABA_ID>/subscribed_apps, one by one, as the documentation requires. And switch the app to Live mode: Meta warns that some webhooks aren’t sent while the app is in development mode.
Webhooks in production: fast 200, queue and idempotency
If the endpoint returns anything other than 200, or delivery fails, Meta tries again, less and less often, for up to 7 days, and the documentation warns that this produces duplicates your server should discard. A payload can be up to 3 MB and bundle up to 1,000 updates. There’s no API to fetch old webhooks: whatever you didn’t store is gone.
The right design: the handler checks the signature, writes the raw payload to a durable queue or table and returns 200. Updating the conversation, downloading media or calling the CRM and the AI agent is left to a worker. A handler that’s slow to reply risks redeliveries and the same message processed twice.
Idempotency by ID: an incoming message gets a unique key on messages[].id, and a status on the wamid plus the status. And don’t count on order: when the customer reads right away, delivered isn’t even sent, and a redelivery can bring an old status after a newer one. Treat status as a ladder, sent, delivered, read, that never goes down; failed arrives with errors.
Since April 2026, webhooks carry the BSUID, a portfolio-scoped customer ID, in fields such as contacts[].user_id. When the customer uses a username, the phone number may be missing, and Meta recommends not relying on wa_id and from. Since July you can send to a BSUID in the recipient field. Model the contact with both keys.
Media, limits and the most common errors
To send media, upload the file with POST /<PHONE_NUMBER_ID>/media and use the ID you get back, which Meta recommends over a link; it’s kept for 30 days. Images are 8-bit JPEG or PNG, in RGB or RGBA, up to 5 MB: a CMYK JPEG from a print shop won’t go through.
On the way in, the webhook carries the media ID, not the file. GET /<MEDIA_ID> returns a URL that expires in 5 minutes, the download requires the token, and the received ID only lasts 7 days. Copy the file to your own storage as soon as the message arrives.
There are two ceilings. Throughput: each number sends up to 80 messages per second and rises on its own to 1,000 with an unlimited portfolio, 100,000 people outside the window in 24 hours and quality of yellow or better; under coexistence, it stays at 20. And the messaging limit, which counts people outside the window in 24 hours across the portfolio and climbs from 2,000 to 10,000, 100,000 and unlimited with high quality and half the limit used in 7 days.
Common errors fall into three groups. Those that call for waiting and retrying with growing intervals: 130429, throughput, and 131056, too many messages to the same person. Those that call for a different message: 131047, window closed, and 132001, template missing in that language. And those that call for a person: 190, expired token; 133010, number not registered; 131042, payment; and 131048, number restricted for spam. The post on the Cloud API errors covers the trickiest ones.
Integrating directly or through a Meta partner
Integrating directly gives you full control and leaves you with what the API doesn’t do: the queue that respects throughput, retries, webhook storage, copying media, computing the window per contact and version upgrades. Plus Meta’s changes: it has announced that after October 13, 2027, marketing messages on the Cloud API return an error and move to the Marketing Messages API.
The API has no inbox either: assigning conversations, showing history, alerting agents and giving the manager a dashboard is software to build and maintain. Meta’s bill is the same on both paths: per delivered message, by category and country, including, since October 1, 2026, service messages above 1,000 per number per month.
A Meta Business Partner platform delivers those pieces ready-made: it connects the number through Meta’s signup flow, offers coexistence and gives the team an inbox. You integrate with its API and webhook, as in this WhatsApp messaging API documentation, and the number stays in your WhatsApp account. If WhatsApp is the core of your product and you have engineers to maintain it, go direct; if it’s an operations channel, use a platform.