01 · Definition
What is the messages endpoint?
The messages endpoint is the WhatsApp Cloud API call that sends messages: POST https://graph.facebook.com/v26.0/{PHONE_NUMBER_ID}/messages, where the Phone Number ID identifies the business number doing the sending. Every outbound message — text, image, video, audio, document, sticker, location, contacts, reaction, interactive buttons and lists, Flows and message templates — goes through this one URL, and the body's type field decides which kind it is.
The JSON body always carries messaging_product: "whatsapp", the recipient in to (the customer's number in international format with its country code, as in the E.164 Phone Number Format, or a group ID when recipient_type is group), the type, and an object named after that type holding the content: "text": {"body": "..."}, or "template": {"name": "...", "language": {"code": "en"}, "components": [...]}. Optional fields include recipient_type (defaults to individual) and context.message_id to reply to a specific message.
A successful call returns HTTP 200 with contacts[0].input (what you sent), contacts[0].wa_id (the recipient's WhatsApp ID) and messages[0].id — the WhatsApp Message ID (WAMID) used to match later status webhooks. Template sends can also carry message_status: accepted, held_for_quality_assessment or paused. Acceptance is not delivery: sent, delivered, read and failed arrive afterwards on the Statuses Webhook. The endpoint is distinct from the other edges on the same phone number node, such as /media for uploads and /register for registration.
Key takeaways
- The messages endpoint is
POST /{PHONE_NUMBER_ID}/messages: one call for text, media, interactive, template, reaction and location messages. - Every body needs
messaging_product: "whatsapp", the recipient into, atypeand an object named after that type. - A 200 response returns the recipient's
wa_idand the message'swamid; it means accepted, not delivered. - Outside the 24-hour customer service window only template messages go through; free-form sends fail with error 131047.
- The same endpoint marks inbound messages as read, with
status: "read"and the customer message'smessage_id.
02 · Code samples
The messages endpoint in code
curl -X POST "https://graph.facebook.com/v26.0/{PHONE_NUMBER_ID}/messages" \-H "Authorization: Bearer {ACCESS_TOKEN}" \-H "Content-Type: application/json" \-d '{"messaging_product": "whatsapp","recipient_type": "individual","to": "919812345678","type": "template","template": {"name": "order_dispatched","language": { "code": "en" },"components": [{"type": "body","parameters": [{ "type": "text", "text": "Ananya" },{ "type": "text", "text": "A1043" }]}]}}'
03Playground
A working model of Messages Endpoint. Use its controls to replay it step by step.
Send a template and read what comes back
https://graph.facebook.com/v26.0/106540352242922/messagesPOST 200 in 650 ms(simulated)
Request
3 headers · bodyResponse
200 OKResponse: 200 OK after 650 ms.
04 · Specifications
The messages endpoint: key facts and limits
| Endpoint | POST /v26.0/{PHONE_NUMBER_ID}/messages |
|---|---|
| Required fields | messaging_product, to, type + the type's object |
| recipient_type | individual (default) or group |
| Success | 200 with contacts[].wa_id and messages[].id (wamid) |
| Template message_status | accepted, held_for_quality_assessment, paused |
| Text body limit | 4,096 characters |
| Default throughput | Up to 80 messages per second per number |
| Outside the service window | Templates only (error 131047 otherwise) |
05 · Operation
How does the messages endpoint work?
Choose the sending number
Put that number's Phone Number ID in the path. Each registered business number has its own ID, and the token needs
whatsapp_business_messagingaccess to its WABA.Normalise the recipient
Write
toin international format with the country code —919812345678for an Indian mobile — never with a leading 0, spaces or brackets.Pick the type and build its object
Inside an open customer service window any type works, including a free-form Session Message. Outside it, send
type: "template"with an approved message template'sname,language.codeand parameters incomponents.POST and read the response
On 200, store
messages[0].id(the wamid) andcontacts[0].wa_idagainst your own record. On an error, branch onerror.codeand keep thefbtrace_id.Track delivery by webhook
Match incoming status webhooks to the stored wamid to move the message through sent, delivered, read or failed — and never move it backwards when events arrive out of order.
06 · Rationale
Why does the messages endpoint matter?
Almost every WhatsApp feature you build ends in this call — order updates, OTPs, support replies, chatbot answers, campaign sends. Knowing its contract precisely — required fields, which types are allowed when, what the response does and does not promise — prevents the most common integration failures: free-form messages sent after the window has closed, template parameter mismatches, numbers in local format, and systems that mark a message delivered the moment the API returns 200.
07 · Cautions
Common mistakes with the messages endpoint
Treating 200 as delivered
The response confirms acceptance only. Mark messages delivered or read when the statuses webhook says so, and handle
failedstatuses with their error codes.Local-format phone numbers
09812345678or98123 45678can fail or reach the wrong country. Normalise every number to country code plus subscriber number before calling.Free-form messages after the window
Text, media and interactive messages fail with Error 131047 (Re-engagement Message) once 24 hours have passed since the customer's last message. Check the window and fall back to an approved template.
Template parameter mismatch
Sending more or fewer body parameters than the template defines fails with error 132000. Build
componentsfrom the template's stored definition, not by hand.Bursting past throughput
Firing a whole campaign at once exceeds the number's throughput and returns error 130429. Queue sends and pace them below the number's limit.
08 · Worked example: An order update and a reply in Indore
Example: An order update and a reply in Indore
An Indore home-appliance store's order system calls the messages endpoint with an order_dispatched utility template when a refrigerator leaves the warehouse. The 200 response returns a wamid, which the system stores against order A1043. A status webhook marks it delivered four seconds later and read after ten minutes. The customer then replies asking for an evening slot, which opens a customer service window; the agent's free-form answer goes to the same endpoint with type: "text" and context.message_id set to the customer's question, so it appears as a quoted reply.
09 · Recommended practice
Best practices for the messages endpoint
- Keep one request builder per message type and unit-test the JSON it produces.
- Store the wamid next to your own message ID so webhooks and support tools can find the record.
- Use
context.message_idwhen answering a specific question so the customer sees what you are replying to. - Add
biz_opaque_callback_datawhen you need your own tracking value echoed back in status webhooks.
10 · Questions and answers
Messages Endpoint FAQs
- How do I send a WhatsApp message with the Cloud API?
- POST JSON to
https://graph.facebook.com/v26.0/{PHONE_NUMBER_ID}/messageswith a bearer token,messaging_product: "whatsapp",to,typeand the matching type object. Outside a service window, the type must betemplate. - What does the messages endpoint return?
- On success, HTTP 200 with the recipient's
wa_idincontactsand the message's wamid inmessages[0].id, plusmessage_statusfor template sends. Delivery outcomes arrive later by webhook. - Can I send to a number without the country code?
- No — include the country code. Write
toin international format, such as 919812345678 for India, with no leading 0 or spaces, or the send can fail or go to the wrong person. - Is there a separate endpoint for templates or media?
- No. Templates, media, interactive and text messages are all sent through the same messages endpoint; only
typechanges. Uploading a file first uses the separate/mediaedge, which returns an ID you then send.