curl --request POST \
--url https://api.wppfy.com/send/menu \
--header 'Content-Type: application/json' \
--header 'token: <api-key>' \
--data '
{
"number": "5511999999999",
"type": "button",
"text": "Como podemos ajudar?",
"choices": [
"Suporte|suporte",
"Fazer pedido|pedido"
]
}
'{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"messageid": "<string>",
"chatid": "<string>",
"sender": "<string>",
"senderName": "<string>",
"isGroup": false,
"fromMe": false,
"messageType": "<string>",
"source": "<string>",
"messageTimestamp": 0,
"status": "<string>",
"text": "",
"quoted": "",
"edited": "",
"reaction": "",
"vote": "",
"convertOptions": "",
"buttonOrListid": "",
"owner": "",
"error": "",
"content": {},
"wasSentByApi": true,
"pinned": true,
"sendFunction": "<string>",
"sendPayload": {},
"fileURL": "<string>",
"callPeer": {
"jid": "<string>",
"name": "<string>",
"imagePreviewUrl": "<string>"
},
"send_folder_id": "<string>",
"track_source": "<string>",
"track_id": "<string>",
"ai_metadata": {
"agent_id": "<string>",
"turn": "<string>",
"flow_id": "<string>",
"request": {
"messages": "<array>",
"tools": "<array>",
"options": {
"model": "<string>",
"temperature": 123,
"maxTokens": 123,
"topP": 123,
"frequencyPenalty": 123,
"presencePenalty": 123
}
},
"response": {
"choices": "<array>",
"toolResults": "<array>",
"error": "<string>"
},
"outcome": {
"status": "sent",
"texts": 123,
"audio": true,
"menus": 123,
"files": 123,
"handoff": "<string>",
"lead": [
"<string>"
],
"detail": "<string>"
}
},
"sender_pn": "<string>",
"sender_lid": "<string>",
"sender_image_preview_url": "<string>",
"response": {
"status": "success",
"message": "Menu sent successfully"
}
}{
"error": "Missing required fields or invalid menu type"
}{
"error": "Invalid token"
}{
"error": "Rate limit exceeded"
}{
"error": "Failed to send menu"
}Send interactive menu (buttons, carousel, list, or poll)
This endpoint provides a unified interface for sending four main types of interactive messages:
- Buttons: For quick, direct actions
- Button carousel: For a horizontal list of buttons with images
- Lists: For menus organized into sections
- Polls: For collecting opinions and voting
Tracking field support: This endpoint also supports track_source and track_id documented in the “Send Message” tag.
Base Payload Structure
All requests follow this base structure:
{
"number": "5511999999999",
"type": "button|button_legacy|list|poll|carousel",
"text": "Texto principal da mensagem",
"choices": ["opções baseadas no tipo escolhido"],
"footerText": "Texto do rodapé (opcional para botões e listas)",
"listButton": "Texto do botão (para listas)",
"selectableCount": "Número de opções selecionáveis (apenas para enquetes)"
}
Interactive Message Types
1. Buttons (type: “button”)
Creates interactive buttons with different action functionalities.
Specific Fields
footerText: Optional text displayed below the main messagechoices: Array of options that will be converted into buttons
Button Formats
Each button can be configured using | (pipe) or \n (line break) as separators:
-
Reply Button:
"texto|id"or"texto\nid"or"texto"(ID will be the same as the text)
-
Copy Button:
"texto|copy:código"or"texto\ncopy:código"
-
Call Button:
"texto|call:+5511999999999"or"texto\ncall:+5511999999999"
-
URL Button:
"texto|https://exemplo.com"or"texto|url:https://exemplo.com"
Buttons with image, video, or document
The button header can carry an image, a video, or a document in file, via URL or base64, as in
/send/media. The type is taken from the file itself; to force it, send mimetype: image, video, document, or the file’s MIME type (for example, video/mp4). The document uses the
name from docName; without it, the file name.
Audio is not allowed in the button: WhatsApp does not support audio in the header, and the API responds 400. Send the audio first,
with /send/media. Without mimetype, the API detects audio by the URL extension or by the beginning of the base64; for a
URL without an extension, provide the mimetype.
{
"number": "5511999999999",
"type": "button",
"text": "Escolha um produto:",
"file": "https://exemplo.com/produto1.jpg",
"choices": [
"Produto A|prod_a",
"Mais Info|https://exemplo.com/produto-a",
"Produto B|prod_b",
"Ligar|call:+5511999999999"
],
"footerText": "Produtos em destaque"
}
{
"number": "5511999999999",
"type": "button",
"text": "Tabela de preços de outubro",
"file": "https://exemplo.com/tabela.pdf",
"docName": "Tabela de preços.pdf",
"choices": ["Quero comprar|comprar", "Falar com vendedor|vendedor"]
}
If the media fails to download or upload to WhatsApp, the button is sent without it.
Buttons in legacy format (type: “button_legacy”)
Use button_legacy only when you need to send reply buttons in the
previous format. The choices format is:
"texto|id""texto\nid""texto|reply:id""texto"— the text itself will be used as the ID
This mode creates only reply buttons. URL, call, and copy actions and
media in file remain available in the current type: "button" format.
{
"number": "5511999999999",
"type": "button_legacy",
"text": "Deseja continuar?",
"choices": [
"Sim|confirmar",
"Não|reply:cancelar"
],
"footerText": "Escolha uma opção"
}
Complete Example
{
"number": "5511999999999",
"type": "button",
"text": "Como podemos ajudar?",
"choices": [
"Suporte Técnico|suporte",
"Fazer Pedido|pedido",
"Nosso Site|https://exemplo.com",
"Falar Conosco|call:+5511999999999"
],
"footerText": "Escolha uma das opções abaixo"
}
Limitations and Compatibility
Important: When combining reply buttons with other types (call, url, copy) in the same message, the following notice will be displayed: “This message can’t be displayed on WhatsApp Web. Open WhatsApp on your phone to view it.”
2. Lists (type: “list”)
Creates menus organized into sections with selectable items.
Specific Fields
listButton: Text of the button that opens the listfooterText: Optional footer textchoices: Array with the list’s sections and items
Choices Format
"[Título da Seção]": Starts a new section"texto|id|descrição": List item with:- texto: Item label
- id: Unique identifier, optional
- descrição: Additional, optional descriptive text
Complete Example
{
"number": "5511999999999",
"type": "list",
"text": "Catálogo de Produtos",
"choices": [
"[Eletrônicos]",
"Smartphones|phones|Últimos lançamentos",
"Notebooks|notes|Modelos 2024",
"[Acessórios]",
"Fones|fones|Bluetooth e com fio",
"Capas|cases|Proteção para seu device"
],
"listButton": "Ver Catálogo",
"footerText": "Preços sujeitos a alteração"
}
3. Polls (type: “poll”)
Creates interactive polls for voting.
Specific Fields
selectableCount: Number of options that can be selected (default: 1)choices: Simple array with the voting options
Complete Example
{
"number": "5511999999999",
"type": "poll",
"text": "Qual horário prefere para atendimento?",
"choices": [
"Manhã (8h-12h)",
"Tarde (13h-17h)",
"Noite (18h-22h)"
],
"selectableCount": 1
}
4. Carousel (type: “carousel”)
Creates a carousel of cards with images and interactive buttons.
Specific Fields
choices: Array with carousel elements in the following order:[Texto do cartão]: Card text in square brackets{URL ou base64 da imagem}: Image in curly braces- Card buttons (one per line):
"texto|copy:código"for a copy button"texto|https://url"for a link button"texto|call:+número"for a call button
Complete Example
{
"number": "5511999999999",
"type": "carousel",
"text": "Conheça nossos produtos",
"choices": [
"[Smartphone XYZ\nO mais avançado smartphone da linha]",
"{https://exemplo.com/produto1.jpg}",
"Copiar Código|copy:PROD123",
"Ver no Site|https://exemplo.com/xyz",
"Fale Conosco|call:+5511999999999",
"[Notebook ABC\nO notebook ideal para profissionais]",
"{https://exemplo.com/produto2.jpg}",
"Copiar Código|copy:NOTE456",
"Comprar Online|https://exemplo.com/abc",
"Suporte|call:+5511988888888"
]
}
Note: We created another endpoint for carousels:
/send/carousel. It works the same way, but with a different payload format. See which one is easier for you.
Terms of use
The interactive buttons and lists features may be discontinued at any time without prior notice. We are not responsible for any changes to or unavailability of these features.
Alternatives and Compatibility
Given the dynamic nature of these features, our endpoint was designed to make it easy to migrate between different message types (buttons, lists, and polls).
We recommend building your flows flexibly, ready to switch between the different types.
If a feature is discontinued, you can easily migrate to another message type just by changing the “type” field in the payload, keeping the same choices structure.
curl --request POST \
--url https://api.wppfy.com/send/menu \
--header 'Content-Type: application/json' \
--header 'token: <api-key>' \
--data '
{
"number": "5511999999999",
"type": "button",
"text": "Como podemos ajudar?",
"choices": [
"Suporte|suporte",
"Fazer pedido|pedido"
]
}
'{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"messageid": "<string>",
"chatid": "<string>",
"sender": "<string>",
"senderName": "<string>",
"isGroup": false,
"fromMe": false,
"messageType": "<string>",
"source": "<string>",
"messageTimestamp": 0,
"status": "<string>",
"text": "",
"quoted": "",
"edited": "",
"reaction": "",
"vote": "",
"convertOptions": "",
"buttonOrListid": "",
"owner": "",
"error": "",
"content": {},
"wasSentByApi": true,
"pinned": true,
"sendFunction": "<string>",
"sendPayload": {},
"fileURL": "<string>",
"callPeer": {
"jid": "<string>",
"name": "<string>",
"imagePreviewUrl": "<string>"
},
"send_folder_id": "<string>",
"track_source": "<string>",
"track_id": "<string>",
"ai_metadata": {
"agent_id": "<string>",
"turn": "<string>",
"flow_id": "<string>",
"request": {
"messages": "<array>",
"tools": "<array>",
"options": {
"model": "<string>",
"temperature": 123,
"maxTokens": 123,
"topP": 123,
"frequencyPenalty": 123,
"presencePenalty": 123
}
},
"response": {
"choices": "<array>",
"toolResults": "<array>",
"error": "<string>"
},
"outcome": {
"status": "sent",
"texts": 123,
"audio": true,
"menus": 123,
"files": 123,
"handoff": "<string>",
"lead": [
"<string>"
],
"detail": "<string>"
}
},
"sender_pn": "<string>",
"sender_lid": "<string>",
"sender_image_preview_url": "<string>",
"response": {
"status": "success",
"message": "Menu sent successfully"
}
}{
"error": "Missing required fields or invalid menu type"
}{
"error": "Invalid token"
}{
"error": "Rate limit exceeded"
}{
"error": "Failed to send menu"
}Authorizations
Body
ID of the chat the message will be sent to. Can be a phone number in international format, a group ID (@g.us), a user ID (with @s.whatsapp.net or @lid).
"5511999999999"
Menu type. Use button for the current format and button_legacy only for reply buttons in the previous format.
button, button_legacy, list, poll, carousel "list"
Main text (accepts placeholders)
"Choose an option:"
List of options. Use [Title] for sections in lists
[
"[Eletrônicos]",
"Smartphones|phones|Últimos lançamentos",
"Notebooks|notes|Modelos 2024",
"[Acessórios]",
"Fones|fones|Bluetooth e com fio",
"Capas|cases|Proteção para seu device"
]
Footer text (optional)
"Service menu"
Main button text
"View options"
Maximum number of selectable options (for polls)
1
Header media (type: button): image, video or document, by URL or base64, as in /send/media. Audio returns 400.
"https://exemplo.com/produto1.jpg"
Forces the file type (optional): image, video, document or the file's MIME type, such as video/mp4. Without it, the type comes from the file; any other value returns 400.
"video/mp4"
Filename when the file is a document. Without it, the file's name.
"Price list.pdf"
ID of the message to reply to
"3EB0538DA65A59F6D8A251"
Numbers to mention (comma separated)
"5511999999999,5511888888888"
Marks chat as read after sending
true
Marks latest received messages as read
true
Delay in milliseconds before sending; during delay 'Typing...' will appear
1000
Message tracking source
"chatwoot"
ID for message tracking (accepts duplicate values)
"msg_123456789"
If true, sends the message asynchronously via the internal queue
false
Response
Menu sent successfully
Represents a message exchanged in the system
Internal unique message ID (format r + 7 random hex characters)
Original message ID at the provider
Related conversation ID
Message sender ID
Sender's display name
Indicates whether it is a group message
Indicates whether the message was sent by the user
Message content type
Message source platform
Original message timestamp in milliseconds
Message lifecycle status.
Common examples: Queued, Canceled, Failed, Sent, Delivered, Read.
In group messages, it follows the most advanced participant: Delivered when at least one received it, Read when at least one read it, and Played when at least one listened to it.
Original message text
ID of the quoted/replied message
Message edit history
ID of the reacted message
Poll and list voting data
Conversion of message options, list, poll and buttons
ID of the selected button or list item
Message owner
Error message if sending failed
Raw message content (serialized JSON or text)
Indicates whether the message was sent via API
Message pinned in the conversation (pin and unpin also arrive in messages_update, types Pinned and Unpinned)
Function used to send the message (when sent via API)
Payload used in the send when available. Base64 media data is omitted.
Message file URL or reference
Known contact associated with a call, when available in messageType: call.
Show child attributes
Show child attributes
Folder associated with the send (when applicable)
Tracking source
Tracking ID (may repeat)
Record of an AI round. The full one (request to the model, response, and outcome) is stored once, on the contact's last message that the round replied to, even when nothing went out (error, discarded response). Each message sent by the AI carries only agent_id and turn (the id of that contact message).
Show child attributes
Show child attributes
Sender's resolved PN JID (when available)
Sender's original LID (when available)
Temporary preview URL of the sender, when known. Hydration in this version fills in messages received in groups; the field may be omitted. It does not trigger a photo query to WhatsApp during listing.
Show child attributes
Show child attributes
