Ir para o conteúdo
22 min read

Conectando seu software ao WhatsApp

Emita uma chave de API, mapeie um template aprovado do WhatsApp, aponte um webhook para o seu próprio servidor e chame a API REST — o guia completo do API Management.

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.

Vídeo guiado — 2 passos, 0:05. Escolha um passo para reproduzir só aquela parte.
O painel API Keys listando todas as chaves emitidas com seu status, prefixo público e contagem diária de chamadas

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.

Uma linha de chave de API exibindo o selo Needs setup porque ainda não há nenhum template mapeado para ela

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.

Vídeo guiado — 4 passos, 0:19. Escolha um passo para reproduzir só aquela parte.
Passo 1 do assistente, nomeando a chave de API e escolhendo o preset correspondente ao sistema que será conectado

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 2 do assistente, escolhendo um dos templates aprovados do WhatsApp buscados em tempo real na Meta

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 3 do assistente, definindo a URL de webhook para a qual as respostas e os relatórios de entrega são enviados

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.

Passo 4 do assistente, exibindo uma única vez a chave de API, o segredo da API e o segredo de assinatura do webhook

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.

Vídeo guiado — 1 passo, 0:05.
A aba Overview de uma chave de API exibindo suas permissões, seu uso e os endpoints que ela pode chamar

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.

Vídeo guiado — 1 passo, 0:05.
Uma chave não configurada abrindo na aba Setup, sem que as abas Templates e Webhooks sejam oferecidas ainda

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.

Vídeo guiado — 2 passos, 0:07. Escolha um passo para reproduzir só aquela parte.
A aba Templates, com um card por template exibindo o texto aprovado do corpo e os chips com os nomes das variáveis

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.

Um card de template cujos placeholders aparecem fora da ordem numérica no corpo da mensagem

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.

Vídeo guiado — 7 passos, 0:21. Escolha um passo para reproduzir só aquela parte.
O diálogo Try it, pré-carregado com um valor de exemplo por variável e com o alternador Simulate Only

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.

A resposta de um envio simulado, devolvendo a requisição com seu status HTTP e seu tempo de ida e volta

"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.

O diálogo Get code exibindo uma requisição pronta para este template, com credenciais de exemplo

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.

A mesma requisição reescrita para outra linguagem depois de clicar nela na fileira de linguagens

"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.

O diálogo de edição do mapeamento, com a prévia em celular acima da lista de variáveis renomeáveis e reordenáveis

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.

O diálogo Test API com um menu de endpoint, um menu de template e um corpo livremente editável

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.

A aba Webhooks, onde um endpoint HTTPS recebe as mensagens recebidas e as atualizações de entrega

Quais eventos você recebe

Marque os eventos que este endpoint deve receber. Cada um chega como seu próprio POST.

EventoQuando dispara
message.receivedUm cliente enviou uma mensagem para você.
message.reactionAlguém adicionou ou removeu uma reação de emoji. Dispara nos dois sentidos — veja Reações.
message.sentUma mensagem saiu — pela API, pelo painel web ou digitada no próprio celular da empresa. data.source diz qual.
message.deliveredA mensagem chegou ao aparelho do destinatário.
message.readO destinatário abriu a mensagem.
message.failedO envio ou a entrega falhou; data.error traz o motivo.

Nota: message.received e as message.reaction de 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.sent com o corpo "[Unsupported message type]". Agora chegam como message.reaction com direction: "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çalhoValor
X-API-KeySua chave de API — o valor pk_live_… completo, não apenas o prefixo exibido no painel.
X-TimestampTimestamp Unix em segundos. Rejeitado se estiver a mais de 5 minutos do horário do servidor.
X-API-SignatureHMAC-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_signature ativado. 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étodoCaminhoFinalidade
POST/messages/textEnvia uma mensagem de texto simples. Só funciona dentro de uma janela de conversa de 24 horas aberta.
POST/messages/mediaEnvie um documento, imagem, vídeo ou arquivo de áudio. Só funciona dentro de uma janela de conversa aberta de 24 horas.
POST/messages/reactionReaja a uma mensagem com um emoji, ou remova uma reação que você enviou.
POST/messages/templateEnvia um template com você mesmo fornecendo o array de components bruto da Meta.
POST/messages/template/sendEnvia um template usando o mapeamento que você configurou no painel. É este que você deve usar.
POST/messages/interactive/listEnvia uma mensagem de lista interativa.
POST/messages/interactive/buttonEnvia até três botões de resposta.
GET/messages/:uuidConsulta o status de entrega de uma mensagem que você enviou.
POST/media/uploadFaz upload de um PDF/imagem/vídeo para a Meta e devolve um media_id para usar no cabeçalho de um template.
GET/templatesLista os templates disponíveis para esta chave.
GET/templates/:nameBusca 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 campo reference é 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.

