Message types
Every part type you can send on WhatsApp, with a minimal example and its limits, and the types you receive.
A message’s content is its parts array, and each part’s type decides what
the customer sees. Send one part per request, with
either POST /v1/messages or
the chat route POST /v1/chats/{chat}/messages. A request with
two parts is refused with error.code
1019.
type |
Customer sees | In window | Outside window |
|---|---|---|---|
text |
Text | Yes | No |
media |
Image, video, file | Yes | No |
location |
Map pin | Yes | No |
contacts |
Contact cards | Yes | No |
reaction |
Emoji on a message | Yes | No |
interactive_buttons |
Reply buttons | Yes | No |
interactive_list |
Pick-list menu | Yes | No |
interactive_cta_url |
Link button | Yes | No |
interactive_carousel |
Scrolling cards | Yes | No |
location_request |
Send-location button | Yes | No |
contact_request |
Share-contact button | Yes | No |
template |
Approved template | Yes | Yes |
The window is the 24-hour customer window. Outside it only an approved
template sends; anything else is refused with error.code
2036. See
Sending messages
for the window, idempotency and outcomes.
Group chats accept only text, standard media (not voice notes) and
approved text or media template parts.
The exact schema for each part is served
at GET /v1/parts/{type}. Content that breaks a
limit is rejected with a JSON Pointer to the field, never truncated or
converted.
Examples below show the request body for the chat route,
POST /v1/chats/{chat}/messages. With
POST /v1/messages, add the customer’s address as to.
Plain text. URLs render as tappable links.
{ "parts": [{ "type": "text", "body": "Your order is on its way." }] }body: required, up to 4,096 characters, not blank.preview_url:trueshows a preview card for the first URL. Defaultfalse.
One image, video, audio file, document or sticker. Name the bytes with
exactly one of upload_ref (from POST /v1/media)
or source_url (a public https address fetched once, before the send is
accepted). Attachments
covers both routes.
{ "parts": [ { "type": "media", "kind": "image", "upload_ref": "upload.example", "caption": "Today's menu" } ]}kind:image,video,audio,documentorsticker.caption: up to 1,024 characters;image,videoanddocumentonly.filename: up to 240 characters;documentonly.source_url: up to 2,048 characters.
kind |
Upload limit (bytes) | Media types |
|---|---|---|
image |
5,000,000 | JPEG, PNG |
video |
16,000,000 | MP4, 3GPP |
audio |
16,000,000 | AAC, AMR, MPEG, MP4, Ogg |
document |
100,000,000 | Plain text, PDF, Word, Excel, PowerPoint |
sticker |
100,000 static / 500,000 animated | WebP |
Voice notes
Section titled “Voice notes”An audio part with "voice": true arrives as a voice note instead of an
audio file.
{ "parts": [ { "type": "media", "kind": "audio", "upload_ref": "upload.example", "voice": true } ]}- The upload must be
audio/oggholding mono Opus audio. Bytes are never converted. - 512 KB or smaller shows a play button; larger is delivered but must be downloaded first.
- Individual chats only.
location
Section titled “location”A map pin.
{ "parts": [ { "type": "location", "latitude": 37.7749, "longitude": -122.4194, "name": "Downtown store", "address": "100 Main St, San Francisco, CA" } ]}latitude(−90 to 90) andlongitude(−180 to 180): required, never defaulted.name,address: optional, up to 1,000 characters each.
contacts
Section titled “contacts”One or more contact cards.
{ "parts": [ { "type": "contacts", "contacts": [ { "name": { "formatted_name": "Ana Ruiz" }, "phones": [{ "phone": "+15550101234", "kind": "CELL" }] } ] } ]}- 1 to 5 cards.
name.formatted_name: required, up to 512 characters, not blank.- Optional
phones,emails,urls,addresses,org,birthday(YYYY-MM-DD). originis response-only and appears on inbound cards; a send carrying it is refused. Seecontact_request.
reaction
Section titled “reaction”An emoji on an earlier message in the chat.
{ "parts": [{ "type": "reaction", "message_id": "msg_example", "emoji": "👍" }]}message_id: the API’s own message id, notchannel_message_id.emoji: required; an empty string removes your earlier reaction.- Ends at
sent: no delivered or read receipt. See React to a message.
interactive_buttons
Section titled “interactive_buttons”Body text with up to three reply buttons. A tap arrives as an
interactive_reply with kind: "button_reply" and
the button’s id.
{ "parts": [ { "type": "interactive_buttons", "body": "Did your order arrive?", "buttons": [ { "id": "arrived-yes", "title": "Yes" }, { "id": "arrived-no", "title": "No" } ] } ]}body: up to 1,024 characters.buttons: 1 to 3.titleup to 20 characters,idup to 256; both unique.header(text, up to 60) ormedia_header(image,videoordocumentbyupload_ref), not both.footer: up to 60 characters.
interactive_list
Section titled “interactive_list”Body text with a button that opens a sectioned list. A pick arrives as an
interactive_reply with kind: "list_reply" and the
row’s id.
{ "parts": [ { "type": "interactive_list", "body": "Choose a delivery slot.", "button": "See slots", "sections": [ { "rows": [ { "id": "slot-am", "title": "Morning", "description": "8am to 12pm" }, { "id": "slot-pm", "title": "Afternoon", "description": "12pm to 5pm" } ] } ] } ]}button: up to 20 characters.body: up to 1,024.sections: 1 to 10; a sectiontitle(up to 24) is required once there are two or more.- Up to 10 rows in total across all sections. Row
idup to 200, unique across sections. - Row
titleup to 24,descriptionup to 72. header,footer: text only, up to 60 characters.
interactive_cta_url
Section titled “interactive_cta_url”Body text with one button that opens a URL.
{ "parts": [ { "type": "interactive_cta_url", "body": "Track your parcel.", "display_text": "Track", "url": "https://example.com/track/A-1042" } ]}display_text: up to 20 characters.url: absolutehttporhttps, up to 2,000 characters.header(text, up to 60) ormedia_header, not both.footer: text only, up to 60 characters.
Media header
Section titled “Media header”media_header shows an uploaded image, video or document above the body.
Upload the file with POST /v1/media, then name it
with the matching kind:
{ "parts": [ { "type": "interactive_cta_url", "media_header": { "kind": "document", "upload_ref": "upload.example", "filename": "Menu.pdf" }, "body": "This week's menu", "display_text": "Order now", "url": "https://example.com/order" } ]}kind:image,videoordocument, matching the upload’s kind.filename: up to 240 characters;documentonly.headerwithmedia_header, orfilenameon an image or video header, is refused with422 invalid_contentand a pointer at the member.- A missing upload is refused with
422 upload_ref_not_found; an upload of another kind with422 upload_ref_invalid.
interactive_carousel
Section titled “interactive_carousel”A body above 2 to 10 cards that scroll sideways, each showing an image or a
video. For image cards, upload each image
with POST /v1/media, then list the cards in
card_index order.
{ "parts": [ { "type": "interactive_carousel", "body": "Three new listings near you", "cards": [ { "card_index": 0, "header": { "kind": "image", "upload_ref": "upload.example0" }, "body": "2 bed, garden", "reply_buttons": [{ "id": "listing-0-like", "title": "Like" }] }, { "card_index": 1, "header": { "kind": "image", "upload_ref": "upload.example1" }, "body": "Loft, city view", "reply_buttons": [{ "id": "listing-1-like", "title": "Like" }] }, { "card_index": 2, "header": { "kind": "image", "upload_ref": "upload.example2" }, "body": "Studio, near park", "reply_buttons": [{ "id": "listing-2-like", "title": "Like" }] } ] } ]}body: up to 1,024 characters.cards: 2 to 10.card_indexstarts at 0 and matches the card’s position.- Card
header: animagebyupload_ref, or avideobyurl(video cards). - Every card has the same header kind: all images or all videos.
- Card
body: optional, up to 160 characters and two line breaks. - Each card has one
url_button(display_textup to 20, absolutehttpsurlup to 2,000) or 1 to 2reply_buttons. - Every card has the same kind of button, and the same number of reply buttons.
- Reply button
id: up to 256, unique across the whole carousel.title: up to 20, unique within a card.
A reply-button tap arrives as a template_reply
whose payload is the button’s id. Nothing else names the card, so the
id is the card’s identity. A URL-button tap sends you no event.
Video cards
Section titled “Video cards”A video card names the public https address its video plays from, in
header.url, instead of an upload:
{ "parts": [ { "type": "interactive_carousel", "body": "Take a tour before you visit", "cards": [ { "card_index": 0, "header": { "kind": "video", "url": "https://cdn.example.com/tour-0.mp4" }, "body": "Virtual tour", "url_button": { "display_text": "Book", "url": "https://example.com/book/0" } }, { "card_index": 1, "header": { "kind": "video", "url": "https://cdn.example.com/tour-1.mp4" }, "body": "Garden walk-through", "url_button": { "display_text": "Book", "url": "https://example.com/book/1" } } ] } ]}- An image card takes
upload_refand a video card takesurl; the other way round is refused. url: up to 2,000 characters, absolutehttps, with its host named by a DNS name (no IP address, no percent-escapes in the host), no port (omit it), no credentials, no fragment, and no spaces or control characters. Anything else is refused with422 invalid_content, never rewritten.- The video is never fetched before the send: the address is sent exactly as written, so it must be publicly reachable and serve a playable video.
- A link the customer’s WhatsApp cannot fetch is accepted, then reported as
message.failedwith the failure codechannel_refused. See Why a message failed. - WhatsApp fetches each linked video before delivering, so a carousel with
video cards can arrive after messages you send behind it. If order matters,
wait for its
message.deliveredbefore sending the next message.
location_request
Section titled “location_request”A prompt with a send-location button. The answer arrives as a location part.
{ "parts": [ { "type": "location_request", "body": "Share your location so we can find the nearest store." } ]}body: required, up to 1,024 characters.
address_request also appears in the reference. It is reserved and cannot be
sent yet.
contact_request
Section titled “contact_request”A prompt with a button that shares the customer’s own contact card with one tap. Useful when you know a customer only by a username and need a phone number.
{ "parts": [ { "type": "contact_request", "body": "Share your number so we can call you back." } ]}body: required, up to 1,024 characters, not blank.- Individual chats only; refused on a send to a group chat.
The shared card arrives as an ordinary inbound contacts part.
Each inbound card may carry origin:
origin |
Meaning |
|---|---|
contact_request |
The customer tapped your share-contact button. |
other |
The customer sent a card themselves. |
| absent | Not reported. |
New values may appear; treat any other value as unrecognised. The shared card
is not linked as a reply, so its message carries no reply_to_message_id:
match it to your request by origin and timing.
{ "parts": [ { "type": "contacts", "contacts": [ { "name": { "formatted_name": "Ana Ruiz" }, "phones": [ { "phone": "+15550101234", "messaging_id": "15550101234" } ], "origin": "contact_request" } ] } ]}template
Section titled “template”An approved template, and the one type that sends outside the window. Read
each template’s send_schema from GET /v1/templates
and fill its named parameters.
{ "parts": [ { "type": "template", "name": "order_shipped", "language": "en_US", "parameters": { "order_id": "A-1042" } } ]}name: lowercase letters, digits and underscores, up to 512 characters.language: a code such asen_US, up to 32 characters.header,buttons,cards: only when the template’ssend_schemaasks for them.- Authentication templates take one parameter,
code, 1 to 15 characters.
See Templates to register one.
Messages you receive
Section titled “Messages you receive”Inbound messages use the same parts array. These types arrive inbound; the
last seven are never sendable.
type |
What it is |
|---|---|
text |
Text from the customer. |
media |
A file. Carries media_id and a download url; see Download incoming attachments. |
location |
A shared location, including an answer to location_request. |
contacts |
Contact cards, including an answer to contact_request. Each card may carry origin; see contact_request. |
reaction |
An emoji on a message. Carries channel_message_id, and message_id when the chat holds the target. |
interactive_reply |
A tap on interactive_buttons (button_reply) or interactive_list (list_reply). id is yours. |
template_reply |
A tap on a template quick-reply button or a carousel reply button. payload is the button’s value or id; text is its label. |
address_reply |
A structured address the customer submitted. |
order |
An order placed from a product catalog. |
referral |
The ad or post a first message came from. Opens a longer window. |
system |
A chat event, such as the customer changing number. body is readable. |
unsupported |
A message kind not yet typed. raw holds the original payload. |
For a template tap, the message’s reply_to_message_id names the template
message that carried the button.