Como enviar uma mensagem de WhatsApp pela API
Uma chamada, um header que quase todo mundo esquece, e uma resposta que não quer dizer o que parece. Este guia cobre o caminho inteiro: da requisição até saber se a mensagem chegou de fato.
O que você precisa
- Uma chave de API (
hub_pk_...) com o escopomessages:send. Crie em /painel/chaves. - O id do canal — o número do WhatsApp já conectado. Liste os canais da organização em
GET /api/v1/channels. - Uma janela de atendimento aberta com aquele contato, ou um template aprovado. Mensagem de texto livre só sai se o cliente falou com você nas últimas 24 horas — fora disso, veja enviar template.
A requisição
curl -X POST https://crprohub.com/api/v1/channels/CHANNEL_UUID/messages \
-H "Authorization: Bearer hub_pk_EXEMPLO_NAO_REAL" \
-H "Idempotency-Key: 4a50df76-d6c5-49f3-90a4-13907579d924" \
-H "Content-Type: application/json" \
-d '{"to":"5521999999999","type":"text","text":{"body":"Olá"}}'O campo to é o número do destinatário com código do país, só dígitos — sem +, sem espaço e sem parênteses. O type aceita text, image, audio, video, document, sticker, location, contacts, reaction, interactive e template. Para texto, o objeto text é obrigatório, no formato { "body": "..." }.
{
"data": {
"message_id": "wamid.EXEMPLO123",
"status": "accepted"
}
}Por que a Idempotency-Key é obrigatória
O header Idempotency-Key não é opcional aqui, e a razão é prática: envio de mensagem é a operação que mais sofre com retentativa cega. Um timeout de rede não diz se a mensagem saiu ou não, e reenviar por via das dúvidas é como se manda a mesma cobrança duas vezes para o mesmo cliente.
Com a chave, repetir a requisição é seguro: a mesma Idempotency-Key com o mesmo corpo devolve a resposta original em vez de enviar de novo. Use um valor novo por envio distinto — um UUID serve bem.
409 IDEMPOTENCY_KEY_REUSED. Isso é proposital: significa que o seu código reaproveitou uma chave que já pertence a outro envio, e mandar a mensagem nesse caso seria esconder um defeito seu.O que 202 significa
A resposta é 202, não 200. A diferença importa: a mensagem foi aceita e enfileirada para a Meta, não entregue. O message_id que volta identifica a mensagem para acompanhar depois; o status vem como accepted.
Tratar 202 como “enviado com sucesso” na interface é o erro mais comum de quem integra WhatsApp pela primeira vez. O cliente pode ter o número bloqueado, estar fora da janela, ou a Meta pode recusar por qualidade — nada disso aparece nesta resposta.
Como saber se entregou
O estado final chega de forma assíncrona, por webhook. Configure um endpoint em configurar webhook e correlacione pelo message_id:
{
"event": "message.status",
"data": {
"message_id": "wamid.EXEMPLO123",
"status": "delivered"
}
}O mesmo histórico também está em GET /api/v1/channels/{id}/messages, com paginação por cursor — útil para reconciliar depois de uma queda, mas não substitui o webhook: consultar em laço é desperdício e esbarra no limite de taxa.
Quando falha
401— a chave está errada, revogada, ou você mandou o valor sem o prefixoBearer.403— a chave existe mas não tem o escopomessages:send.404— o canal não existe ou pertence a outra organização. A API responde 404 nos dois casos de propósito: 403 revelaria que o recurso existe.409—Idempotency-Keyreaproveitada com corpo diferente.400 INVALID_MESSAGE— o corpo é válido como JSON mas inválido como mensagem: número malformado,typesem o objeto correspondente, texto vazio.429— limite de taxa. Veja limites.
Toda resposta traz um header x-request-id. Guarde-o no seu log: é o que liga a sua chamada ao registro em /painel/logs. A tabela completa de códigos está em erros.
hub_ch_...) também autentica este envio, como alternativa à chave de API. Ele vale só para o canal que o emitiu e nunca autoriza enviar por outro canal da organização — é a credencial certa para dar a um sistema que cuida de um cliente só. O contrato completo do endpoint está em referência de mensagens.