As duas formas de anexar um arquivo a uma mensagem de mídiaPOST /messages/medialinkuma URL pública que passamos ao WhatsAppbaixada a cada enviomelhor para arquivos que você já hospedamedia_idenvie o arquivo uma vez e reuse o idválido por 30 diasmelhor para o mesmo arquivo repetidoO link precisa ser publicamente acessível — o WhatsApp o baixa, seu servidor não o envia.
Cada envio de mídia carrega exatamente um dos dois. Se enviar ambos, o media_id prevalece.

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.

TipoFormatosTamanho máx.LegendaNome do arquivo
documentpdf, doc, docx, xls, xlsx, ppt, pptx, txt100 MB
imagejpg, jpeg, png, webp5 MB
videomp4, 3gp16 MB
audioaac, amr, mp3, ogg, m4a16 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.

Enviar um arquivo uma vez e mandá-lo repetidamentePOST /media/uploado arquivo em simedia_idválido por 30 diasPOST /messages/mediamande quantas vezes quiserEntregueos webhooks informam o resultado
O id é reutilizável até expirar; apenas a etapa de envio se repete.
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_id nem link. 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/media na 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.

As três origens de uma reação e os campos que identificam cada umaSeu clientereagiu a vocêdirection: inboundsem campo sourcecontact diz quem foiO telefone do Businesso proprietário tocoudirection: outboundsource: mobileSeu softwarevocê chamou a APIdirection: outboundsource: apireference volta para vocêSó as reações de entrada trazem dados de contato — uma de saída é a empresa, que você já conhece.
Um evento, três origens. Leia direction primeiro, depois source.

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.

CampoO que é
data.message_idO id da própria reação — não o da mensagem que recebeu a reação.
data.reaction.message_idO wamid da mensagem que recebeu a reação. É este que você cruza com seus próprios registros.
data.reaction.emojiO emoji. String vazia quando a reação foi removida.
data.reaction.actionadded ou removed. Derivado do emoji, então você pode ramificar direto nele.
data.directioninbound — quem reagiu foi o cliente. outbound — foi a empresa.
data.sourceSó na saída: mobile (telefone do app Business) ou api (seu software). Ausente na entrada.
data.from / data.toRelativos ao sentido, como em message.sent: entrada é cliente → empresa, saída é empresa → cliente.
data.reference nas reações que o seu próprio software enviou, e apenas se você informou uma.
data.contact, data.user_id, data.usernameSó na entrada — quem reagiu. Ausentes em toda reação de saída.

Nota: data.from normalmente é 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 trate from como 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.

Como o mesmo endpoint de reação adiciona e remove uma reaçãoPOST /messages/reactionum emojio emoji que você quer exibirum por pessoa e por mensagemadiciona a reaçãoum emoji vazioapaga o que você enviou antesenvie o campo, mas vazioremove elaPara remover uma reação, envie emoji como string vazia. Omitir o campo é rejeitado.
Um endpoint, dois resultados. O campo emoji decide qual.
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.delivered nem message.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 com GET /messages/:uuid funciona 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.
  • to não está em E.164. 422. O mesmo formato +… de qualquer outro endpoint.
  • message_id está ausente ou vazio. 422. Até 255 caracteres.
  • emoji está 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 wamid de 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 500 e um message.failed cujo data.message_id é o nosso uuid, não um wamid — 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.