curl --request POST \
--url https://api.wppfy.com/send/carousel \
--header 'Content-Type: application/json' \
--header 'token: <api-key>' \
--data '
{
"number": "5511999999999",
"text": "Nossos produtos",
"carousel": [
{
"text": "Smartphone XYZ",
"image": "https://exemplo.com/produto.jpg",
"buttons": [
{
"id": "comprar_xyz",
"text": "Comprar agora",
"type": "REPLY"
}
]
}
]
}
'import requests
url = "https://api.wppfy.com/send/carousel"
payload = {
"number": "5511999999999",
"text": "Nossos produtos",
"carousel": [
{
"text": "Smartphone XYZ",
"image": "https://exemplo.com/produto.jpg",
"buttons": [
{
"id": "comprar_xyz",
"text": "Comprar agora",
"type": "REPLY"
}
]
}
]
}
headers = {
"token": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {token: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
number: '5511999999999',
text: 'Nossos produtos',
carousel: [
{
text: 'Smartphone XYZ',
image: 'https://exemplo.com/produto.jpg',
buttons: [{id: 'comprar_xyz', text: 'Comprar agora', type: 'REPLY'}]
}
]
})
};
fetch('https://api.wppfy.com/send/carousel', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.wppfy.com/send/carousel",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'number' => '5511999999999',
'text' => 'Nossos produtos',
'carousel' => [
[
'text' => 'Smartphone XYZ',
'image' => 'https://exemplo.com/produto.jpg',
'buttons' => [
[
'id' => 'comprar_xyz',
'text' => 'Comprar agora',
'type' => 'REPLY'
]
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"token: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.wppfy.com/send/carousel"
payload := strings.NewReader("{\n \"number\": \"5511999999999\",\n \"text\": \"Nossos produtos\",\n \"carousel\": [\n {\n \"text\": \"Smartphone XYZ\",\n \"image\": \"https://exemplo.com/produto.jpg\",\n \"buttons\": [\n {\n \"id\": \"comprar_xyz\",\n \"text\": \"Comprar agora\",\n \"type\": \"REPLY\"\n }\n ]\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("token", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.wppfy.com/send/carousel")
.header("token", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"number\": \"5511999999999\",\n \"text\": \"Nossos produtos\",\n \"carousel\": [\n {\n \"text\": \"Smartphone XYZ\",\n \"image\": \"https://exemplo.com/produto.jpg\",\n \"buttons\": [\n {\n \"id\": \"comprar_xyz\",\n \"text\": \"Comprar agora\",\n \"type\": \"REPLY\"\n }\n ]\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.wppfy.com/send/carousel")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["token"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"number\": \"5511999999999\",\n \"text\": \"Nossos produtos\",\n \"carousel\": [\n {\n \"text\": \"Smartphone XYZ\",\n \"image\": \"https://exemplo.com/produto.jpg\",\n \"buttons\": [\n {\n \"id\": \"comprar_xyz\",\n \"text\": \"Comprar agora\",\n \"type\": \"REPLY\"\n }\n ]\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"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": "Carousel sent successfully"
}
}{
"error": "Missing required fields or invalid card format"
}{
"error": "Invalid token"
}{
"error": "Failed to send carousel"
}Send media carousel with buttons
This endpoint lets you send a carousel with images and interactive buttons.
It works the same way as the /send/menu endpoint with type: carousel, but using a different payload format.
Common Fields
This endpoint supports all common optional fields documented in the “Send Message” tag, including:
delay, readchat, readmessages, replyid, mentions, forward, track_source, track_id, placeholders and sending to groups.
Payload Structure
{
"number": "5511999999999",
"text": "Texto principal",
"carousel": [
{
"text": "Texto do cartão",
"image": "URL da imagem",
"buttons": [
{
"id": "resposta1",
"text": "Texto do botão",
"type": "REPLY"
}
]
}
]
}
Button Types
-
REPLY: Quick reply button- When clicked, sends the id value as a reply to the chat
- The id will be the text sent as the reply
-
URL: Link button- When clicked, opens the specified URL
- The id must contain the full URL (e.g.: https://exemplo.com)
-
COPY: Copy text button- When clicked, copies the text to the clipboard
- The id will be the text that is copied
-
CALL: Call button- When clicked, starts a phone call
- The id must contain the phone number
Button Example
{
"buttons": [
{
"id": "Sim, quero comprar!",
"text": "Confirmar Compra",
"type": "REPLY"
},
{
"id": "https://exemplo.com/produto",
"text": "Ver Produto",
"type": "URL"
},
{
"id": "CUPOM20",
"text": "Copiar Cupom",
"type": "COPY"
},
{
"id": "5511999999999",
"text": "Falar com Vendedor",
"type": "CALL"
}
]
}
Full Carousel Example
{
"number": "5511999999999",
"text": "Nossos Produtos em Destaque",
"carousel": [
{
"text": "Smartphone XYZ\nO mais avançado smartphone da linha",
"image": "https://exemplo.com/produto1.jpg",
"buttons": [
{
"id": "SIM_COMPRAR_XYZ",
"text": "Comprar Agora",
"type": "REPLY"
},
{
"id": "https://exemplo.com/xyz",
"text": "Ver Detalhes",
"type": "URL"
}
]
},
{
"text": "Cupom de Desconto\nGanhe 20% OFF em qualquer produto",
"image": "https://exemplo.com/cupom.jpg",
"buttons": [
{
"id": "DESCONTO20",
"text": "Copiar Cupom",
"type": "COPY"
},
{
"id": "5511999999999",
"text": "Falar com Vendedor",
"type": "CALL"
}
]
}
]
}
curl --request POST \
--url https://api.wppfy.com/send/carousel \
--header 'Content-Type: application/json' \
--header 'token: <api-key>' \
--data '
{
"number": "5511999999999",
"text": "Nossos produtos",
"carousel": [
{
"text": "Smartphone XYZ",
"image": "https://exemplo.com/produto.jpg",
"buttons": [
{
"id": "comprar_xyz",
"text": "Comprar agora",
"type": "REPLY"
}
]
}
]
}
'import requests
url = "https://api.wppfy.com/send/carousel"
payload = {
"number": "5511999999999",
"text": "Nossos produtos",
"carousel": [
{
"text": "Smartphone XYZ",
"image": "https://exemplo.com/produto.jpg",
"buttons": [
{
"id": "comprar_xyz",
"text": "Comprar agora",
"type": "REPLY"
}
]
}
]
}
headers = {
"token": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {token: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
number: '5511999999999',
text: 'Nossos produtos',
carousel: [
{
text: 'Smartphone XYZ',
image: 'https://exemplo.com/produto.jpg',
buttons: [{id: 'comprar_xyz', text: 'Comprar agora', type: 'REPLY'}]
}
]
})
};
fetch('https://api.wppfy.com/send/carousel', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.wppfy.com/send/carousel",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'number' => '5511999999999',
'text' => 'Nossos produtos',
'carousel' => [
[
'text' => 'Smartphone XYZ',
'image' => 'https://exemplo.com/produto.jpg',
'buttons' => [
[
'id' => 'comprar_xyz',
'text' => 'Comprar agora',
'type' => 'REPLY'
]
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"token: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.wppfy.com/send/carousel"
payload := strings.NewReader("{\n \"number\": \"5511999999999\",\n \"text\": \"Nossos produtos\",\n \"carousel\": [\n {\n \"text\": \"Smartphone XYZ\",\n \"image\": \"https://exemplo.com/produto.jpg\",\n \"buttons\": [\n {\n \"id\": \"comprar_xyz\",\n \"text\": \"Comprar agora\",\n \"type\": \"REPLY\"\n }\n ]\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("token", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.wppfy.com/send/carousel")
.header("token", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"number\": \"5511999999999\",\n \"text\": \"Nossos produtos\",\n \"carousel\": [\n {\n \"text\": \"Smartphone XYZ\",\n \"image\": \"https://exemplo.com/produto.jpg\",\n \"buttons\": [\n {\n \"id\": \"comprar_xyz\",\n \"text\": \"Comprar agora\",\n \"type\": \"REPLY\"\n }\n ]\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.wppfy.com/send/carousel")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["token"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"number\": \"5511999999999\",\n \"text\": \"Nossos produtos\",\n \"carousel\": [\n {\n \"text\": \"Smartphone XYZ\",\n \"image\": \"https://exemplo.com/produto.jpg\",\n \"buttons\": [\n {\n \"id\": \"comprar_xyz\",\n \"text\": \"Comprar agora\",\n \"type\": \"REPLY\"\n }\n ]\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"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": "Carousel sent successfully"
}
}{
"error": "Missing required fields or invalid card format"
}{
"error": "Invalid token"
}{
"error": "Failed to send carousel"
}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"
Array of carousel cards
Show child attributes
Show child attributes
Main message text
"Our Featured Products"
Delay in milliseconds before sending
1000
Marks chat as read after sending
true
Marks latest received messages as read
true
ID of the message to reply to
"3EB0538DA65A59F6D8A251"
Numbers to mention (comma separated)
"5511999999999,5511888888888"
Marks the message as forwarded on WhatsApp
false
If true, sends the message asynchronously via the internal queue
false
Message tracking source
"chatwoot"
ID for message tracking (accepts duplicate values)
"msg_123456789"
Response
Carousel 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
