Documentação da API
Uma API HTTP com o contrato que o mercado já usa: mesmos caminhos, mesmo formato de credenciais e mesmo formato de webhook. Se você já integra WhatsApp por HTTP, basta trocar a URL base.
Visão geral
Toda operação de um número acontece dentro de uma instância. Cada instância tem um
instanceId e um token, ambos no caminho da URL. O cabeçalho opcional
Client-Token adiciona uma camada de segurança em nível de conta.
Autenticação
| Credencial | Onde vai | Quando |
|---|---|---|
instanceId + token |
Caminho da URL | Toda rota de instância. Identificam e autorizam. |
Client-Token |
Header HTTP | Opcional. Se definido na instância, passa a ser obrigatório em toda requisição. |
Authorization: Bearer |
Header HTTP | Apenas nas rotas administrativas /admin/*. |
URL base
Erros
Respostas de erro trazem o código HTTP e um corpo JSON com a descrição.
| Código | Significado |
|---|---|
400 |
Corpo inválido ou campo obrigatório ausente. |
401 |
Credencial inválida: token, Client-Token ou Bearer administrativo. |
404 |
Instância inexistente ou token do caminho inválido. Os dois devolvem a mesma coisa de propósito: respostas diferentes confirmariam a existência de conexões que não são suas. |
202 |
QR Code ainda não pronto — repita a chamada em instantes. |
409 |
Instância já conectada (ao pedir QR Code). |
Limitações e configuração
A API fala com o WhatsApp como um aparelho vinculado (não é a API oficial da Meta). Alguns recursos dependem disso ou de a Meta liberar o recurso para a conta de quem recebe. Cada rota afetada traz um aviso; o resumo:
| Recurso | Situação |
|---|---|
send-text com spoiler; send-poll com allowAddOption |
Dependem de a Meta liberar o recurso para quem recebe. Hoje chegam como "Não foi possível carregar". |
communities/join-group |
Não confirmado (405 no teste). |
send-event-response; reminderMinutes em send-event |
Aceitos pelo servidor, não conferidos no aparelho. |
send-status com phones |
Restrição de quem vê ainda não conferida no aparelho. |
ask-meta-ai |
A conversa aparece no celular conectado e a Meta AI lembra o contexto. Use com moderação: volume alto pode chamar a atenção da Meta. |
forward-message |
Só com MESSAGE_CONTENT_TTL_HOURS ligado no servidor. |
fetch-history |
Ainda não funciona: o celular responde vazio. |
| Destrancar conversa | Não existe: o WhatsApp só destranca com digital/PIN no celular. |
| Desconectar outro aparelho da conta; ler ou alterar o e-mail da conta | Não existem: o WhatsApp só permite isso no celular principal. A instância pode desconectar a si mesma (disconnect) e listar os aparelhos (linked-devices). |
| Botões nativos de pedir localização, pedir endereço e lembrete | Não funcionam fora da API oficial: o servidor recusa ou o aparelho não exibe. |
| Reportar conversão de anúncio por etiqueta | Etiqueta pela API não reporta conversão à Meta. O caminho é a Conversions API com o ctwaClid (detalhes). |
Teto de ritmo por conexão
O limite do WhatsApp é do número, não da conta — e estourá-lo é o caminho curto para o número ser bloqueado. Cada balde é independente e conta por conexão. A rajada inicial é um décimo do teto (mínimo 5), então passar 30 grupos seguidos não esbarra no teto.
| Balde | Padrão | O que conta |
|---|---|---|
| Grupo e status | 60 por minuto | Mensagens endereçadas a @g.us e a status@broadcast. É o
padrão de difusão, o que mais chama atenção da Meta. |
| Conversa 1:1 | 480 por minuto | Conversas diretas, bem mais toleradas. O valor saiu de medição (pico
real de produção: 204 msg/min e 19 msg/s), com mais que o dobro de folga
— apertar aqui não atrasa envio, faz o gateway desistir e marcar
deliveryError. |
| Operação de grupo | 15 por minuto | create-group, create-community,
group-settings, group-participants,
group-invite-link, group-review, group-mode,
link-group, unlink-group. Operações estruturais são as que
o WhatsApp limita com mais rigor. |
Estourou o teto: HTTP 429 com o cabeçalho
Retry-After dizendo quantos segundos esperar. Honre o prazo
— retentar na hora só aumenta a fila.
Configurável por conexão em
PUT /account/instances/{id}/settings (limiteGrupo,
limiteDireto, limiteOps: -1 volta ao padrão,
0 desliga aquele balde). O teto em vigor sai no campo limites da
resposta das rotas de conexão. No servidor, as variáveis são
LIMITE_GRUPO_POR_MINUTO, LIMITE_DIRETO_POR_MINUTO e
LIMITE_OPS_GRUPO_POR_MINUTO.
O teto protege o seu sistema de receber erro cru do WhatsApp, mas não elimina o risco de bloqueio do número. O padrão que mais derruba número é mandar a mesma mensagem para muitos grupos seguidos: espaçe os envios entre grupos em vez de disparar tudo de uma vez.
Configuração do servidor
| Variável | Descrição |
|---|---|
MESSAGE_CONTENT_TTL_HOURS |
Guarda o conteúdo das mensagens enviadas e recebidas por esse número de horas, para
forward-message encaminhar pelo ID. 0 (padrão) desliga: nada é guardado.
O conteúdo vencido é apagado de hora em hora. |
send-charge usa centavos (R$ 1,00 = 100).
Criar instância
Requer Authorization: Bearer <ADMIN_TOKEN>.
| Campo | Tipo | Descrição |
|---|---|---|
name |
string | Rótulo da instância no painel. |
webhookUrl |
string | URL que recebe os eventos de entrada. |
clientToken |
string | Opcional. Se preenchido, vira obrigatório no header de toda chamada. |
{
"name": "Vendas",
"webhookUrl": "https://seu-app.com/webhook",
"clientToken": "opcional-token-de-seguranca"
}
{
"id": "b1a2c3d4-...",
"token": "f9e8d7c6-...",
"clientToken": "opcional-token-de-seguranca",
"name": "Vendas",
"webhookUrl": "https://seu-app.com/webhook",
"apiBaseUrl": "https://api.zapbot.online/instances/b1a2.../token/f9e8..."
}
Listar instâncias
Retorna todas as instâncias com o status de conexão atual e o telefone conectado.
Deletar instância
Desconecta o número e remove a instância. A operação não é reversível.
Status da conexão
{
"connected": true,
"session": true,
"smartphoneConnected": true,
"phone": "5511777777777"
}
QR Code
Inicia o pareamento e devolve o QR Code como PNG em base64. Escaneie em WhatsApp › Aparelhos conectados.
202.
O código chega na chamada seguinte, poucos segundos depois.
{
"value": "data:image/png;base64,iVBORw0KGgoAAAANS..."
}
Variantes: GET /qr-code devolve o conteúdo bruto do código em
value; GET /restart força a reconexão.
Parear por código
Alternativa ao QR Code: devolve um código de 8 caracteres que a pessoa digita no WhatsApp em Aparelhos conectados → Conectar com número de telefone. Serve para parear sem ter como mostrar uma imagem na tela.
{phone} vai com código do país e só dígitos.
Chamar de novo durante o mesmo pareamento devolve o código em andamento em vez de
começar outro. Com a instância já conectada, responde 409.
{
"code": "ABCD-EFGH"
}
Reconectar
Refaz a conexão com o WhatsApp sem perder a sessão — o
número continua pareado. Diferente de disconnect, que desfaz o
pareamento e exige ler o QR de novo.
Desconectar
Encerra a sessão do número. A instância continua existindo e pode ser pareada de novo.
Configurar webhook
{
"value": "https://seu-app.com/webhook",
"notifySentByMe": true,
"notifyGroups": true,
"notifyGroupMessages": false,
"notifyExtras": false
}
update-webhook-received aceita o mesmo corpo e existe por
compatibilidade — nesta API as duas configuram o mesmo endereço.
notifySentByMe faz o webhook receber também o que a própria
linha envia, inclusive pelo celular. Útil para manter seu banco em dia quando alguém
responde pelo aparelho.
notifyGroups liga o
GroupParticipantCallback (entrada, saída, remoção, promoção e
rebaixamento de participante). Desligado por padrão: o volume é
proporcional ao tamanho do grupo, e um grupo de mil entrando de uma vez é uma chamada com
mil números.
notifyExtras é a chave mestra do conjunto de callbacks
adicionais: voto de enquete, mensagem apagada, evento do contato, tipos de entrada que
antes não geravam aviso (lista de contatos, localização ao vivo, produto do catálogo,
vídeo redondo), etiquetas, respostas rápidas e status.
Desligado por padrão — ligue só quando o seu endpoint já aguentar o
volume, e tenha um plano B se o webhook cair.
notifyGroupMessages liga a mensagem escrita
dentro de um grupo no webhook (o mesmo ReceivedCallback, com
isGroup: true e participantPhone com quem escreveu). É
separado de notifyGroups porque o volume não tem nada a ver: um é a lista
de gente, o outro é o papo inteiro. Desligado por padrão.
Todos são por conexão. Desligar volta ao comportamento anterior sem precisar de deploy.
Enviar texto
| Campo | Tipo | Descrição |
|---|---|---|
phoneobrigatório |
string | Número com código do país, só dígitos. |
messageobrigatório |
string | Texto da mensagem. |
messageId |
string | Responde citando essa mensagem. |
participant |
string | Autor da mensagem citada. Opcional: sem ele, a API usa o registro de envio/recebimento; se a mensagem não estiver no registro (grupo, enviada pelo celular ou mais antiga que o registro), o texto sai sem citação. |
mentioned |
string[] | Números mencionados; o texto deve trazer @numero. |
mentionAll |
boolean | Menciona todos do grupo; o texto deve trazer @all. |
groupMentioned |
object[] | Grupos da comunidade mencionados (phone, subject); o texto traz @ + id do grupo. |
forwarded |
boolean | Marca a mensagem como "Encaminhada". |
spoiler |
boolean | Esconde as partes entre || || até tocar; sem marcação, esconde o texto todo. Veja o aviso abaixo. |
spoiler depende de a Meta liberar o recurso para quem recebe. Hoje a mensagem chega como "Não foi possível carregar a mensagem". Use só em teste.
curl -X POST "https://api.zapbot.online/instances/{id}/token/{token}/send-text" \ -H "Content-Type: application/json" \ -H "Client-Token: seu-token-de-seguranca" \ -d '{"phone":"5511999999999","message":"Olá!"}'
{
"zaapId": "3999984CE2CF9E0",
"messageId": "3EB0C9A1F2D4",
"id": "3EB0C9A1F2D4"
}
Guarde o messageId: é ele que identifica a mensagem nos webhooks de status
e em send-reaction.
Enviar imagem
| Campo | Tipo | Descrição |
|---|---|---|
phoneobrigatório |
string | Destinatário. |
imageobrigatório |
string | URL http(s) ou data URI base64 (data:image/png;base64,...). |
caption |
string | Legenda opcional. |
viewOnce |
boolean | Envia como visualização única. |
forwarded |
boolean | Marca a mensagem como "Encaminhada". |
{
"phone": "5511999999999",
"image": "https://exemplo.com/foto.jpg",
"caption": "legenda opcional"
}
Enviar áudio
Entregue como mensagem de voz no aparelho do destinatário.
Opcionais: viewOnce (visualização única) e forwarded (marca "Encaminhada").
{
"phone": "5511999999999",
"audio": "https://exemplo.com/audio.ogg"
}
Enviar vídeo
Opcionais: viewOnce (visualização única), gif: true (toca em loop e sem som, como GIF) e forwarded (marca "Encaminhada").
{
"phone": "5511999999999",
"video": "https://exemplo.com/video.mp4",
"caption": "legenda"
}
Enviar documento
A extensão vai no caminho, por exemplo /send-document/pdf.
Opcional: forwarded (marca "Encaminhada").
{
"phone": "5511999999999",
"document": "https://exemplo.com/arquivo.pdf",
"fileName": "contrato.pdf"
}
Enviar localização
latitude e longitude são números, não strings.
{
"phone": "5511999999999",
"title": "Escritório",
"address": "Av. Paulista, 1000",
"latitude": -23.5615,
"longitude": -46.656
}
Enviar contato
{
"phone": "5511999999999",
"contactName": "João Silva",
"contactPhone": "5511888888888",
"contactBusinessDescription": "opcional"
}
Enviar link com preview
{
"phone": "5511999999999",
"message": "Confira:",
"linkUrl": "https://zapbot.online",
"title": "Zapbot",
"linkDescription": "API de WhatsApp",
"image": "https://exemplo.com/preview.png"
}
Enviar reação
O messageId é o mesmo devolvido no envio ou recebido no webhook.
{
"phone": "5511999999999",
"messageId": "3EB0C9A1F2D4",
"reaction": "👍"
}
Enviar figurinha
Aceita URL ou base64. PNG e JPEG são convertidos para WebP; GIF vira figurinha animada.
{
"phone": "5511999999999",
"sticker": "https://exemplo.com/fig.webp"
}
Enviar botões
Até 3 botões. type aceita REPLY, URL, CALL, COPY e CONTACT_INFO.
Com image, video ou document, a mídia vira o cabeçalho da mensagem.
WebView: num botão URL, "webview": true abre o link no navegador dentro do WhatsApp, em vez do navegador do aparelho. Só vale para type URL.
CONTACT_INFO envia o botão "Compartilhar informações de contato" do app Business (só label). A resposta do cliente chega como contato (vCard). Nos testes, o botão apareceu no celular, mas não no WhatsApp Web.
{
"phone": "5511999999999",
"message": "Como prefere continuar?",
"footer": "Atendimento",
"buttons": [
{
"type": "REPLY",
"id": "sim",
"label": "Quero agora"
},
{
"type": "URL",
"label": "Ver catálogo",
"url": "https://exemplo.com",
"webview": true
},
{
"type": "CALL",
"label": "Ligar",
"phone": "5511999999999"
}
]
}
Enviar lista de opções
Cabe mais opção do que em botões, e elas ficam agrupadas por seção.
{
"phone": "5511999999999",
"message": "Escolha o horário",
"buttonText": "Ver horários",
"sections": [
{
"title": "Manhã",
"rows": [
{
"id": "9h",
"title": "09:00",
"description": "Última vaga"
},
{
"id": "10h",
"title": "10:00",
"description": ""
}
]
}
]
}
Enviar botão de Pix
keyType aceita CPF, CNPJ, PHONE, EMAIL e EVP.
{
"phone": "5511999999999",
"pixKey": "loja@exemplo.com",
"keyType": "EMAIL",
"merchantName": "Loja Exemplo"
}
Enviar carrossel
Até 10 cards, cada um com no máximo 2 botões — o terceiro é descartado pelo aplicativo, sem aviso.
Cada card precisa de image ou video. Os botões podem misturar tipos, e os cards não precisam ter os mesmos botões entre si.
{
"phone": "5511999999999",
"message": "Novidades da semana",
"carousel": [
{
"text": "Camiseta branca — R$ 79",
"image": "https://exemplo.com/1.jpg",
"buttons": [
{
"type": "REPLY",
"id": "c1",
"label": "Quero esta"
},
{
"type": "URL",
"label": "Detalhes",
"url": "https://exemplo.com/1"
}
]
},
{
"text": "Boné preto — R$ 59",
"image": "https://exemplo.com/2.jpg",
"buttons": [
{
"type": "REPLY",
"id": "c2",
"label": "Quero este"
}
]
}
]
}
Enviar enquete
maxSelections acima de 1 permite marcar mais de uma opção. Os votos chegam pelo webhook.
Opcionais: correctAnswer (texto da opção certa: vira quiz, escolha única), endTime (encerra a votação nesse momento, em segundos Unix) e hideVoters (esconde quem votou em cada opção — só na tela dos participantes; a API continua recebendo o voto com o número).
allowAddOption (participantes acrescentam opções) depende de a Meta liberar o recurso para quem recebe. Hoje a enquete chega como "Não foi possível carregar". Use só em teste.
{
"phone": "5511999999999",
"poll": "Qual horário fica melhor?",
"pollOptions": [
"Manhã",
"Tarde",
"Noite"
],
"maxSelections": 1
}
Publicar status
Publica no status (stories). Envie message, image ou video.
Sem phones, vai para todos os contatos conforme a privacidade configurada no aparelho. Com phones, até 5000 números.
phones, o status é enviado só para essa lista, mas ainda não foi conferido num aparelho que quem ficou de fora realmente não vê.
{
"message": "Estamos atendendo até às 20h hoje"
}
Editar mensagem
O WhatsApp só aceita edição até cerca de 15 minutos depois do envio.
{
"phone": "5511999999999",
"messageId": "3EB0C9A1F2D4",
"message": "Texto corrigido"
}
Apagar mensagem
owner identifica de quem é a mensagem quando ela não é sua; sem ele, apaga uma sua.
{
"phone": "5511999999999",
"messageId": "3EB0C9A1F2D4"
}
Marcar como lida
Manda o recibo de leitura e zera o contador de não lidas da conversa.
{
"phone": "5511999999999",
"messageIds": [
"3EB0C9A1F2D4"
]
}
Favoritar mensagem
value ausente alterna o estado atual. fromMe indica que a mensagem é sua.
{
"phone": "5511999999999",
"messageId": "3EB0C9A1F2D4",
"value": true
}
Listar conversas
Aceita search, unread=true, archived=true|false, limit (até 200) e offset.
A lista vem do nosso banco, alimentado pelo histórico do pareamento e mantido em dia a cada mensagem — por isso responde mesmo com o número fora do ar.
Conexões pareadas antes desta versão começam vazias e vão se preenchendo conforme o tráfego.
{
"chats": [
{
"chatId": "5511999999999@s.whatsapp.net",
"phone": "5511999999999",
"name": "Maria",
"isGroup": false,
"unread": 2,
"archived": false,
"pinned": false,
"lastMessageAt": "2026-09-11T14:03:00Z"
}
],
"limit": 50,
"offset": 0
}
Sincronizar conversas
Repopula a lista de chats a partir do WhatsApp, sem
desparear o número. Serve para conexões pareadas antes de a listagem existir,
que começam vazias.
O que ela traz: todos os grupos de que a linha participa, com nome, e toda conversa que tenha algum estado — arquivada, fixada ou silenciada.
O que ela não traz: conversas diretas comuns. O WhatsApp só registra desvios do padrão, então uma conversa direta sem arquivar, fixar nem silenciar não tem nada a sincronizar. Essas aparecem sozinhas na próxima mensagem que passar, ou de uma vez num novo pareamento.
É uma varredura completa do lado do WhatsApp: uma por conexão de
cada vez, no máximo uma por minuto. Fora disso responde 409 — estado, não
falha, então basta tentar mais tarde.
Arquivar conversa
value ausente alterna. Vale para grupo também — mande o ID do grupo em phone.
{
"phone": "5511999999999",
"value": true
}
Fixar conversa
O WhatsApp limita a 3 conversas fixadas.
{
"phone": "5511999999999",
"value": true
}
Silenciar conversa
muteSeconds define por quanto tempo. Sem ele, silencia por tempo indeterminado.
{
"phone": "5511999999999",
"value": true,
"muteSeconds": 28800
}
Apagar conversa
Apaga a conversa inteira do aparelho. Não tem volta.
{
"phone": "5511999999999"
}
Etiquetar conversa
Só existe em contas WhatsApp Business. value ausente alterna.
{
"phone": "5511999999999",
"labelId": "1",
"value": true
}
Presença (digitando…)
status aceita composing (digitando), recording (gravando áudio), paused, available e unavailable.
Sem phone, muda a presença global da linha em vez da de uma conversa.
{
"phone": "5511999999999",
"status": "composing",
"duration": 5
}
Perfil de um contato
O que volta depende da privacidade da pessoa: recado oculto vem vazio, e isso não é erro.
Não responde se o número existe — para isso use phone-exists, que consulta por outra via. Esta rota devolve entrada até para número inexistente.
{
"phones": [
"5511999999999"
]
}
{
"contacts": [
{
"phone": "5511999999999",
"jid": "5511999999999@s.whatsapp.net",
"status": "Disponível",
"pictureId": "1258241349",
"devices": 2
}
]
}
Foto de um contato
preview traz a miniatura, mais rápida e suficiente para listar.
A URL é da CDN do WhatsApp e expira em horas. Quem for guardar precisa baixar os bytes.
{
"phone": "5511999999999",
"preview": true
}
{
"phone": "5511999999999",
"hasPicture": true,
"url": "https://pps.whatsapp.net/…",
"id": "1258241349",
"type": "preview"
}
Estado de uma mensagem
Responde do registro desta conexão, sem falar com o WhatsApp — que não tem consulta para isso: o estado chega por recibo, uma vez, e quem não guardou perdeu.
Some junto com o registro, quando o expurgo passa.
{
"messageId": "3EB0C9A1F2D4"
}
{
"messageId": "3EB0C9A1F2D4",
"direction": "out",
"phone": "5511999999999",
"kind": "text",
"status": "READ",
"at": "2026-09-11T14:03:00Z",
"deliveredAt": "2026-09-11T14:03:01Z",
"readAt": "2026-09-11T14:05:12Z"
}
Mídia de uma mensagem
Devolve o endereço da mídia que já re-hospedamos no recebimento. Não rebaixa do WhatsApp: o download original exige chaves que vêm dentro da mensagem, e não guardamos a mensagem.
Vale para mídia recebida, por até 24 horas. Depois disso o arquivo é apagado e a resposta é 410.
{
"messageId": "3EB0C9A1F2D4"
}
{
"messageId": "3EB0C9A1F2D4",
"kind": "image",
"url": "https://api.zapbot.online/media/8f3c….jpg"
}
Resultado da enquete
Só apura enquete enviada por esta API. O voto chega com a opção em SHA-256, nunca em texto: sem ter guardado as opções no envio, o hash não endereça nada.
Opção sem voto aparece com zero, e não sumindo — o resultado mentiria por omissão.
Votar de novo substitui o voto anterior, como no aplicativo. Desmarcar tudo zera a contagem daquele eleitor.
{
"messageId": "3EB0C9A1F2D4",
"poll": "Qual horário?",
"pollOptions": [
"Manhã",
"Tarde"
],
"totalVoters": 2,
"results": [
{
"option": "Manhã",
"count": 2
},
{
"option": "Tarde",
"count": 0
}
],
"voters": [
{
"phone": "5511999999999@s.whatsapp.net",
"options": [
"Manhã"
],
"votedAt": "2026-09-11T14:10:00Z"
}
]
}
Dados de um canal
Aceita newsletterId de quem já segue, ou inviteLink para espiar um canal antes de seguir.
O link pode vir inteiro ou só o código.
{
"newsletterId": "120363000000000000@newsletter"
}
Publicações de um canal
Canal é difusão pública — quem segue recebe o mesmo que todo mundo — então aqui o texto vem junto.
before pagina para trás a partir de um serverId já conhecido. count vai até 100, padrão 20.
{
"newsletterId": "120363000000000000@newsletter",
"count": 20
}
{
"messages": [
{
"messageId": "3EB0…",
"serverId": 42,
"type": "text",
"text": "Novidades da semana",
"views": 128,
"timestamp": "2026-09-11T12:00:00Z"
}
]
}
Listar contatos
Vem da agenda sincronizada pelo aparelho.
{
"contacts": [
{
"phone": "5511999999999",
"name": "Maria",
"short": "Maria",
"notify": "Maria S."
}
]
}
Verificar se o número existe
Aceita phone (um) ou phones (vários). Vale conferir antes de disparar em lote: número inexistente conta como tentativa.
A resposta traz uma entrada para cada número enviado, inclusive os que não existem.
{
"phones": [
"5511999999999",
"5511888888888"
]
}
{
"exists": [
{
"phone": "5511999999999",
"exists": true
},
{
"phone": "5511888888888",
"exists": false
}
]
}
Bloquear contato
Para desbloquear, use unblock com o mesmo corpo.
{
"phone": "5511999999999"
}
Listar bloqueados
{
"blocklist": [
"5511888888888"
]
}
Listar grupos
{
"groups": [
{
"groupId": "120363000000000000@g.us",
"name": "Equipe",
"participantCount": 987,
"amAdmin": true,
"announce": false,
"locked": false,
"communityId": "120363111111111111@g.us",
"isCommunity": false,
"isAnnouncementGroup": false,
"avatarUrl": "https://.../media/grupo.jpg"
}
]
}
A lista vem sem os participantes — só a contagem
(participantCount). Com dezenas de grupos de mil pessoas, todos os membros de
todos seria uma resposta inutilizável. Para os participantes de um grupo, use
groups/{groupId}.
announce restringe o envio de mensagem aos
administradores; locked restringe a edição dos dados. São
independentes: um grupo pode ser aberto para falar e travado para editar.
Comunidade é um grupo no protocolo, então ela aparece nesta lista igual
aos filhos. isCommunity marca o grupo-pai,
isAnnouncementGroup marca o grupo de avisos e communityId diz a
que comunidade o grupo pertence.
Consultar um grupo
Traz um grupo com a lista completa de participantes.
groupId é o JID completo (120363000000000000@g.us); só os
dígitos também são aceitos.
| Query | Padrão | Efeito |
|---|---|---|
participants | true |
false traz só participantCount. É o jeito rápido de
conferir se o grupo encheu, sem carregar mil números. |
inviteLink | true |
false pula a consulta ao WhatsApp e não traz inviteLink. |
{
"groupId": "120363000000000000@g.us",
"name": "AULÃO #01",
"description": "...",
"participantCount": 987,
"amAdmin": true,
"announce": false,
"locked": false,
"communityId": "120363111111111111@g.us",
"isAnnouncementGroup": false,
"inviteLink": "https://chat.whatsapp.com/XXXXXXXX",
"participants": [
{
"phone": "5511999999999",
"name": "Maria",
"isAdmin": false,
"isSuperAdmin": false,
"joinedAt": 1759320000000
}
]
}
participants[].phone já vem resolvido de LID para número
quando possível: um @lid não endereça ninguém nem cruza com a agenda.
inviteLink só vem quando amAdmin é true — link
de convite exige ser administrador.
participants[].joinedAt é quando nós vimos
a pessoa entrar (milissegundos Unix). O WhatsApp não informa data de entrada em lugar
nenhum, então o campo só existe para quem entrou enquanto a conexão estava no ar
— fica ausente para quem já estava no grupo, porque inventar data
seria pior que não ter. Quem sai perde a data de propósito: se voltar, a data é a da
volta.
A foto de um grupo sozinho: GET
/instances/{id}/token/{token}/groups/{groupId}/avatar.
Criar grupo
Quem cria entra como administrador. Números que não existem no WhatsApp são recusados.
{
"name": "Time de vendas",
"phones": [
"5511999999999",
"5511888888888"
]
}
Participantes do grupo
action aceita add, remove, promote e demote.
A resposta diz o resultado de cada número: quem tem a privacidade fechada não entra por adição, só por convite.
{
"groupId": "120363000000000000@g.us",
"phones": [
"5511888888888"
],
"action": "add"
}
Configurar grupo
Só muda o que você mandar. O nome tem limite de 25 caracteres.
announce restringe o envio aos administradores; locked restringe a edição dos dados do grupo.
{
"groupId": "120363000000000000@g.us",
"name": "Time de vendas",
"description": "Combinados do time",
"announce": false
}
Trocar a foto do grupo
Troca a foto de um grupo ou de uma
comunidade — é a mesma chamada, passando o JID da comunidade em
groupId (a comunidade é um grupo "pai" no protocolo).
{
"groupId": "120363000000000000@g.us",
"image": "https://exemplo.com/capa.jpg"
}
image aceita URL http(s) pública ou data URI
base64, igual ao send-image. A imagem é
normalizada para JPEG quadrado antes de subir: o WhatsApp recusa o que não for.
Existe como rota própria, e não só como campo do
group-settings, por paridade de nome com a
Z-API. Trocar a foto de um número é outra coisa: use
PUT profile-picture. Resposta: {"value": true}.
Link de convite
reset invalida o link anterior — quem tinha o antigo deixa de conseguir entrar.
{
"groupId": "120363000000000000@g.us"
}
{
"inviteLink": "https://chat.whatsapp.com/XXXXXXXX"
}
Espiar grupo pelo link
Mostra nome, descrição e número de participantes sem entrar no grupo.
{
"inviteLink": "https://chat.whatsapp.com/XXXXXXXX"
}
Entrar em grupo
{
"inviteLink": "https://chat.whatsapp.com/XXXXXXXX"
}
Sair do grupo
{
"groupId": "120363000000000000@g.us"
}
Modo de entrada do grupo
requireApproval põe quem entra pelo link numa fila de aprovação. adminsOnlyAdd restringe quem pode adicionar.
{
"groupId": "120363000000000000@g.us",
"requireApproval": true
}
Aprovar ou recusar entrada
action aceita approve ou reject.
{
"groupId": "120363000000000000@g.us",
"phones": [
"5511888888888"
],
"action": "approve"
}
Vincular grupo à comunidade
Para desvincular, use unlink-group com o mesmo corpo.
{
"communityId": "120363000000000000@g.us",
"groupId": "120363111111111111@g.us"
}
Listar canais
{
"newsletters": [
{
"newsletterId": "120363000000000000@newsletter",
"name": "Avisos",
"subscribers": 128
}
]
}
Criar canal
{
"name": "Avisos da loja",
"description": "Promoções e horários"
}
Seguir canal
value ausente alterna entre seguir e deixar de seguir.
mute-newsletter tem o mesmo corpo e silencia o canal.
{
"newsletterId": "120363000000000000@newsletter",
"value": true
}
Listar etiquetas
Só existe em contas WhatsApp Business.
A lista é montada a partir dos eventos que o WhatsApp manda ao sincronizar — o protocolo não tem uma consulta que devolva tudo de uma vez.
Para cor em hex, conversas por etiqueta e sincronização completa, veja labels/full e labels/sync.
{
"labels": [
{
"labelId": "1",
"name": "Novo cliente",
"color": 3
}
]
}
Criar ou renomear etiqueta
color é o índice da paleta do WhatsApp, de 0 a 19 — não é uma cor livre.
Sem labelId, cria uma nova.
{
"labelId": "1",
"name": "Cliente fiel",
"color": 5
}
Recusar chamada
O callId chega pelo webhook quando a chamada entra.
{
"phone": "5511999999999",
"callId": "ABC123"
}
Enviar álbum
Envia várias fotos e vídeos agrupados num álbum (mosaico), como o celular faz.
De 2 a 30 itens; cada item leva image ou video (URL ou base64). A legenda exibida no mosaico é a do primeiro item.
Todas as mídias sobem antes do envio: se uma falhar, nada é enviado. A resposta traz o ID da mensagem do álbum. Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"phone": "5511999999999",
"album": [
{
"image": "https://exemplo.com/foto1.jpg",
"caption": "Coleção nova"
},
{
"video": "https://exemplo.com/video.mp4"
},
{
"image": "https://exemplo.com/foto2.jpg"
}
]
}
Enviar vídeo redondo
Envia vídeo redondo (a "bolinha" de vídeo gravado na hora).
O aparelho recorta em círculo: prefira vídeo quadrado (ex.: 480x480). Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"phone": "5511999999999",
"video": "https://exemplo.com/video-quadrado.mp4"
}
Enviar vários contatos
Envia vários contatos numa mensagem só.
Mínimo de 2; para um contato, use send-contact. name e phone são obrigatórios em cada item. Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"phone": "5511999999999",
"contacts": [
{
"name": "Maria Silva",
"phone": "5511988888888"
},
{
"name": "João Souza",
"phone": "5511977777777"
}
]
}
Enviar localização em tempo real
Envia um cartão de localização em tempo real.
É um ponto só: aparece como localização em tempo real, mas não há atualização contínua. Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"phone": "5511999999999",
"latitude": -23.5505,
"longitude": -46.6333,
"caption": "Estou aqui"
}
Enviar convite de grupo
Envia o cartão de convite de grupo, com o botão "Entrar no grupo".
Usa o código do link de convite do grupo: a instância precisa ser admin. O convite expira em 3 dias. Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"phone": "5511999999999",
"groupId": "120363000000000000@g.us",
"message": "Entre no nosso grupo de clientes"
}
Pedir número do contato
Pede ao contato que compartilhe o próprio número. Útil para contatos que chegam só com LID.
O que chega no webhook quando o contato compartilha ainda não foi conferido. Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"phone": "5511999999999"
}
Criar link de ligação
Cria um link de ligação do WhatsApp (call.whatsapp.com). video escolhe vídeo ou áudio.
Sem phone, só cria e devolve o link. Com phone, também envia o link na conversa como texto com prévia (igual ao aparelho), com message como texto, e a resposta traz o messageId.
startTime (segundos Unix) cria o link de uma chamada agendada; use-o com send-event (scheduleCall + joinLink).
{
"video": true,
"phone": "5511999999999",
"message": "Link da nossa reunião"
}
{
"link": "https://call.whatsapp.com/video/AbCdEfGh123",
"messageId": "3EB0C9A1F2D4"
}
Fixar mensagem
Fixa (ou desafixa) uma mensagem no topo da conversa.
duration: 24h, 7d (padrão, como no aparelho) ou 30d. fromMe diz se a mensagem é sua. unpin: true desafixa (desafixar ainda não foi conferido em aparelho). Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"phone": "5511999999999",
"messageId": "3EB0C9A1F2D4",
"fromMe": true,
"duration": "7d"
}
Manter mensagem temporária
Numa conversa com mensagens temporárias, marca a mensagem para não sumir (aparece com o ícone de mantida).
undo: true desfaz. Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"phone": "5511999999999",
"messageId": "3EB0C9A1F2D4",
"fromMe": false
}
Encaminhar pelo ID
Reenvia uma mensagem já enviada ou recebida, pelo ID, com a marca "Encaminhada".
phone é o destino. messagePhone (conversa de origem) é aceito por compatibilidade, mas o ID basta.
Mídia não é baixada de novo: a cópia aponta para o mesmo arquivo nos servidores do WhatsApp, como o próprio app faz. Resposta: a padrão de todo send-* (zaapId, messageId, id).
MESSAGE_CONTENT_TTL_HOURS ligado no servidor. Com a guarda desligada (padrão, 0), a rota responde erro. Ligada, só dá para encaminhar mensagens enviadas ou recebidas dentro desse prazo (contado a partir de quando a guarda foi ligada).
{
"phone": "5511988888888",
"messageId": "3EB0C9A1F2D4"
}
Marcar áudio como ouvido
Marca um áudio recebido como ouvido (microfone azul para quem mandou), sem ninguém tocar.
participant: autor do áudio, quando é de grupo.
{
"phone": "5511999999999",
"messageId": "3EB0C9A1F2D4"
}
{
"value": true
}
Enviar evento
Envia um convite de evento com confirmação de presença (vou / não vou / talvez).
Horários em segundos Unix. name e startTime são obrigatórios; endTime, description, location e extraGuestsAllowed são opcionais.
scheduleCall: true transforma o evento em chamada agendada; com joinLink (gerado em call-link com startTime), aparece o botão "Entrar na ligação".
As confirmações chegam no webhook EventResponseCallback. Resposta: a padrão de todo send-* (zaapId, messageId, id).
reminderMinutes (aviso X minutos antes) é aceito pelo servidor, mas a exibição no aparelho ainda não foi conferida.
{
"phone": "5511999999999",
"name": "Reunião de alinhamento",
"description": "Pauta: metas do mês",
"startTime": 1791205200,
"endTime": 1791208800,
"location": {
"latitude": -23.5505,
"longitude": -46.6333,
"name": "Escritório"
},
"extraGuestsAllowed": false,
"reminderMinutes": 30
}
Editar ou cancelar evento
Edita ou cancela um evento enviado por esta instância. canceled: true cancela.
event.name e event.dateTime são obrigatórios; a edição substitui os dados do evento. dateTime no formato 2026-10-05T16:00:00, no horário de Brasília. Em location, só o name é usado.
Só funciona com eventos enviados por esta instância (é preciso o segredo do evento). Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"phone": "5511999999999",
"eventMessageId": "3EB0C9A1F2D4",
"event": {
"name": "Reunião de alinhamento",
"dateTime": "2026-10-05T16:00:00",
"description": "Novo horário",
"location": {
"name": "Sala 2"
},
"canceled": false
}
}
Responder a um evento
Responde a um evento criado por outra pessoa. response: GOING, NOT_GOING ou MAYBE.
Em grupo, informe participant (quem criou o evento). O evento precisa ter chegado nesta instância. Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"phone": "5511999999999",
"eventMessageId": "3EB0C9A1F2D4",
"response": "GOING",
"extraGuests": 1
}
Votar em enquete
Vota numa enquete da conversa. pollVote[].name é o texto exato da opção; mais de um item vale para enquete de múltipla escolha.
pollSender: autor da enquete, em grupo (opcional). Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"phone": "5511999999999",
"pollMessageId": "3EB0C9A1F2D4",
"pollVote": [
{
"name": "Tarde"
}
]
}
Marcar como não lida
Marca a conversa como não lida (a bolinha verde no celular). value: false desfaz, marcando como lida.
Funciona com a conversa fechada no celular; se ela estiver aberta, o celular marca como lida na hora.
{
"phone": "5511999999999",
"value": true
}
{
"value": true
}
Mensagens temporárias da conversa
Liga ou desliga mensagens temporárias numa conversa ou grupo.
seconds: 0 (desliga), 86400 (24 horas), 604800 (7 dias) ou 7776000 (90 dias).
{
"phone": "5511999999999",
"seconds": 604800
}
{
"value": true
}
Limpar conversa
Apaga as mensagens da conversa sem apagar a conversa. keepStarred: true preserva as favoritadas.
{
"phone": "5511999999999",
"keepStarred": true
}
{
"value": true
}
Apagar só para mim
Apaga uma mensagem só deste número (e dos aparelhos vinculados); o contato continua vendo.
Em grupo, mensagem de outra pessoa exige participant (o autor). A API usa o LID da conversa quando o conhece: indexada pelo número, o aparelho ignorava o pedido.
{
"phone": "5511999999999",
"messageId": "3EB0C9A1F2D4",
"fromMe": true
}
{
"value": true
}
Trancar conversa
Tranca a conversa: no celular ela vai para "Conversas trancadas".
value: false é aceito e ignorado.
{
"phone": "5511999999999",
"value": true
}
{
"value": true
}
Salvar contato
Grava ou renomeia o contato na agenda sincronizada do número.
firstName padrão: a primeira palavra de name. saveOnPhone: true também grava na agenda do celular.
{
"phone": "5511999999999",
"name": "Maria Silva",
"saveOnPhone": true
}
{
"value": true
}
Acompanhar presença
Passa a receber online, digitando e visto por último do contato, no webhook PresenceChatCallback.
O WhatsApp só manda presença enquanto a conta está disponível (online), e o visto por último respeita a privacidade do contato. Nos testes chegaram UNAVAILABLE, COMPOSING e PAUSED; RECORDING não chegou.
{
"phone": "5511999999999"
}
{
"value": true
}
Privacidade avançada da conversa
Liga ou desliga a privacidade avançada da conversa. phone aceita número ou grupo.
Em conversa individual a resposta é a padrão de envio; em grupo, {"value": true}.
{
"phone": "5511999999999",
"value": true
}
Nota da conversa
Cria, edita ou apaga a nota da conversa (app Business); aparece no perfil do contato.
Sem id, cria; com id, edita; delete: true apaga (exige id).
{
"phone": "5511999999999",
"text": "Prefere contato à tarde"
}
{
"id": "b1a2c3d4-0000-0000-0000-000000000000"
}
Resposta rápida
Cria, edita ou apaga uma resposta rápida do app Business. shortcut é o atalho (sem a barra) e message o texto.
Sem id, cria; com id, edita; delete: true apaga (exige id).
id numérico (o app usa 1, 2, 3…). Sem id, a API gera um ID hexadecimal que o celular ignorou. Informe um id numérico livre.
{
"id": "12",
"shortcut": "horario",
"message": "Atendemos de segunda a sexta, das 8h às 18h.",
"keywords": [
"horário",
"funcionamento"
]
}
{
"id": "12"
}
Histórico sob demanda
Pede ao celular as mensagens anteriores a uma mensagem conhecida e devolve no formato do webhook de mensagem recebida.
Use phone ou groupId. messageId, timestamp (segundos) e fromMe são da mensagem mais antiga que você conhece; count até 50. O celular precisa estar online (espera até 45 s).
{
"phone": "5511999999999",
"messageId": "3EB0C9A1F2D4",
"timestamp": 1790000000,
"fromMe": false,
"count": 50
}
{
"messages": [
{
/* ReceivedCallback, um por mensagem */
}
]
}
Número e LID
Converte número em LID e LID em número (envie o LID em phone para o sentido inverso).
{
"phone": "5511999999999"
}
{
"phone": "5511999999999",
"lid": "123456789012345@lid"
}
Achar contato por @username
Acha o contato dono de um @username do WhatsApp (3 a 35 caracteres). Username protegido exige o pin de 4 dígitos; sem ele, a resposta vem com pinRequired: true.
{
"username": "lojaexemplo"
}
{
"phone": "5511999999999",
"lid": "123456789012345@lid",
"username": "lojaexemplo"
}
Limites da conta
Consulta a cota de conversas novas do ciclo e se há bloqueio temporário para iniciar conversas.
total_quota: -1 = sem limite. Se uma das consultas falhar, o erro vem em cappingError ou timelockError.
{
"newChats": {
"total_quota": -1,
"used_quota": 0,
"cycle_start_timestamp": "1790000000",
"cycle_end_timestamp": "1792592000",
"capping_status": "NONE",
"ote_status": "...",
"mv_status": "..."
},
"timelock": {
"is_active": false,
"time_enforcement_ends": "",
"enforcement_type": ""
}
}
Aparelhos vinculados
Lista os aparelhos da conta. device: 0 é o celular principal; thisInstance marca o aparelho desta instância.
[
{
"jid": "5511999999999@s.whatsapp.net",
"device": 0,
"primary": true,
"thisInstance": false
},
{
"jid": "5511999999999:12@s.whatsapp.net",
"device": 12,
"primary": false,
"thisInstance": true
}
]
Listar etiquetas completas
Lista as etiquetas com cor em hex, ordem, tipo, se está ativa e quantas conversas têm cada uma.
Sincroniza com o celular nos dois sentidos: o que é feito no app chega aqui em cerca de 1 s. Etiquetas padrão do app trazem predefinedId e predefined.
[
{
"labelId": "11",
"name": "Cliente fiel",
"color": 5,
"hexColor": "#55CCB3",
"orderIndex": 10,
"type": "CUSTOM",
"active": true,
"chats": 3
}
]
Criar etiqueta
Cria uma etiqueta como o app cria: próximo ID livre (as próprias começam em 11; 1 a 10 são reservados às padrão), tipo CUSTOM, ativa e no fim da lista.
color: índice da paleta, de 0 a 19 (veja labels/colors). name até 100 caracteres.
Com predefined, ativa uma sugestão padrão do app com o nome e a cor dela (idempotente): NEW_CUSTOMER, NEW_ORDER, PENDING_PAYMENT, PAID, ORDER_COMPLETE, IMPORTANT, FOLLOW_UP, LEAD.
{
"name": "Cliente fiel",
"color": 5
}
{
"labelId": "11"
}
Conversas com a etiqueta
Lista as conversas que têm a etiqueta.
[
{
"phone": "5511999999999",
"chatJid": "123456789012345@lid",
"labelId": "11"
}
]
Etiquetas da conversa
Lista as etiquetas aplicadas a uma conversa, no mesmo formato de labels/full.
[
{
"labelId": "11",
"name": "Cliente fiel",
"color": 5,
"hexColor": "#55CCB3",
"orderIndex": 10,
"type": "CUSTOM",
"active": true,
"chats": 3
}
]
Sincronizar etiquetas
Puxa do celular todas as etiquetas e associações (sincronização completa) e devolve a lista no formato de labels/full. Sem corpo.
[
{
"labelId": "11",
"name": "Cliente fiel",
"color": 5,
"hexColor": "#55CCB3",
"orderIndex": 10,
"type": "CUSTOM",
"active": true,
"chats": 3
}
]
Etiqueta em mensagem
Aplica ou remove etiqueta de uma mensagem. value: false remove; fromMe diz se a mensagem é sua.
{
"phone": "5511999999999",
"messageId": "3EB0C9A1F2D4",
"fromMe": false,
"labelId": "11",
"value": true
}
{
"value": true
}
Paleta de cores
Devolve a paleta de 20 cores do WhatsApp, do índice para o hex. É a mesma resposta de business/tags/colors.
{
"0": "#FF9485",
"1": "#64C4FF",
"2": "#FFD429",
/* ... até "19" */
}
Sugestões de etiqueta
Lista as etiquetas padrão do app Business ainda não ativadas (as "Sugestões"). Ative com POST labels e predefined.
[
{
"labelId": "1",
"name": "Novo cliente",
"color": 1,
"hexColor": "#64C4FF",
"orderIndex": 0,
"active": false,
"chats": 0,
"predefinedId": 1,
"predefined": "NEW_CUSTOMER"
}
]
Listar tags
Lista as etiquetas no formato tags (id, nome e índice de cor).
[
{
"id": "11",
"name": "Cliente fiel",
"color": 5
}
]
Criar tag
Cria uma etiqueta. Mesmo corpo e regras de POST labels (aceita predefined).
{
"name": "Cliente fiel",
"color": 5
}
{
"id": "11"
}
Editar tag
Renomeia ou troca a cor de uma etiqueta. Sem color, mantém a cor atual.
{
"name": "Cliente VIP",
"color": 9
}
{
"success": true
}
Apagar tag
Apaga uma etiqueta.
{
"success": true
}
Cores das tags
Paleta de cores, do índice para o hex. Igual a labels/colors.
{
"0": "#FF9485",
"1": "#64C4FF",
/* ... até "19" */
}
Aplicar tag na conversa
Aplica a etiqueta {tag} (o ID) à conversa. Sem corpo. A mudança chega ao celular pela sincronização.
{
"value": true
}
Remover tag da conversa
Remove a etiqueta {tag} da conversa. Sem corpo.
{
"value": true
}
Ler catálogo
Lê o catálogo de uma empresa: sem phone, o próprio. limit de 1 a 100 (padrão 50); para a página seguinte, envie em after o cursor paging.cursors.after.
Devolve a resposta da GraphQL da Meta como veio. Preços em milésimos (R$ 1,00 = "1000").
{
"limit": 50
}
{
"data": {
"xfb_whatsapp_catalog": {
"product_catalog": {
"products": [
{
"id": "1234567890123456",
"retailer_id": "SKU-1",
"name": "Mesa",
"description": "Mesa de madeira",
"price": "990000",
"currency": "BRL",
"sale_price": {
"price": "500000"
},
"media": {
"images": [
{
"original_image_url": "https://..."
}
]
}
}
],
"paging": {
"cursors": {
"after": "..."
}
}
}
}
}
}
Enviar produto
Envia um produto do próprio catálogo como cartão (foto, preço, promoção e botão Ver). productId é o id do produto em catalog. Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"phone": "5511999999999",
"productId": "1234567890123456",
"message": "Olha essa",
"footer": "Loja Exemplo"
}
Criar produto
Cria um produto no catálogo. name e ao menos uma imagem são obrigatórios.
Preços em milésimos, como texto (R$ 990,00 = "990000"). images: até 10, URLs ou base64 (JPEG ou PNG, até 16 MB cada). hidden: true cria oculto.
Devolve a resposta da GraphQL da Meta como veio.
{
"name": "Mesa",
"description": "Mesa de madeira",
"price": "990000",
"salePrice": "500000",
"currency": "BRL",
"retailerId": "SKU-1",
"hidden": false,
"images": [
"https://exemplo.com/mesa.jpg"
]
}
Editar produto
Edita um produto (productId obrigatório). Mesmos campos de criar.
Devolve a resposta da GraphQL da Meta como veio.
salePrice) some. Fotos já hospedadas pelo WhatsApp podem ser reenviadas pela URL do CDN, sem novo upload.
{
"productId": "1234567890123456",
"name": "Mesa",
"description": "Mesa de madeira",
"price": "1050000",
"salePrice": "500000",
"currency": "BRL",
"retailerId": "SKU-1",
"hidden": false,
"images": [
"https://exemplo.com/mesa.jpg"
]
}
Apagar produtos
Apaga produtos pelo ID. Devolve a resposta da GraphQL da Meta como veio (com a quantidade apagada).
{
"productIds": [
"1234567890123456"
]
}
Ligar carrinho
Liga ou desliga o carrinho do catálogo. Com ele ligado, o cliente monta o pedido e ele chega no webhook order.
Devolve a resposta da GraphQL da Meta como veio.
{
"enabled": true
}
Itens do pedido
Busca os itens de um pedido recebido: produtos, quantidade, preço, subtotal, total e foto.
orderId e token vêm do webhook order.
{
"orderId": "1234567890123456",
"token": "AR4b...token-do-webhook"
}
Enviar cobrança
Envia uma cobrança ("Pedido") do app Business: o cliente revisa e confirma.
Valores em centavos (R$ 500,00 = 50000), diferente do catálogo. name e amount são obrigatórios em cada item; quantity padrão 1; currency padrão BRL; image vira o cabeçalho.
orderRequestId liga a cobrança ao pedido que o cliente mandou do carrinho. Informe seu próprio referenceId para atualizar depois com charge-status: sem ele, a API gera um que não volta na resposta (só no webhook de confirmação).
A confirmação do cliente chega no webhook paymentConfirmation. Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"phone": "5511999999999",
"message": "Seu pedido",
"image": "https://exemplo.com/mesa.jpg",
"currency": "BRL",
"orderRequestId": "1234567890123456",
"referenceId": "PED-1001",
"items": [
{
"productId": "1234567890123456",
"name": "Mesa",
"amount": 50000,
"quantity": 1
}
]
}
Atualizar cobrança
Atualiza uma cobrança, como o "Atualizar status" do app. referenceId é obrigatório.
status: pending, payment_requested, preparing_to_ship, partially_shipped, shipped, delivered, completed ou canceled; message vai como recado. paid (opcional) marca pago ou não pago.
Resposta: a padrão de envio, da última mensagem enviada.
{
"phone": "5511999999999",
"referenceId": "PED-1001",
"status": "shipped",
"message": "Saiu para entrega",
"paid": true
}
Enviar formulário
Envia o formulário do app Business (modelo da Meta). title é obrigatório.
Os campos booleanos escolhem o que pedir: fullName, email, phoneNumber, deliveryAddress, cpfOrCnpj, document. customFields acrescenta campos de texto (label obrigatório).
As respostas chegam no webhook formResponse. Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"phone": "5511999999999",
"message": "Preencha para receber a proposta",
"title": "Proposta",
"description": "Leva 1 minuto",
"fullName": true,
"email": true,
"phoneNumber": true,
"customFields": [
{
"label": "Empresa",
"helper": "Nome fantasia"
}
]
}
Dados do cliente e etapa do lead
Grava os dados do cliente e a etapa do lead (Customer Manager do app Business).
leadStage: LEAD, INTAKE, QUALIFIED, CONVERTED ou LOST. contactType: NONE, CUSTOMER, COLLEAGUE, VENDOR ou PERSONAL. Também email, altPhones, address, birthday, acquisitionSource e lastOrder.
Exige o LID do contato (a API procura sozinha).
birthday, lastOrder e acquisitionSource não foi confirmado.
{
"phone": "5511999999999",
"leadStage": "QUALIFIED",
"contactType": "CUSTOMER",
"email": "cliente@exemplo.com"
}
{
"value": true
}
Ler perfil comercial
Devolve endereço, e-mail, descrição e sites do perfil comercial do próprio número, e a capa atual (id para apagar, url para ver).
{
"address": "Rua Exemplo, 100",
"email": "contato@exemplo.com",
"description": "Móveis sob medida",
"websites": [
"https://exemplo.com"
],
"coverPhoto": {
"id": "1234567890123456",
"url": "https://..."
}
}
Alterar perfil comercial
Altera só os campos enviados; os ausentes ficam como estão. description até 1024 bytes; no máximo 2 sites.
{
"description": "Móveis sob medida desde 2010",
"websites": [
"https://exemplo.com"
]
}
{
"value": true
}
Trocar capa
Troca a foto de capa do perfil comercial. value: URL ou base64 de uma imagem JPEG. Devolve o id da capa nova.
{
"value": "https://exemplo.com/capa.jpg"
}
{
"id": "1234567890123456"
}
Apagar capa
Apaga a capa pelo id (o de business-profile ou o devolvido ao trocar).
{
"value": true
}
Nome do perfil
Troca o nome do perfil do número.
{
"value": "Loja Exemplo"
}
{
"value": true
}
Recado do perfil
Troca o recado ("Sobre") do perfil. O servidor grava e devolve na leitura; o app Business da conta de teste não mostrava o campo.
{
"value": "Atendimento das 8h às 18h"
}
{
"value": true
}
Foto do perfil
Troca a foto do perfil. value: URL ou base64. value vazio ("") apaga a foto.
{
"value": "https://exemplo.com/logo.jpg"
}
{
"value": true
}
Ler privacidade
Lê as configurações de privacidade direto do servidor, sem cache. Valores como o WhatsApp devolve: all, contacts, contact_blacklist, none, match_last_seen.
{
"lastSeen": "all",
"online": "all",
"photoVisualization": "contacts",
"description": "all",
"groupAdd": "contacts",
"readReceipts": "all",
"callAdd": "all",
"messages": "all"
}
Privacidade: visto por último
Define quem vê o visto por último. Corpo: visualizationType (ou type) com ALL, CONTACTS, CONTACT_BLACKLIST (meus contatos, exceto…) ou NONE.
{
"visualizationType": "CONTACTS"
}
{
"success": true
}
Privacidade: online
Define quem vê quando você está online: ALL ou SAME_LAST_SEEN (igual ao visto por último).
{
"visualizationType": "SAME_LAST_SEEN"
}
{
"success": true
}
Privacidade: foto do perfil
Define quem vê a foto do perfil. Corpo: visualizationType (ou type) com ALL, CONTACTS, CONTACT_BLACKLIST (meus contatos, exceto…) ou NONE.
{
"visualizationType": "CONTACTS"
}
{
"success": true
}
Privacidade: recado
Define quem vê o recado. Corpo: visualizationType (ou type) com ALL, CONTACTS, CONTACT_BLACKLIST (meus contatos, exceto…) ou NONE.
{
"visualizationType": "ALL"
}
{
"success": true
}
Privacidade: adicionar a grupos
Define quem pode adicionar o número a grupos. Corpo: visualizationType (ou type) com ALL, CONTACTS, CONTACT_BLACKLIST (meus contatos, exceto…) ou NONE.
{
"visualizationType": "CONTACTS"
}
{
"success": true
}
Privacidade: confirmação de leitura
Liga ou desliga a confirmação de leitura. Sem corpo: o valor vai na query, ?value=enable ou ?value=disable.
{
"success": true
}
Privacidade: temporárias padrão
Define a duração padrão das mensagens temporárias para conversas novas. Sem corpo: ?value=days90, days7, hours24 ou disable. Para uma conversa só, use chat-disappearing.
{
"success": true
}
Exceções de privacidade
Lista quem está nas exceções ("meus contatos, exceto…") de uma categoria: last-seen, photo-visualization, description ou group-add.
{
"category": "photo-visualization",
"phones": [
"5511999999999"
]
}
Alterar exceções de privacidade
Acrescenta (add) ou tira (remove) contatos das exceções de uma categoria. Alterar também coloca a categoria em "meus contatos, exceto…".
{
"category": "photo-visualization",
"action": "add",
"phones": [
"5511999999999"
]
}
{
"value": true
}
Criar comunidade
Cria uma comunidade, com entrada por pedido. name é obrigatório. Devolve a comunidade no formato de groups.
{
"name": "Clientes Exemplo",
"description": "Avisos e novidades"
}
{
"groupId": "120363000000000000@g.us",
"name": "Clientes Exemplo",
"description": "Avisos e novidades",
"amAdmin": true,
"participantCount": 1,
"announce": false,
"locked": false
}
Desativar comunidade
Desativa a comunidade. Os grupos ligados continuam existindo, soltos.
{
"communityId": "120363000000000000@g.us"
}
{
"value": true
}
Transferir comunidade
Passa a posse da comunidade para outro membro (phone).
{
"communityId": "120363000000000000@g.us",
"phone": "5511999999999"
}
{
"value": true
}
Participantes da comunidade
Lista, de uma vez, os participantes de todos os grupos da comunidade.
[ "5511999999999", "5511988888888" ]
Entrar em grupo da comunidade
Entra num grupo de uma comunidade da qual o número já é membro. announcement: true indica o grupo de avisos.
{
"communityId": "120363000000000000@g.us",
"groupId": "120363111111111111@g.us"
}
{
"value": true
}
Histórico para novos membros
Define quem pode mandar o histórico recente do grupo a quem entra: todos os membros (value: true) ou só admins (false).
{
"groupId": "120363000000000000@g.us",
"value": true
}
{
"value": true
}
Etiqueta de membro
Define a etiqueta deste número no grupo (ex.: "Suporte"), exibida sob o nome. label vazio remove. Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"groupId": "120363000000000000@g.us",
"label": "Suporte"
}
Canal ao vivo
Assina por alguns minutos as atualizações ao vivo de um canal (visualizações e reações por publicação), que chegam no webhook NewsletterLiveUpdate.
A resposta diz por quanto tempo a assinatura vale; depois, assine de novo.
{
"newsletterId": "120363000000000000@newsletter"
}
{
"durationSeconds": 90
}
Marcar publicação como vista
Conta visualização nas publicações do canal, pelos serverIds.
{
"newsletterId": "120363000000000000@newsletter",
"serverIds": [
100,
101
]
}
{
"value": true
}
Reagir a publicação
Reage a uma publicação do canal. reaction vazio tira a reação.
{
"newsletterId": "120363000000000000@newsletter",
"serverId": 100,
"reaction": "👍"
}
{
"value": true
}
Excluir canal
Exclui um canal do qual a instância é dona. id termina em @newsletter.
{
"id": "120363000000000000@newsletter"
}
{
"value": true
}
Quem vê o status
Diz quem vê os status do número: contacts (todos os contatos), whitelist (só a lista) ou blacklist (todos menos a lista).
{
"type": "contacts"
}
Status com menção
Publica um status de texto mencionando até 20 contatos; cada mencionado recebe o aviso de menção.
O status vai para o público da privacidade de status do número (veja status-privacy). Resposta: a padrão de todo send-* (zaapId, messageId, id).
{
"message": "Novidades da semana!",
"mentioned": [
"5511999999999"
]
}
Apagar status
Apaga um status publicado pela instância, pelo messageId.
{
"messageId": "3EB0C9A1F2D4"
}
{
"value": true
}
Marcar status como visto
Marca o status de um contato como visto: ele passa a ver a instância na lista de quem viu. phone é o autor do status.
{
"phone": "5511999999999",
"messageId": "3EB0C9A1F2D4"
}
{
"value": true
}
Listar IAs da Meta
Lista as IAs da Meta disponíveis para a conta, com o perfil de cada uma quando o servidor devolve (nome, descrição, sugestões e comandos).
[
{
"phone": "<jid do bot>",
"personaId": "<id da persona>",
"name": "Meta AI",
"description": "...",
"isDefault": true,
"prompts": [
"..."
]
}
]
Perguntar à Meta AI
Manda a pergunta à Meta AI pelo número conectado e espera a resposta final na mesma chamada. timeoutSeconds: padrão 60, máximo 120.
Se a resposta não vier no prazo, a rota responde erro, e a resposta, se vier, chega pelo webhook como mensagem de texto. Só texto: imagem gerada não é tratada.
{
"question": "Me dê 3 dicas para alugar um carro",
"timeoutSeconds": 60
}
{
"questionMessageId": "3EB0C9A1F2D4",
"answer": "1. Compare preços..."
}
Webhook — mensagem recebida
O Zapbot faz POST na sua webhookUrl a cada mensagem recebida, com
type: "ReceivedCallback".
{
"type": "ReceivedCallback",
"instanceId": "b1a2c3d4-...",
"messageId": "3EB0C9A1F2D4",
"phone": "5511999999999",
"connectedPhone": "5511777777777",
"fromMe": false,
"momment": 1720000000000,
"senderName": "Maria",
"isGroup": false,
"text": { "message": "Oi!" }
}
{
"type": "ReceivedCallback",
"phone": "5511999999999", /* ... */
"image": {
"imageUrl": "https://api.zapbot.online/media/uuid.jpg",
"caption": "legenda",
"mimeType": "image/jpeg"
}
}
O campo que carrega o conteúdo muda conforme o tipo da mensagem:
| Tipo | Campo no payload |
|---|---|
| Texto | text.message |
| Imagem | image.imageUrl, image.caption |
| Áudio | audio.audioUrl |
| Vídeo | video.videoUrl, video.caption |
| Documento | document.documentUrl, document.fileName |
| Figurinha | sticker.stickerUrl |
| Localização | location.latitude, location.longitude |
| Contato | contact.displayName, contact.vCard |
| Reação | reaction.value, reaction.referencedMessage.messageId |
| Enquete | poll.question, poll.options[], poll.correctAnswer, poll.endTime, poll.hideVoters (detalhes) |
| Mensagem editada | isEdit: true + conteúdo novo, com o messageId da original (detalhes) |
| Formulário respondido | formResponse.fields (detalhes) |
| Pedido do carrinho | order.orderId, order.token, order.totalAmount1000 (detalhes) |
| Confirmação de cobrança | paymentConfirmation.referenceId, paymentConfirmation.method (detalhes) |
| Vinda de anúncio | externalAdReply.ctwaClid, adAttribution — junto do conteúdo (detalhes) |
| Etiqueta na conversa | notification: "CHAT_LABEL_ASSOCIATION", notificationParameters[] (detalhes) |
| Participantes de grupo | GroupParticipantCallback com action, participants[], by e viaInviteLink — precisa de notifyGroups ligado (detalhes) |
notifyGroups, notifyExtras,
notifyContactStatus). Mergear uma versão nova da API não muda o que a sua
conexão recebe.
Webhook — status de mensagem
Acompanha o ciclo de entrega das mensagens que você enviou.
Sai um aviso por mensagem, pessoa e status (antes vinha um por aparelho de quem recebe). ids traz todas as mensagens do recibo; messageId é a primeira. Em grupo, isGroup: true e participantPhone diz quem recebeu ou leu — um aviso para cada participante.
{
"type": "MessageStatusCallback",
"status": "READ", // SENT | RECEIVED | READ | PLAYED
"phone": "120363000000000000",
"messageId": "3EB0C9A1F2D4",
"ids": [
"3EB0C9A1F2D4"
],
"isGroup": true,
"participantPhone": "5511988888888"
}
Webhook — conexão
Disparado quando o número conclui o pareamento.
{
"type": "ConnectedCallback",
"phone": "5511777777777",
"connectedPhone": "5511777777777"
}
Webhook — mensagem editada
Quando o contato edita uma mensagem, chega um ReceivedCallback com isEdit: true. O messageId é o da mensagem original e o conteúdo é o novo: troque o texto da mensagem que você já tem com esse ID.
{
"type": "ReceivedCallback",
"instanceId": "b1a2c3d4-...",
"messageId": "3EB0C9A1F2D4",
"phone": "5511999999999",
"fromMe": false,
"momment": 1720000000000,
"isEdit": true,
"text": {
"message": "Texto corrigido"
}
}
Webhook — presença
Chega depois de subscribe-presence. status: AVAILABLE, UNAVAILABLE, COMPOSING, RECORDING ou PAUSED. lastSeen (ms) vem com UNAVAILABLE quando a privacidade do contato permite; senão, null.
{
"type": "PresenceChatCallback",
"phone": "5511999999999",
"status": "COMPOSING",
"lastSeen": null,
"instanceId": "b1a2c3d4-..."
}
Webhook — resposta a evento
Confirmação de presença num evento enviado por send-event. eventId é o messageId do evento; response: GOING, NOT_GOING ou MAYBE; momment em ms.
{
"type": "EventResponseCallback",
"instanceId": "b1a2c3d4-...",
"eventId": "3EB0C9A1F2D4",
"phone": "5511999999999",
"response": "GOING",
"extraGuests": 1,
"momment": 1720000000000
}
Webhook — canal ao vivo
Chega enquanto vale a assinatura de newsletter-live: visualizações e contagem de reações por publicação (serverId).
{
"type": "NewsletterLiveUpdate",
"instanceId": "b1a2c3d4-...",
"newsletterId": "120363000000000000@newsletter",
"momment": 1720000000000,
"messages": [
{
"serverId": 100,
"views": 12,
"reactions": {
"❤️": 1,
"👍": 1
}
}
]
}
Webhook — resposta da Meta AI
A Meta AI responde em pedaços, editando a mesma mensagem. Os pedaços intermediários não viram webhook: só a versão final chega, como ReceivedCallback de texto comum, com o messageId da resposta do bot. Só chegam respostas a perguntas feitas por ask-meta-ai (até 10 minutos depois da espera); conversas com a Meta AI feitas no celular não viram webhook.
Os recibos de entrega das mensagens do bot não geram MessageStatusCallback.
{
"type": "ReceivedCallback",
"messageId": "3EB0C9A1F2D4",
/* ... */
"text": {
"message": "A capital do Brasil é Brasília."
}
}
Webhook — origem do anúncio e conversões
Na mensagem que o lead manda pelo botão de um anúncio Click-to-WhatsApp (Facebook ou Instagram), o ReceivedCallback traz externalAdReply com o anúncio de origem e o ctwaClid (identificador do clique). sourceId é o ID do anúncio.
Nas mensagens seguintes da conversa iniciada pelo anúncio, vem adAttribution (conversionSource, ex.: FB_Ads; conversionData é opaco, em base64).
ctwaClid só vem na primeira mensagem vinda do anúncio: guarde na hora. E só vem com o compartilhamento de dados de anúncio ligado no app Business.
{
"type": "ReceivedCallback",
"instanceId": "b1a2c3d4-...",
"messageId": "3EB0C9A1F2D4",
"phone": "5511999999999",
"fromMe": false,
"momment": 1720000000000,
"text": {
"message": "Olá! Quero saber mais"
},
"externalAdReply": {
"title": "Promoção de outubro",
"body": "Fale com a gente",
"mediaType": "IMAGE",
"thumbnailUrl": "https://...",
"sourceType": "ad",
"sourceId": "120200000000000000",
"ctwaClid": "ARAkLm...",
"sourceUrl": "https://fb.me/...",
"containsAutoReply": false,
"renderLargerThumbnail": true,
"showAdAttribution": true
}
}
{
"adAttribution": {
"conversionSource": "FB_Ads",
"conversionData": "base64..."
}
}
Reportar conversão à Meta
Etiqueta aplicada pela API não reporta conversão à Meta. No app Business, quem marca uma conversa de anúncio com etiqueta padrão (Novo pedido, Pago…) é o próprio aparelho, que envia o evento de conversão pela telemetria interna dele; a etiqueta que chega ao celular pela sincronização não dispara esse evento.
O caminho é a Conversions API da Meta para mensagens de negócios:
| Passo | O que fazer |
|---|---|
| 1 | Guarde o externalAdReply.ctwaClid do primeiro webhook do lead. |
| 2 | Ao concluir o fluxo (venda, lead qualificado…), envie o evento para POST https://graph.facebook.com/v{versão}/{DATASET_ID}/events com action_source: "business_messaging", messaging_channel: "whatsapp", user_data.ctwa_clid (o mesmo valor) e user_data.whatsapp_business_account_id, o evento (Purchase, LeadSubmitted, QualifiedLead…) e custom_data com valor e moeda. |
| 3 | Opcional: aplique também a etiqueta padrão pela API, só para organizar no celular. |
Requisitos do lado da Meta: dataset (pixel) vinculado à conta WhatsApp Business (WABA) e token com a permissão whatsapp_business_manage_events. Sem ctwaClid (conversa orgânica), não envie. Referência: developers.facebook.com/docs/marketing-api/conversions-api/business-messaging.
Webhook — pedido do carrinho
Pedido montado pelo cliente no carrinho do catálogo. totalAmount1000 em milésimos (R$ 500,00 = 500000). Use orderId e token em order-details para ver os itens, e orderId como orderRequestId em send-charge.
{
"type": "ReceivedCallback",
"instanceId": "b1a2c3d4-...",
"messageId": "3EB0C9A1F2D4",
"phone": "5511999999999",
"fromMe": false,
"momment": 1720000000000,
"order": {
"orderId": "1234567890123456",
"token": "AR4b...",
"itemCount": 1,
"status": "INQUIRY",
"title": "Mesa",
"totalAmount1000": 500000,
"currency": "BRL",
"sellerJid": "5511777777777@s.whatsapp.net"
}
}
Webhook — confirmação de cobrança
O cliente revisou e confirmou uma cobrança de send-charge. stanzaId é a mensagem da cobrança; params traz o JSON original (itens e valores) como veio.
{
"type": "ReceivedCallback",
"instanceId": "b1a2c3d4-...",
"messageId": "3EB0C9A1F2D4",
"phone": "5511999999999",
"fromMe": false,
"momment": 1720000000000,
"paymentConfirmation": {
"referenceId": "PED-1001",
"method": "confirm",
"orderStatus": "pending",
"currency": "BRL",
"stanzaId": "3EB0C9A1F2D4",
"params": "{...}"
}
}
Webhook — formulário respondido
Respostas de um formulário de send-form. stanzaId é a mensagem do formulário; em fields, a chave é o nome do campo no formulário (ou o rótulo, quando não há nome) e o valor é o que o cliente preencheu.
{
"type": "ReceivedCallback",
"instanceId": "b1a2c3d4-...",
"messageId": "3EB0C9A1F2D4",
"phone": "5511999999999",
"fromMe": false,
"momment": 1720000000000,
"formResponse": {
"stanzaId": "3EB0C9A1F2D4",
"fields": {
"<campo>": "<valor preenchido>"
}
}
}
Webhook — etiqueta na conversa
Quando uma etiqueta é aplicada ou removida de uma conversa (no celular ou pela API), chega um ReceivedCallback com notification: "CHAT_LABEL_ASSOCIATION". label é o ID da etiqueta; assigned: false indica remoção. Sai com o notifyExtras ligado na conexão
(antes exigia notifySentByMe; a partir desta versão o interruptor é o
notifyExtras).
{
"type": "ReceivedCallback",
"notification": "CHAT_LABEL_ASSOCIATION",
"notificationParameters": [
{
"phone": "5511999999999",
"label": "11",
"assigned": true
}
],
"instanceId": "b1a2c3d4-...",
/* ... */
}
Webhook — participantes do grupo
Avisa sempre que a lista de participantes de um grupo mudar, na conexão
que tiver o notifyGroups ligado. Cada ação é uma chamada; uma entrada coletiva
(mil pessoas pelo link) é uma chamada com mil números.
{
"type": "GroupParticipantCallback",
"instanceId": "b1a2c3d4-...",
"connectedPhone": "5511777777777",
"groupId": "120363000000000000@g.us",
"groupName": "AULÃO #01",
"action": "add",
"participants": ["5511999999999"],
"by": "5511888888888",
"viaInviteLink": true,
"momment": 1720000000000
}
| Campo | Detalhe |
|---|---|
action |
add | leave | remove |
promote | demote. Um mesmo aviso do WhatsApp pode trazer
mais de uma mudança (entrou e virou admin): nesse caso sai um webhook para cada. |
participants |
Números afetados, já resolvidos de LID para telefone. |
by |
Quem fez a ação. Vazio quando a pessoa entrou sozinha pelo link ou saiu por conta própria — não houve administrador nenhum. |
viaInviteLink |
true quando a entrada foi pelo link de convite. Sai sempre
no corpo, mesmo false, para "não entrou por link" e "campo ausente" não
ficarem iguais. |
groupName |
Vem de cache de 15 minutos por grupo — o aviso do WhatsApp não traz o nome, só o JID. Se a consulta falhar, sai vazio em vez de o aviso não sair. |
leave ou remove: o WhatsApp não
manda essa distinção em campo próprio — os dois chegam pelo mesmo aviso interno.
Distinguimos pelo autor: quando quem age é a própria pessoa (ou ninguém), é
leave; quando é outro, é remove.
Se dois dos seus números estiverem no mesmo grupo, cada instância
avisa. Deduplique por groupId + participante + momment.
Webhook — enquete recebida
Além de question e options, a enquete recebida traz, quando existem: correctAnswer (quiz), endTime (encerramento, em segundos Unix) e hideVoters (votos ocultos).
{
"type": "ReceivedCallback",
"instanceId": "b1a2c3d4-...",
"messageId": "3EB0C9A1F2D4",
"phone": "5511999999999",
"fromMe": false,
"momment": 1720000000000,
"poll": {
"question": "Qual a capital do Brasil?",
"options": [
{
"name": "Brasília"
},
{
"name": "São Paulo"
}
],
"correctAnswer": "Brasília",
"endTime": 1791205200,
"hideVoters": true
}
}
Zapbot — A API de WhatsApp mais estável do mercado, construída sobre whatsmeow. Precisa de ajuda? suporte@zapbot.online