Conectando seu software ao WhatsApp
O API Management é o caminho pelo qual outro sistema — seu software de cobrança, sua loja virtual, seu próprio backend — envia mensagens de WhatsApp pelo WizMessage. Aqui você emite uma chave de API para esse sistema, define qual template aprovado do WhatsApp ele pode enviar e o aponta para um webhook, para que as respostas voltem. Este guia percorre todo o caminho e, em seguida, documenta a API REST que seus desenvolvedores vão chamar.
O painel
Onde ficam suas chaves de API
Todas as chaves que você emitiu aparecem aqui, em API Keys. Cada linha mostra o status da chave, seu prefixo público (a única parte da chave que volta a ser exibida depois que você a cria) e quantas chamadas ela fez hoje. "Manage" abre a chave; o botão de alternância a pausa sem excluí-la.


Uma chave que ainda não está pronta
Uma chave sem nenhum template mapeado exibe o selo "Needs setup". Ela consegue se autenticar, mas não consegue enviar mensagens de template enquanto você não mapear um — assim, uma integração pela metade fica visível de imediato, em vez de falhar mais tarde, na hora do envio.

Configuração guiada
Passo 1 — O que você está conectando?
"Connect your software" abre um assistente de quatro passos. Comece dando à chave o nome do sistema que vai usá-la e escolha o preset correspondente. Os presets apenas sugerem nomes de campo razoáveis — o de cobrança sugere invoice_number, amount, due_date, customer_name —, eles não restringem o que você pode mapear.


Passo 2 — Escolha a mensagem
Escolha um dos seus templates aprovados do WhatsApp. Os templates ficam na Meta, não aqui — esta lista é buscada em tempo real na sua conta do WhatsApp conectada, e somente templates APROVADOS podem ser enviados. Este passo é opcional: pule-o se a chave só enviar texto simples e mapeie um template mais tarde.

Passo 3 — Para onde vão as respostas?
Quando alguém responde à sua mensagem, ou quando chega um relatório de entrega, enviamos tudo via POST para uma URL que você controla. Deixe isso desativado se o seu sistema apenas envia. Você pode adicionar depois, na aba Webhooks da chave — nada aqui é permanente.

Passo 4 — Entre no ar
É ao pressionar Next no passo anterior que a chave é de fato criada — esta tela é a confirmação. Copie os três valores agora: a chave de API, o segredo da API e o segredo de assinatura do webhook são exibidos uma única vez e não podem ser recuperados depois. Se você perder o segredo, terá de rotacioná-lo, o que significa atualizar todo sistema que o estivesse usando.

Gerenciando uma chave
Visão geral
Ao abrir uma chave, você cai na aba Overview: o que ela tem permissão de fazer, o quanto está sendo usada e os endpoints que pode chamar. Os números de uso vêm do log de requisições, ou seja, refletem o tráfego real.


Uma chave que ainda precisa de configuração
Uma chave sem template mapeado abre na aba Setup — o mesmo assistente, embutido. Repare que as abas são diferentes das da captura anterior: enquanto a chave não tiver ao menos um template, Templates e Webhooks não são oferecidas, porque ainda não há nada sobre o que elas possam atuar.


Templates
Cada card é um template que esta chave pode enviar. O texto do corpo é a mensagem tal como foi aprovada na Meta, e os chips abaixo dele são os nomes das variáveis que o seu sistema vai fornecer. "Try it" envia um teste; "Get code" gera uma requisição pronta em oito linguagens.


Lendo as variáveis — o ponto que merece atenção redobrada
Veja o card payment_failed_alert. A mensagem dele vai de {{1}} para {{3}} e depois para {{2}} — os placeholders NÃO estão em ordem numérica no texto. Já os chips abaixo são listados em ordem numérica: customer_name preenche {{1}}, renewal_date preenche {{2}}, plan_name preenche {{3}}. Associe seus valores ao NÚMERO, nunca à ordem em que os placeholders por acaso aparecem na frase. É aí que a maioria das pessoas tropeça em templates traduzidos, em que a ordem das palavras muda os placeholders de lugar.

