WhatsApp Business API & Cloud API
How to Send a WhatsApp Message With Cloud API
Learn how to send WhatsApp messages with the Cloud API using a Phone Number ID, access token, Graph API request, and message payload.
Step 1
What You Need
Before sending your first Cloud API message from code, you need a WhatsApp Business Account, a registered or test phone number, its Phone Number ID, an access token, and a recipient that your current setup is allowed to message. Keep the Graph API version configurable so the guide does not lock your application to one old version.
For a production sender, use server side credentials and a real business number. For initial development, Meta's test resources are enough to prove the request format. Once one message succeeds, save the returned message ID and add webhook tracking before building larger workflows.
Implementation Checklist
- Have the Phone Number ID for the sending number.
- Have a valid server side access token with messaging access.
- Use a recipient number in a consistent international format.
- Know whether the message can be free form or must use an approved template for the current conversation context.
Step 2
Find Your Phone Number ID
Phone number configuration is part identity, part routing. The visible business number is what customers recognize, while the Phone Number ID is what the Graph API uses in message and registration endpoints. Make sure the number is attached to the intended WABA and is in the expected registration state.
Normalize customer numbers before sending and keep country code handling explicit. Avoid guessing a country code from local formatting in production workflows. A CRM should store phone numbers in a consistent international representation so the same customer is not created multiple times under different formats.
Implementation Checklist
- Confirm the business number belongs to the intended WhatsApp Business Account.
- Use the Phone Number ID in Graph API paths, not the display phone number.
- Normalize customer numbers consistently before matching or sending.
- When registration fails, check ownership verification and two step verification before changing code.
Step 3
Get an Access Token
An access token proves that your backend is allowed to call Meta's Graph API. The token alone is not enough: it must belong to the right app or system user, include the required permissions, and have access to the WhatsApp business assets involved in the request.
When debugging authentication, first identify what kind of token you are using and whether it is intended for temporary development or server side production use. Then verify the permissions and asset assignments instead of repeatedly generating new tokens without understanding the original failure.
Implementation Checklist
- Keep access tokens in server side environment variables, never in client side JavaScript.
- Check the token type and expiration before changing application code.
- Confirm whatsapp_business_messaging for message operations and management permissions where required.
- Regenerate a token only after fixing the underlying permission or asset assignment problem.
Step 4
Understand the Messages Endpoint
Outbound Cloud API messages are sent to the Graph API messages endpoint for the sending Phone Number ID. The request is authenticated with a bearer token and the JSON body declares messaging_product as whatsapp together with the recipient and message type.
Keep the Graph API version configurable instead of hard coding an old version throughout your application. Your backend should also store the returned WhatsApp message ID because that ID is how later webhook status events are matched back to the message you sent.
Implementation Checklist
- Use POST /<PHONE_NUMBER_ID>/messages.
- Send Content-Type: application/json and an Authorization bearer token.
- Use an international recipient number in the format expected by the API.
- Persist the returned message ID for delivery tracking and troubleshooting.
Messages endpoint
POST https://graph.facebook.com/<GRAPH_API_VERSION>/<PHONE_NUMBER_ID>/messagesStep 5
Send A Text Message
For a first direct API test, keep the payload simple. Send a text message to a test recipient that is allowed by your current setup, then inspect both the HTTP response and the message that arrives on the phone. A successful API response confirms that the token, Phone Number ID, payload, and recipient are accepted by Meta.
Do not treat an accepted request as the final delivery result. The API response gives you a message ID. Delivery, read, or failure outcomes arrive later through webhook status events, so production applications should store that ID and update its status asynchronously.
Implementation Checklist
- Start with a plain text payload before adding templates, media, or interactive content.
- Log the HTTP status code and sanitized response body.
- Store the returned message ID with the customer and workflow that created it.
- Use webhooks for the final sent, delivered, read, or failed state.
Basic text message request
curl -X POST \
"https://graph.facebook.com/<GRAPH_API_VERSION>/<PHONE_NUMBER_ID>/messages" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"to": "<RECIPIENT_PHONE_NUMBER>",
"type": "text",
"text": { "body": "Hello from WhatsApp Cloud API" }
}'Step 6
Send a Message With cURL
cURL is useful because it removes your application framework from the test. If the same request works in cURL but fails in your backend, the problem is usually in how your application builds headers, serializes JSON, reads environment variables, or handles the response.
Use placeholders in documentation and keep real tokens out of shell history, screenshots, repositories, and support messages. When debugging, compare the exact URL, headers, and JSON body with the request generated by your application.
Implementation Checklist
- Replace the Graph API version, Phone Number ID, token, and recipient before running the command.
- Keep the Authorization header exactly as Bearer followed by the token.
- Check the raw JSON for missing commas, wrong nesting, or accidental string conversion.
- Copy the response message ID into your logs so webhook events can be correlated later.
cURL example
curl -X POST \
"https://graph.facebook.com/<GRAPH_API_VERSION>/<PHONE_NUMBER_ID>/messages" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"messaging_product":"whatsapp","to":"<RECIPIENT>","type":"text","text":{"body":"Test message"}}'Step 7
Send a Message From Node.js
Once the request works outside your application, move it into server side code. Keep the token and Phone Number ID in environment variables, validate the recipient before sending, and return a controlled error to the calling workflow instead of exposing the full Meta response to the browser.
The example below uses the built in fetch API available in current Node.js runtimes. In a larger application, put this logic behind a small WhatsApp client module so retries, logging, API version changes, and error handling stay in one place.
Implementation Checklist
- Read credentials from server side environment variables.
- Check response.ok before treating the request as successful.
- Parse and log a sanitized error response when Meta rejects the request.
- Return or persist the message ID from the successful response.
Node.js example
const response = await fetch(
`https://graph.facebook.com/${process.env.META_GRAPH_VERSION}/${process.env.WHATSAPP_PHONE_NUMBER_ID}/messages`,
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.WHATSAPP_ACCESS_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
messaging_product: "whatsapp",
to: recipient,
type: "text",
text: { body: message },
}),
}
);
const data = await response.json();
if (!response.ok) throw new Error(data?.error?.message || "WhatsApp API request failed");Step 8
Read the API Response
The initial send response is not the final delivery result. Store the outbound WhatsApp message ID and update that record when webhook events report sent, delivered, read, or failed states.
Implementation Checklist
- Match events by WhatsApp message ID.
- Store timestamps and failure details.
- Make repeated status events safe to process.
This guide only covers the part needed for the current workflow. For the complete setup, examples, and troubleshooting, continue with WhatsApp Cloud API Message Statuses Explained.
Step 9
Handle Errors
Start with the exact error returned by the failing layer. For a Graph API request, record the HTTP status and the structured error object. For a webhook, record the request path, response status, and event identifiers. For an external API or CRM, keep its response separate so one provider's error is not mistaken for another provider's failure.
Retry only when the failure is safe to retry. Authentication, invalid parameters, missing templates, and permission errors usually need a configuration or data fix. Network timeouts and some temporary server failures can be retried with backoff, but message and workflow operations should be idempotent so a retry does not create duplicates.
Implementation Checklist
- Capture the error code, message, HTTP status, and relevant request correlation information.
- Remove tokens and sensitive customer data before writing logs.
- Separate permanent input or permission failures from temporary transport failures.
- Use idempotency or unique event keys before adding automatic retries.
Step 10
Track Delivery Through Webhooks
Webhooks are the event input for a WhatsApp integration. Meta verifies a public HTTPS callback URL, then sends incoming messages and message status events to that endpoint. Your app also needs the correct WhatsApp Business Account subscription so events reach the callback.
Implementation Checklist
- Use a public HTTPS callback and a separate verify token.
- Subscribe the app to the correct WABA and required webhook fields.
- Acknowledge events quickly and process them idempotently.
This guide only covers the part needed for the current workflow. For the complete setup, examples, and troubleshooting, continue with How to Set Up WhatsApp Webhooks.
Step 11
Production Considerations
A production sender needs more than one successful API call. Move credentials into protected server configuration, use a system user token appropriate for production, add webhook status tracking, and make message creation idempotent so retries do not send duplicates.
Separate message intent from transport. Your business workflow should decide what needs to be sent, while one messaging service validates the recipient, selects the correct message type or template, calls Meta, stores the message ID, and returns a controlled result to the workflow.
Implementation Checklist
- Use protected server side credentials and controlled environment configuration.
- Persist outbound messages before or immediately after the API request with a stable idempotency key.
- Track final delivery through webhooks instead of relying only on the send response.
- Add rate, error, and failure monitoring before connecting large campaign or notification workloads.
Related Guides
Continue Learning
Need Implementation Help?
Need Help With Your WhatsApp or Automation Project?
If you need help building, integrating, troubleshooting, or improving a production system, you can discuss the project with me directly.
