Skip to content
Linq Copy agent prompt
Messaging

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: true shows a preview card for the first URL. Default false.

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, document or sticker.
  • caption: up to 1,024 characters; image, video and document only.
  • filename: up to 240 characters; document only.
  • 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

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/ogg holding 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.

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) and longitude (−180 to 180): required, never defaulted.
  • name, address: optional, up to 1,000 characters each.

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).
  • origin is response-only and appears on inbound cards; a send carrying it is refused. See contact_request.

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, not channel_message_id.
  • emoji: required; an empty string removes your earlier reaction.
  • Ends at sent: no delivered or read receipt. See React to a message.

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. title up to 20 characters, id up to 256; both unique.
  • header (text, up to 60) or media_header (image, video or document by upload_ref), not both.
  • footer: up to 60 characters.

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 section title (up to 24) is required once there are two or more.
  • Up to 10 rows in total across all sections. Row id up to 200, unique across sections.
  • Row title up to 24, description up to 72.
  • header, footer: text only, up to 60 characters.

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: absolute http or https, up to 2,000 characters.
  • header (text, up to 60) or media_header, not both.
  • footer: text only, up to 60 characters.

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, video or document, matching the upload’s kind.
  • filename: up to 240 characters; document only.
  • header with media_header, or filename on an image or video header, is refused with 422 invalid_content and a pointer at the member.
  • A missing upload is refused with 422 upload_ref_not_found; an upload of another kind with 422 upload_ref_invalid.

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_index starts at 0 and matches the card’s position.
  • Card header: an image by upload_ref, or a video by url (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_text up to 20, absolute https url up to 2,000) or 1 to 2 reply_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.

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_ref and a video card takes url; the other way round is refused.
  • url: up to 2,000 characters, absolute https, 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 with 422 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.failed with the failure code channel_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.delivered before sending the next message.

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.

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"
}
]
}
]
}

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 as en_US, up to 32 characters.
  • header, buttons, cards: only when the template’s send_schema asks for them.
  • Authentication templates take one parameter, code, 1 to 15 characters.

See Templates to register one.

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.