Os três botões de todo template
"Try it" — envie um teste sem escrever código
O botão de aviãozinho de papel abre este template pré-carregado com valores de exemplo, um por nome de variável. É a forma mais rápida de conferir se um mapeamento está certo antes que seus desenvolvedores encostem nele. Repare no botão de alternância: "Simulate Only" é o padrão e nada sai daqui — o servidor devolve o seu payload como recebeu. Vire para "Send Real Message" e uma mensagem real do WhatsApp vai para o número indicado, e é cobrada.


O que um envio simulado devolve
Pressionar Send Request no modo Simulate Only devolve a requisição como a plataforma a recebeu, com o status HTTP e o tempo de ida e volta. Leia isso como uma conferência de formato, não como um ensaio geral: confirma que o seu JSON foi interpretado e que a chave foi aceita, mas NÃO valida os nomes das suas variáveis contra o mapeamento e nunca entra em contato com a Meta. Um payload que simula sem erros ainda pode ser recusado de verdade. Para comprovar o mapeamento em si, ative Send Real Message e envie para o seu próprio telefone.

"Get code" — a requisição, já escrita para você
O botão de colchetes angulares gera uma requisição funcional para exatamente este template em oito linguagens: cURL, JavaScript, Node.js, Python, PHP, Ruby, Go e C#. Entregue-a a quem estiver escrevendo a integração. As variáveis aparecem como {{placeholders}} com os nomes do seu mapeamento, e as credenciais ficam como YOUR_API_KEY / YOUR_API_SECRET — este diálogo nunca imprime sua chave real, então o trecho pode ser colado em um chamado sem risco.

Trocando de linguagem
Clique em qualquer linguagem no topo e o trecho é reescrito para ela — a mesma chamada, os mesmos cabeçalhos, o cliente HTTP daquela linguagem. Cada um é gerado na primeira vez que você clica, então um indicador de carregamento rápido na primeira troca é normal. Copy leva o trecho como está: ele é completo, imports e tudo, e se esta chave exigir assinaturas, o passo de assinatura já vem incluído.

"Edit" — mude o que o seu sistema envia
O lápis abre o próprio mapeamento. O mock de celular no topo é a mensagem real com os seus nomes atuais substituídos, então você vê o que o cliente vai ler. Abaixo dele, cada variável detectada pode ser renomeada, reordenada, marcada como obrigatória ou receber um tipo — uma data, um valor monetário, uma imagem. Renomear uma variável a renomeia também no payload da sua API, então um sistema que já envia o nome antigo vai começar a falhar: mude os dois juntos.

Test API — a mesma ferramenta, qualquer endpoint
"Test API", no topo da aba, é o "Try it" sem a trava do template: escolha qualquer um dos três endpoints de envio, troque de template no segundo menu suspenso e edite o corpo à vontade. Use-o para conferir as chamadas cruas de /messages/text ou /messages/template, que não têm card próprio porque não estão presas a nenhum mapeamento. Repare que as variáveis deste template ainda se chamam body_1, body_2, body_3 — é assim que um template não mapeado se parece, e é exatamente isso que o "Edit" existe para resolver.

Respostas
Webhooks — recebendo as respostas de volta
Enviar é só metade de uma integração. Aponte um webhook para o seu próprio endpoint HTTPS e o WizMessage fará POST das mensagens recebidas e das atualizações de entrega para ele. O segredo de assinatura é exibido uma única vez, no momento em que é gerado; verifique-o em cada entrega para ter certeza de que a chamada veio mesmo de nós.

