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.
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.
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.