Quais eventos você recebe
Marque os eventos que este endpoint deve receber. Cada um chega como seu próprio POST.
| Evento | Quando dispara |
|---|---|
message.received | Um cliente enviou uma mensagem para você. |
message.reaction | Alguém adicionou ou removeu uma reação de emoji. Dispara nos dois sentidos — veja Reações. |
message.sent | Uma mensagem saiu — pela API, pelo painel web ou digitada no próprio celular da empresa. data.source diz qual. |
message.delivered | A mensagem chegou ao aparelho do destinatário. |
message.read | O destinatário abriu a mensagem. |
message.failed | O envio ou a entrega falhou; data.error traz o motivo. |
Nota:
message.receivede asmessage.reactionde entrada só são entregues quando o sistema de chatbot da conta está definido como Webhook. Com qualquer outra configuração, as mensagens recebidas vão para o bot e seu endpoint nunca é chamado — a aba Webhooks avisa quando isso se aplica. As reações que a própria empresa envia (direction: "outbound") não são afetadas por essa configuração.
Nota: As reações que a empresa envia do próprio telefone chegavam antes como
message.sentcom o corpo"[Unsupported message type]". Agora chegam comomessage.reactioncomdirection: "outbound". Marque o novo evento se você dependia daquelas.
As entregas são at-least-once. Faça a deduplicação por event junto com data.message_id, e não apenas pelo cabeçalho X-Webhook-Id, e responda 2xx rapidamente.
Referência da API REST
Tudo o que vem acima configura a chave. Isto aqui é o que seus desenvolvedores de fato chamam. A URL base é https://wizmessage.com/api/v1/external. Todo endpoint exige os cabeçalhos de autenticação abaixo e está sujeito aos limites de uso da chave.
Autenticação
Envie sua chave de API em toda requisição. Se a chave estiver com "Require signature" ativado, você também precisa assinar a requisição — e uma chave com essa opção ativada rejeita de imediato qualquer chamada sem assinatura.
| Cabeçalho | Valor |
|---|---|
X-API-Key | Sua chave de API — o valor pk_live_… completo, não apenas o prefixo exibido no painel. |
X-Timestamp | Timestamp Unix em segundos. Rejeitado se estiver a mais de 5 minutos do horário do servidor. |
X-API-Signature | HMAC-SHA256 de timestamp + "." + rawBody, com chave SHA256(api_secret), em hexadecimal minúsculo. |
Nota: As assinaturas só são exigidas quando a chave está com
require_signatureativado. Se não estiver, a chave de API sozinha já basta para enviar mensagens — e é exatamente por isso que a chave é um segredo, não um identificador. Trate-a como uma senha: ela é exibida uma única vez, e qualquer pessoa que a tenha em mãos pode enviar mensagens aos seus clientes.
const crypto = require('node:crypto')
const body = JSON.stringify({ to: '+919876543210', message: 'Hello' })
const timestamp = Math.floor(Date.now() / 1000).toString()
const secretHash = crypto.createHash('sha256').update(API_SECRET).digest('hex')
const signature = crypto.createHmac('sha256', secretHash)
.update(timestamp + '.' + body)
.digest('hex')
await fetch('https://wizmessage.com/api/v1/external/messages/text', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY,
'X-Timestamp': timestamp,
'X-API-Signature': signature,
},
body,
})
Endpoints
| Método | Caminho | Finalidade |
|---|---|---|
POST | /messages/text | Envia uma mensagem de texto simples. Só funciona dentro de uma janela de conversa de 24 horas aberta. |
POST | /messages/media | Envie um documento, imagem, vídeo ou arquivo de áudio. Só funciona dentro de uma janela de conversa aberta de 24 horas. |
POST | /messages/reaction | Reaja a uma mensagem com um emoji, ou remova uma reação que você enviou. |
POST | /messages/template | Envia um template com você mesmo fornecendo o array de components bruto da Meta. |
POST | /messages/template/send | Envia um template usando o mapeamento que você configurou no painel. É este que você deve usar. |
POST | /messages/interactive/list | Envia uma mensagem de lista interativa. |
POST | /messages/interactive/button | Envia até três botões de resposta. |
GET | /messages/:uuid | Consulta o status de entrega de uma mensagem que você enviou. |
POST | /media/upload | Faz upload de um PDF/imagem/vídeo para a Meta e devolve um media_id para usar no cabeçalho de um template. |
GET | /templates | Lista os templates disponíveis para esta chave. |
GET | /templates/:name | Busca os detalhes de um template. |
Enviando um template mapeado
Este é o payload que faz par com a aba Templates. Você envia as variáveis por NOME — os nomes exibidos como chips no card do template — e a plataforma monta os components da Meta para você. É exatamente esse o objetivo de mapear um template: seu sistema de cobrança não precisa saber nada sobre o formato de components da Meta.
POST /api/v1/external/messages/template/send
{
"to": "+919876543210",
"template_name": "payment_failed_alert",
"language": "en",
"variables": {
"customer_name": "Priya Sharma",
"renewal_date": "12 Aug 2026",
"plan_name": "Wiz Pro"
},
"reference": "invoice-2026-0817"
}
Nota: Os nomes das variáveis precisam corresponder exatamente ao mapeamento — um nome não reconhecido é ignorado, e a falta de um nome obrigatório é rejeitada com um
400. O camporeferenceé seu: ele é devolvido nos webhooks para que você possa associar um relatório de entrega a um registro no seu próprio sistema.
Limites de uso
Cada chave tem seus próprios limites, visíveis na aba Overview — 60 requisições/minuto e 1.000 mensagens/dia por padrão. As respostas incluem o saldo restante, então reduza o ritmo quando ele estiver baixo, em vez de insistir com novas tentativas até bater na parede.
Enviando arquivos com a API
Dentro de uma janela aberta de 24 horas, seu software pode enviar um arquivo diretamente — sem template e sem aprovação. Um único endpoint cobre documentos, imagens, vídeo e áudio.
O que você pode enviar
O type informado na requisição define os limites. Qualquer coisa maior, ou em formato não listado, é recusada antes de chegar ao WhatsApp.
| Tipo | Formatos | Tamanho máx. | Legenda | Nome do arquivo |
|---|---|---|---|---|
document | pdf, doc, docx, xls, xlsx, ppt, pptx, txt | 100 MB | ✓ | ✓ |
image | jpg, jpeg, png, webp | 5 MB | ✓ | — |
video | mp4, 3gp | 16 MB | ✓ | — |
audio | aac, amr, mp3, ogg, m4a | 16 MB | — | — |
POST /api/v1/external/messages/media
{
"to": "+919876543210",
"type": "document",
"link": "https://example.com/invoice-2026-0817.pdf",
"filename": "Invoice 2026-0817.pdf",
"caption": "Your invoice for August",
"reference": "invoice-2026-0817"
}
Envie uma vez, mande muitas
Se você envia o mesmo arquivo repetidamente — tabela de preços, catálogo, PDF de condições — faça o upload uma vez e guarde o id. Ele continua válido por 30 dias e evita que o WhatsApp rebaixe sua URL a cada mensagem.
POST /api/v1/external/messages/media
{
"to": "+919876543210",
"type": "image",
"media_id": "1234567890",
"caption": "This month’s catalogue"
}
Quando um envio é recusado
- Fora da janela de 24 horas. Mídia livre exige uma conversa aberta. Envie um template aprovado — veja o guia da caixa compartilhada.
- A chave não tem
send_media. Confira as permissões na aba Overview da chave. - Legenda em áudio. O WhatsApp não a exibe, então é recusada em vez de descartada silenciosamente.
- Nome de arquivo em algo que não seja documento. Só documentos exibem nome.
- Nem
media_idnemlink. Exatamente um é obrigatório.
Nota: Você pode testar tudo isso sem escrever código. Abra a chave, clique em Test API e escolha
POST /messages/mediana lista de endpoints — o corpo já vem preenchido e o "Get code" escreve a mesma requisição em oito linguagens.
Reações
Uma reação é o emoji que alguém toca sobre uma mensagem específica — 👍 numa confirmação de pedido, ❤️ numa foto. Não é uma mensagem própria: ela aponta para uma. Seu software pode ler as reações conforme acontecem, e também enviá-las.
Um evento, três origens
Uma reação pode vir do seu cliente, do proprietário tocando nela no telefone do app Business, ou do seu próprio software chamando a API. As três chegam pelo mesmo evento message.reaction, então um único handler cobre todos os casos — direction e source dizem qual você está vendo.
Como é um webhook de reação
Aqui um cliente adiciona 👍 a uma mensagem que você enviou a ele.
{
"version": 1,
"event": "message.reaction",
"timestamp": "2026-08-01T09:14:22.031Z",
"data": {
"message_id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAERgSQjkz…",
"direction": "inbound",
"from": "+919876543210",
"to": "+911122334455",
"reaction": {
"message_id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAERgUQ0Uz…",
"emoji": "👍",
"action": "added"
},
"contact": {
"name": "Priya Sharma",
"wa_id": "919876543210"
}
}
}
Há dois message_id nesse payload e eles não significam a mesma coisa. Este é o campo que merece ser lido duas vezes.
| Campo | O que é |
|---|---|
data.message_id | O id da própria reação — não o da mensagem que recebeu a reação. |
data.reaction.message_id | O wamid da mensagem que recebeu a reação. É este que você cruza com seus próprios registros. |
data.reaction.emoji | O emoji. String vazia quando a reação foi removida. |
data.reaction.action | added ou removed. Derivado do emoji, então você pode ramificar direto nele. |
data.direction | inbound — quem reagiu foi o cliente. outbound — foi a empresa. |
data.source | Só na saída: mobile (telefone do app Business) ou api (seu software). Ausente na entrada. |
data.from / data.to | Relativos ao sentido, como em message.sent: entrada é cliente → empresa, saída é empresa → cliente. |
data.reference | Só nas reações que o seu próprio software enviou, e apenas se você informou uma. |
data.contact, data.user_id, data.username | Só na entrada — quem reagiu. Ausentes em toda reação de saída. |
Nota:
data.fromnormalmente é o telefone do cliente, mas o WhatsApp nem sempre envia um. Quando não envia, você recebe o id de usuário do WhatsApp dele — então tratefromcomo um identificador, não como algo para o qual você sempre possa ligar.
Adicionar e remover uma reação
Remover uma reação é um evento próprio, com emoji vazio e action: "removed".
{
"event": "message.reaction",
"data": {
"message_id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAERgSN0U3…",
"direction": "inbound",
"reaction": {
"message_id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAERgUQ0Uz…",
"emoji": "",
"action": "removed"
}
}
}
Cada toque é uma mensagem do WhatsApp distinta, com id próprio. Reagir com 👍, mudar para ❤️ e depois remover de vez gera três eventos, e nenhum substitui os outros no seu armazenamento. Indexe por data.reaction.message_id e fique com o mais recente — essa é a reação atual da mensagem.
Enviar uma reação a partir do seu software
O mesmo endpoint adiciona e remove. Qual dos dois depende apenas de você enviar ou não um emoji.
POST /api/v1/external/messages/reaction
{
"to": "+919876543210",
"message_id": "wamid.HBgMOTE5ODc2NTQzMjEwFQIAERgUQ0Uz…",
"emoji": "👍",
"reference": "invoice-2026-0817"
}
Aqui o message_id é o id do WhatsApp — o whatsapp_message_id de uma resposta de envio, ou o data.reaction.message_id de um webhook. Nunca é um dos nossos uuid. As reações usam a permissão send_text, então qualquer chave que possa escrever ao cliente pode reagir.
Nota: Uma reação nunca produz
message.deliverednemmessage.read. O WhatsApp não emite esses eventos para uma reação — o único evento seguinte possível émessage.failed, quando a reação é rejeitada. Consultá-la comGET /messages/:uuidfunciona como em qualquer outro envio.
Quando uma reação é recusada
Algumas recusas acontecem aqui, antes que qualquer coisa chegue ao WhatsApp. Elas devolvem um erro à sua chamada de API e não produzem webhook nenhum — não há o que reportar, porque nada foi enviado.
- A chave não tem
send_text.403. As reações reaproveitam essa permissão; não existe outra para conceder. tonão está em E.164.422. O mesmo formato+…de qualquer outro endpoint.message_idestá ausente ou vazio.422. Até 255 caracteres.emojiestá ausente.422. Para remover uma reação envie"emoji": ""— envie o campo vazio, não o omita. Até 16 caracteres.
O resto acontece no WhatsApp, depois de aceitarmos a chamada. Você recebe primeiro um 200 e um webhook message.reaction, e depois um message.failed com o erro 131009 quando o WhatsApp a rejeita.
- A mensagem tem mais de 30 dias. É o limite do WhatsApp para reagir, não o nosso — não verificamos a idade.
- A mensagem não pertence a esta conversa. Um
wamidde outro chat, ou um dos nossos uuid passado por engano. - A mensagem foi apagada, ou é ela própria uma reação. Não dá para reagir a uma reação.
Nota: Se o envio falhar de imediato em vez de ser rejeitado depois, você recebe um
500e ummessage.failedcujodata.message_idé o nosso uuid, não umwamid— não há id do WhatsApp a reportar, porque o WhatsApp nunca o aceitou.
Nota: Como em qualquer outro endpoint, você pode testar sem escrever código. Abra a chave, clique em Test API e escolha
POST /messages/reaction— reaja a uma mensagem no seu próprio telefone e veja o webhook chegar.