Documentação da API
Swagger UI
Base URL

Todos os caminhos desta documentação são relativos à base URL abaixo.

https://wpp.dw.hml.2wa.com.br

Por exemplo, POST /session/{number}/send-message corresponde a https://wpp.dw.hml.2wa.com.br/session/{number}/send-message.

Autenticação

Todas as rotas exigem o header x-api-key com uma API Key ativa.

x-api-key: sua-api-key-aqui

Gerencie suas API Keys na página API Keys.

Rate Limiting

Todas as rotas possuem limite de requisições por minuto. Quando excedido, a API retorna 429 Too Many Requests.

Headers de resposta
HeaderDescrição
X-RateLimit-LimitLimite de requisições por minuto da sua conta
X-RateLimit-RemainingRequisições restantes na janela atual
X-RateLimit-ResetTimestamp (Unix) quando a janela reseta

O limite padrão é 60 req/min. Caso precise de mais, contate o administrador.

Fluxo de uso
Conectar um novo número
1
POST /session/connect — Inicia uma nova conexão e retorna um tempId
2
GET /session/pending/{tempId}/qr — Obtenha o QR code para escanear
Alternativa: POST /session/pending/{tempId}/pairing-code — Obtenha um código numérico de 8 caracteres para vincular sem QR
3
GET /session/pending/{tempId}/status — Monitore até conectar; a resposta incluirá o phone_number
4
Use o phone_number como identificador em todas as rotas operacionais
Rotas de Conexão
POST /session/connect

Inicia uma nova conexão WhatsApp. Retorna um ID temporário para acompanhar o processo.

Body (JSON, opcional)
CampoTipoDescrição
webhookUrlstringURL para receber eventos via webhook
displayNamestringNome de exibição do número
languagestringen ou pt-BR (padrão: en). Idioma do campo error nos webhooks deste número. Só pode ser definido aqui, no pareamento
proxyHoststringHost do proxy
proxyPortintegerPorta do proxy
proxyUserstringUsuário do proxy
proxyPassstringSenha do proxy
proxyTypestringhttp ou socks5 (padrão: http)
Resposta
{ "status": true, "tempId": "a1b2c3d4e5f6...", "message": "Session started, use the tempId to get QR code" }
GET /session/pending/{tempId}/qr

Retorna o QR code da sessão pendente como imagem base64.

Resposta
{ "status": true, "qr": "data:image/png;base64,iVBOR..." }

Faça polling a cada 2-3 segundos até obter o QR. Se status: false, o QR ainda não está pronto.

POST /session/pending/{tempId}/pairing-code

Alternativa ao QR code. Retorna um código numérico de 8 caracteres para vincular o dispositivo. No celular, vá em Dispositivos Vinculados > Vincular com número de telefone e insira o código.

Body (JSON)
CampoTipoObrigatórioDescrição
phoneNumberstringSimNúmero do telefone a vincular (ex: 5511999998888)
Resposta
{ "status": true, "pairingCode": "A1B2-C3D4" }

O código expira em ~60 segundos. Pode solicitar novo código na mesma sessão. Nota: após solicitar pairing code, o QR code não será mais emitido naquela sessão.

GET /session/pending/{tempId}/status

Monitora o status da conexão pendente. Quando conectado, retorna o número de telefone.

Resposta (aguardando)
{ "status": true, "connected": false, "sessionStatus": "awaiting_auth" }
Resposta (conectado)
{ "status": true, "connected": true, "sessionStatus": "connected", "phone_number": "5511999998888" }

O tempId expira após 5 minutos se não for escaneado.

Rotas Operacionais

Todas usam o número de telefone como identificador (ex: 5511999998888).

GET /session/numbers

Lista todos os números conectados da sua conta com status em tempo real.

Resposta
{ "status": true, "numbers": [ { "phone_number": "5511999998888", "display_name": "Atendimento", "status": "connected", "webhook_url": "https://meusite.com/webhook", "created_at": "2026-02-24T12:00:00.000Z" } ] }
GET /session/{number}/status

Retorna o status da sessão de um número específico.

Use o connected para decidir se pode enviar mensagem agora. O sessionStatus detalha a situação:

sessionStatusDescrição
connectedNúmero em operação, pronto para enviar e receber
connectingSessão subindo, ou religando depois de uma oscilação. O número não saiu do ar: não há o que fazer além de aguardar
disconnectedNúmero fora do ar. Precisa ser conectado de novo

A conexão com o WhatsApp Web oscila várias vezes por dia por conta do próprio servidor, quase sempre com retomada em segundos. Durante essas oscilações a resposta é connecting, e não disconnected, justamente para a sua tela não mostrar o número como caído sem ele ter caído.

Resposta
{ "status": true, "connected": true, "sessionStatus": "connected", // connected | connecting | disconnected "message": "Session is authenticated" }
POST /session/{number}/send-message

Envia uma mensagem de texto.

Body (JSON)
CampoTipoObrigatórioDescrição
tostringSimNúmero destino (ex: 5511999998888)
messagestringSimTexto da mensagem
humanModebooleanNãoSimula digitação antes de enviar: manda "digitando..." e espera um tempo proporcional ao texto
Exemplo
{ "to": "5521988887777", "message": "Ola! Tudo bem?", "humanMode": true }
Resposta
{ "status": true, "message": "Message sent", "messageId": "BAE5F2A7C4B8D123" }

O messageId pode ser usado para rastrear o status de entrega via GET /session/{number}/messages/{messageId}/status.

POST /session/{number}/send-media

Envia arquivo. Aceita dois formatos: upload (multipart/form-data) ou base64 (application/json).

Opção 1 — File upload (multipart/form-data)
CampoTipoObrigatórioDescrição
tostringSimNúmero destino
filefileSimArquivo a enviar
captionstringNãoTexto de legenda (imagens, vídeos, documentos)
sendAudioAsVoicebooleanNãoEnviar áudio como mensagem de voz
humanModebooleanNãoSimula digitação antes de enviar: manda "digitando..." e espera um tempo proporcional ao texto
Opção 2 — Base64 (application/json)
CampoTipoObrigatórioDescrição
tostringSimNúmero destino
base64stringSimConteúdo do arquivo em base64
filenamestringSimNome do arquivo (ex: foto.jpg)
mimetypestringSimTipo MIME (ex: image/jpeg)
captionstringNãoTexto de legenda (imagens, vídeos, documentos)
sendAudioAsVoicebooleanNãoEnviar áudio como mensagem de voz
humanModebooleanNãoSimula digitação antes de enviar: manda "digitando..." e espera um tempo proporcional ao texto
Exemplo base64
{ "to": "5521988887777", "base64": "iVBORw0KGgo...", "filename": "foto.jpg", "mimetype": "image/jpeg", "caption": "Confira esta imagem!", "humanMode": true }
Resposta
{ "status": true, "message": "File sent", "messageId": "BAE5F2A7C4B8D456" }
Formatos de áudio

O WhatsApp entrega áudio apenas nestes formatos. Enviar fora da lista faz a mensagem ser descartada pelo WhatsApp sem aviso, por isso ela é recusada aqui.

Formato do arquivoSituaçãoObservação
OGG/OpusEntregueO ideal para mensagem de voz (sendAudioAsVoice)
MP3Entregue
MP4 / M4A / AACEntregue
AMREntregue
WebMRecusadoRetorna 400 com UNSUPPORTED_AUDIO_FORMAT. Converta para um dos formatos acima
WAVRecusado
FLACRecusado

Não é preciso acertar o texto do mimetype: ele é conferido contra o conteúdo do arquivo e ajustado no envio. Mandar audio/ogg com um arquivo OGG/Opus tem o mesmo efeito de mandar audio/ogg; codecs=opus. A mesma regra vale para schedule-media, que recusa já no agendamento.

GET /session/{number}/messages/{messageId}/status

Consulta o status de entrega de uma mensagem enviada, usando o messageId retornado por send-message ou send-media.

Estados possíveis

O status evolui conforme o WhatsApp confirma a entrega. Cada mudança também dispara o webhook message_status_updated.

StatusSignificado
sentMensagem enviada pelo gateway
serverRecebida pelo servidor do WhatsApp
deliveredEntregue no aparelho do destinatário
readLida pelo destinatário
failedFalha no envio
Resposta
{ "status": true, "messageStatus": { "message_id": "BAE5F2A7C4B8D123", "to_number": "5521988887777", "type": "text", "status": "delivered", "created_at": "2026-06-24T12:00:00.000Z", "updated_at": "2026-06-24T12:00:03.000Z" } }

Retorna 404 se o messageId não for encontrado para esse número.

GET /session/{number}/contacts

Lista os contatos individuais do WhatsApp conectado, com o número e o nome de cada um.

O name sempre vem preenchido e cai para o próprio número quando o WhatsApp não informou nome. Use o hasName para saber se o nome é de verdade, e não exibir o número como se fosse nome.

Contatos cujo telefone o WhatsApp ainda não informou ficam de fora da lista, porque o identificador interno não serve para exibir nem para enviar mensagem.

Importando contatos logo depois de conectar

A agenda não chega junto com a conexão. Ela vem numa sincronização própria, que começa depois do pareamento e leva de segundos a alguns minutos. Quem consulta nesse intervalo recebe poucos contatos, ou nenhum, e a resposta continua sendo 200.

O campo syncing existe para essa situação. Quando vem true, a lista ainda está sendo preenchida: trate como "ainda não terminou", e não como "este número não tem contatos". Uma consulta que encontra a lista vazia também pede a agenda ao WhatsApp antes de responder, então a consulta seguinte tende a já vir completa.

Para importar com segurança, repita a consulta até o syncing vir false, com alguns segundos entre as tentativas. Consultas em sequência rápida não aceleram nada, porque há uma espera mínima entre dois pedidos de agenda do mesmo número.

Resposta
{ "status": true, "syncing": false, // true = a agenda ainda está sendo carregada "contacts": [ { "id": "5521988887777@s.whatsapp.net", "name": "João", "number": "5521988887777", "hasName": true }, { "id": "5511988887777@s.whatsapp.net", "name": "5511988887777", "number": "5511988887777", "hasName": false } ] }
GET /session/{number}/contacts/{contactId}

Retorna detalhes de um contato específico, incluindo foto de perfil. O contactId é o JID do contato (ex.: 5521988887777@s.whatsapp.net).

Resposta
{ "status": true, "contact": { "id": "5521988887777@s.whatsapp.net", "name": "João", "number": "5521988887777", "profilePicUrl": "https://pps.whatsapp.net/..." } }

profilePicUrl é null quando o contato não tem foto ou não permite a visualização.

GET /session/{number}/check-numbers

Verifica quais números têm WhatsApp, antes de tentar enviar. Aceita um número ou até 50 separados por vírgula, numa consulta só.

Parâmetros de query
CampoTipoObrigatórioDescrição
numbersstringSimUm número, ou até 50 separados por vírgula
Exemplo
GET /session/5511999998888/check-numbers?numbers=5521988887777,5531977776666
Resposta
{ "status": true, "total": 2, "registered": 1, "numbers": [ { "number": "5521988887777", "exists": true, "jid": "5521988887777@s.whatsapp.net", "registeredNumber": "5521988887777" }, { "number": "5531977776666", "exists": false, "jid": null, "registeredNumber": null } ] }

A ordem da resposta acompanha a da consulta. O registeredNumber nem sempre é igual ao que foi perguntado: no Brasil o nono dígito entra e sai, e é a forma devolvida que vale para os envios seguintes.

Número com formato inválido volta com exists: false e um errorCode próprio, sem interromper a consulta dos outros.

Esta checagem já acontece sozinha em todo envio para número, então a rota não é obrigatória: ela serve para descobrir antes, e não durante o disparo. Consultar listas grandes de números desconhecidos é um dos comportamentos que o WhatsApp associa a spam e pode custar o bloqueio do seu número, por isso o limite de 50 por consulta.

GET /session/{number}/chats

Lista as conversas em memória do WhatsApp conectado.

Resposta
{ "status": true, "chats": [ { "id": "5521988887777@s.whatsapp.net", "name": "João" } ] }
GET /session/{number}/chats/{chatId}

Retorna detalhes de uma conversa com as últimas mensagens.

Query params
ParamTipoPadrãoDescrição
limitinteger10Quantidade de mensagens
mediaBase64booleanfalseIncluir mídia como base64
Resposta
{ "status": true, "chat": { "id": "5521988887777@s.whatsapp.net", "name": "João", "messages": [ { "id": "BAE5F2A7C4B8D123", "body": "Olá!", "from": "5521988887777@s.whatsapp.net", "to": "5511999998888@s.whatsapp.net", "timestamp": 1708800000, "fromMe": false, "type": "chat", "hasMedia": false } ] } }

Com mediaBase64=true, cada mensagem que possui mídia inclui o campo mediaBase64 com o conteúdo do arquivo.

GET /session/{number}/messages

Histórico de mensagens gravado pelo servidor, com filtro por conversa e por período. Diferente de /chats/{chatId}, que lê a memória da sessão e se perde quando a conexão cai, este histórico sobrevive a quedas e reinícios.

A retenção é de 7 dias. O valor vem em retentionDays na resposta, e o que passa da janela é apagado automaticamente. Não há histórico anterior à data em que o recurso entrou no ar.

Query params
ParamTipoPadrãoDescrição
chatIdstring-Conversa a consultar. Sem ele, traz todas as conversas do número
startDatestring-Início do período em ISO 8601 (ex: 2026-08-29T00:00:00-03:00)
endDatestring-Fim do período em ISO 8601
limitinteger50Máximo de 200 por página
offsetinteger0Deslocamento para paginação
Exemplo: as mensagens de hoje
GET /session/5511999998888/messages ?startDate=2026-08-29T00:00:00-03:00
Resposta
{ "status": true, "total": 412, "limit": 50, "offset": 0, "retentionDays": 7, "messages": [ { "id": "BAE5F2A7C4B8D123", "chatId": "5521988887777@s.whatsapp.net", "phone": "5521988887777", "isGroup": false, "fromMe": false, "participant": null, "pushName": "Joao", "type": "chat", "body": "Bom dia", "hasMedia": false, "timestamp": 1788018452, "date": "2026-08-29T12:00:00.000Z" } ] }

As mensagens vêm da mais recente para a mais antiga, e o total serve para montar a paginação. O período filtra pelo horário da mensagem no WhatsApp, e o fuso enviado é respeitado.

GET /session/{number}/message-chats

Conversas que tiveram mensagem no período, com a contagem e a data da última. Serve para descobrir o que consultar sem precisar saber os ids de antemão. Aceita startDate e endDate.

Resposta
{ "status": true, "retentionDays": 7, "chats": [ { "chatId": "120363023480137063@g.us", "phone": null, "isGroup": true, "total": 412, "lastMessageAt": "2026-08-29T12:00:00.000Z" } ] }
GET /session/{number}/download-media/{messageId}

Faz download da mídia de uma mensagem específica. Funciona para mensagens recebidas dentro da janela de retenção de 7 dias, mesmo que a conexão tenha caído e voltado no meio do caminho.

Resposta
{ "status": true, "media": { "mimetype": "image/jpeg", "data": "base64...", "filename": "photo.jpg" } }
Quando a chamada faz sentido

Só mensagens com anexo têm o que baixar. O webhook message_received e a rota /messages trazem o campo hasMedia, que evita a chamada quando não há anexo. Reações, mensagens de protocolo e enquetes, por exemplo, nunca têm.

O messageId é o id da mensagem no WhatsApp, o mesmo que chega no webhook ou volta no envio. Não é o id do agendamento: o agendamento guarda o messageId da mensagem que ele gerou, e é esse valor que serve aqui.

Respostas de erro
HTTPerrorCodeQuando ocorre
400MESSAGE_HAS_NO_MEDIAA mensagem existe, mas não tem anexo
404MESSAGE_NOT_FOUNDMensagem não encontrada nesse número, ou já fora da janela de retenção
500DOWNLOAD_MEDIA_FAILEDA mensagem tem anexo, mas o WhatsApp não entregou o arquivo
DELETE /session/{number}

Desconecta e remove o número da sua conta. Faz logout do WhatsApp.

Resposta
{ "status": true, "message": "Number disconnected and removed" }
Agendamento de Mensagens

Agende disparos para um horário futuro. No horário, a mensagem é enviada automaticamente, gerando o mesmo registro de status (messages/{messageId}/status) e os mesmos webhooks de entrega/leitura de um envio imediato.

POST /session/{number}/schedule-message

Agenda uma mensagem de texto para envio futuro.

Body (JSON)
CampoTipoObrigatórioDescrição
tostringSimNúmero destino (ex: 5511999998888)
messagestringSimTexto da mensagem
scheduledAtstringSimData/hora futura em ISO 8601. O fuso segue o valor enviado: Z significa UTC; para horário de Brasília use o offset -03:00 (ex: 2026-06-26T14:08:00-03:00)
humanModebooleanNãoSimula comportamento humano antes do envio
retryIfOfflinebooleanNãoSe o número estiver offline no horário, mantém tentando dentro da janela de retentativa
retryWindowMinutesintegerNãoJanela de retentativa em minutos após o horário (padrão 30). Depois disso, marca como falho
Exemplo
{ "to": "5521988887777", "message": "Lembrete: sua consulta e amanha as 10h", "scheduledAt": "2026-06-25T13:00:00Z", "retryIfOffline": true, "retryWindowMinutes": 60 }
Resposta
{ "status": true, "message": "Message scheduled", "scheduled": { "id": 42, "type": "text", "to_number": "5521988887777", "status": "pending", "scheduled_at": "2026-06-25T13:00:00.000Z", "retry_until": "2026-06-25T14:00:00.000Z" } }
POST /session/{number}/schedule-media

Agenda o envio de uma mídia. Aceita upload (multipart/form-data) ou base64 (application/json). Tamanho máximo: 16MB.

Body (além dos campos de mídia de send-media)
CampoTipoObrigatórioDescrição
tostringSimNúmero destino
filefileSim*Arquivo (multipart) — ou use base64/filename/mimetype
base64 / filename / mimetypestringSim*Mídia em base64 (alternativa ao upload)
scheduledAtstringSimData/hora futura em ISO 8601. O fuso segue o valor enviado: Z = UTC; para Brasília use o offset -03:00
captionstringNãoLegenda
sendAudioAsVoicebooleanNãoEnviar áudio como mensagem de voz
humanMode / retryIfOffline / retryWindowMinutes-NãoMesmas opções de schedule-message
Resposta
{ "status": true, "message": "Media scheduled", "scheduled": { "id": 43, "type": "media", "to_number": "5521988887777", "media_filename": "foto.jpg", "media_mimetype": "image/jpeg", "status": "pending", "scheduled_at": "2026-06-25T13:00:00.000Z", "retry_until": null } }
GET /session/scheduled-messages

Lista os agendamentos da sua conta (paginado).

Query params
ParamTipoDescrição
statusstringFiltra por pending, processing, sent, failed ou cancelled
number_idintegerFiltra por número de origem
limit / offsetintegerPaginação (padrão 50 / 0)
Resposta
{ "status": true, "total": 3, "scheduled": [ { "id": 42, "status": "pending", ... } ] }
GET /session/scheduled-messages/{id}

Retorna um agendamento específico.

Campos do objeto
CampoDescrição
idIdentificador do agendamento
typetext ou media
to_numberNúmero destino
messageTexto (ou legenda, no caso de mídia)
scheduled_atData/hora programada do disparo
statusEstado atual (ver abaixo)
retry_if_offline / retry_untilConfiguração e prazo da retentativa
attemptsQuantas vezes o envio foi tentado
message_idID da mensagem gerada (preenchido após o envio)
error_messageMotivo da falha, quando status = failed
sent_atQuando foi efetivamente enviado
Estados do agendamento
StatusSignificado
pendingAguardando o horário do disparo
processingEm processamento pelo worker
sentEnviado com sucesso (use message_id para acompanhar a entrega)
failedFalhou definitivamente (ver error_message)
cancelledCancelado antes do disparo
GET /session/scheduled-messages/{id}/logs

Retorna o histórico de status do agendamento, em ordem cronológica. Cada transição registra o motivo, ajudando a entender por que um disparo falhou ou foi reagendado.

Campos de cada item de logs
CampoDescrição
statusEvento da transição (ver abaixo)
messageDetalhe ou motivo do evento (ex.: mensagem de erro)
attemptNúmero da tentativa de envio no momento do evento
created_atQuando o evento ocorreu
Eventos possíveis
EventoSignificado
createdAgendamento criado
processingReservado pelo worker para envio
sentEnviado com sucesso
requeuedVoltou para a fila (número offline com retentativa ativa, ou reinício do worker)
failedFalhou (ver message com o motivo)
cancelledCancelado antes do disparo
Resposta
{ "status": true, "scheduled": { /* objeto do agendamento */ }, "logs": [ { "status": "created", "message": null, "attempt": 0, "created_at": "2026-06-26T18:30:21.734Z" }, { "status": "failed", "message": "Número offline e janela de retentativa esgotada", "attempt": 1, "created_at": "2026-06-26T18:30:25.110Z" } ] }
DELETE /session/scheduled-messages/{id}

Cancela um agendamento. Apenas agendamentos com status pending podem ser cancelados.

Resposta
{ "status": true, "message": "Scheduled message cancelled" }
Logs e Uso
GET /session/logs

Histórico paginado das chamadas feitas à API pela sua conta.

Query params
ParamTipoPadrãoDescrição
limitinteger50Quantidade de registros
offsetinteger0Deslocamento para paginação
startDatestring-Data inicial (ISO 8601)
endDatestring-Data final (ISO 8601)
Resposta
{ "status": true, "logs": [ { "id": 1024, "method": "POST", "path": "/session/5511999998888/send-message", "status_code": 200, "response_time_ms": 412, "ip_address": "203.0.113.10", "created_at": "2026-06-24T12:00:00.000Z" } ] }
GET /session/usage

Resumo de uso no período: total de chamadas, endpoints mais usados e contagem de mensagens por status.

Query params
ParamTipoDescrição
startDatestringData inicial (ISO 8601)
endDatestringData final (ISO 8601)
Resposta
{ "status": true, "usage": { "api_calls": 1284, "endpoints": [ { "path": "/session/.../send-message", "method": "POST", "count": 980 } ], "messages": { "sent": 12, "delivered": 800, "read": 600, "failed": 3 } } }
GET /session/{number}/webhook-logs

Histórico das tentativas de entrega de webhook de um número, com payload, status HTTP, tentativa e erro.

Query params
ParamTipoPadrãoDescrição
limitinteger50Quantidade de registros
offsetinteger0Deslocamento para paginação
Resposta
{ "status": true, "logs": [ { "id": 88, "event": "message_received", "url": "https://meusite.com/webhook", "response_status": 200, "attempt": 1, "success": true, "error_message": null, "created_at": "2026-06-24T12:00:00.000Z" } ] }
Webhook

Quando configurado, o sistema envia eventos para a URL de webhook via POST.

Segurança — Assinatura HMAC

Toda requisição de webhook inclui o header X-Webhook-Signature com uma assinatura HMAC-SHA256. Use o webhook_secret disponível na página API Keys para validar a autenticidade.

const crypto = require('crypto'); const signature = req.headers['x-webhook-signature']; const expected = crypto.createHmac('sha256', webhookSecret) .update(JSON.stringify(req.body)).digest('hex'); const valid = signature === expected;
Retry automático

Se a entrega falhar, o sistema faz até 3 tentativas: imediata, depois 30s e 2min. Todas as tentativas são registradas e visíveis em GET /session/{number}/webhook-logs.

Evento: message_received

O WhatsApp está migrando as conversas individuais para um identificador interno (LID). Quando isso acontece, o from traz esse identificador, que não é telefone, e o número real vem em fromPhone. Para cadastrar o contato ou responder, use sempre o fromPhone. Ele vem null quando o WhatsApp ainda não informou o número correspondente.

{ "event": "message_received", "phoneNumber": "5511999998888", "message": { "id": "ABCDEF123456", "body": "Ola!", "type": "chat", "from": "5521988887777", "isGroup": false, "participant": null, "fromPhone": "5521988887777", "to": "5511999998888", "timestamp": 1708800000, "hasMedia": false, "pushName": "Joao" } }
Evento: message_status_updated

Enviado a cada confirmação de entrega de uma mensagem que você enviou. O campo status pode ter os seguintes valores:

statusSignificado
serverRecebida pelo servidor do WhatsApp
deliveredEntregue no aparelho do destinatário
readLida pelo destinatário

O estado inicial sent é registrado no momento do envio e não gera webhook. Para consultar o status atual de uma mensagem a qualquer momento, use GET /session/{number}/messages/{messageId}/status.

{ "event": "message_status_updated", "phoneNumber": "5511999998888", "messageId": "BAE5F2A7C4B8D123", "status": "delivered", "timestamp": 1708800005000 }
Evento: webhook_test

Enviado ao clicar em "Testar Webhook" no painel. Use para validar que a URL está acessível e a assinatura HMAC está correta.

{ "event": "webhook_test", "phoneNumber": "5511999998888", "timestamp": 1708800000000, "message": "This is a test event from WA Gateway" }
Evento: session_connected

Enviado sempre que o número entra em operação. É o par do session_disconnected: use os dois para saber se pode enviar mensagens naquele momento. O campo reason diferencia os casos:

reasonDescrição
pairedPrimeiro vínculo do número, logo após a leitura do QR Code ou do código de pareamento. É neste evento que você descobre qual telefone foi vinculado
reconnectedO número voltou depois de uma queda que foi avisada. Oscilações curtas, que se resolvem em segundos, não geram este evento nem o session_disconnected
{ "event": "session_connected", "phoneNumber": "5511999998888", "reason": "paired" // ou "reconnected" }
Evento: session_disconnected

Enviado quando o número sai do ar de verdade. O campo reason diferencia os casos:

reasonDescrição
logged_outA sessão foi desvinculada no celular. É permanente: o número precisa ser vinculado de novo
disconnectedQueda que persistiu. O sistema continua tentando reconectar
Quando a queda é avisada

A conexão com o WhatsApp Web não é estável por natureza: o servidor fecha e reabre o canal várias vezes por dia por conta própria, e a retomada costuma levar poucos segundos, sem perda de mensagem. Este evento não é enviado a cada uma dessas oscilações, justamente para o seu usuário final não ver o número piscando sem motivo.

Ele é enviado quando:

  • a queda passa de cerca de 35 segundos sem o número voltar;
  • as tentativas de reconexão se esgotam;
  • o WhatsApp recusa a sessão de um jeito sem volta, como número bloqueado, e aí o aviso é imediato;
  • a sessão foi desvinculada no celular, caso em que o reason vem logged_out.

Desconexão pedida por você, pelo painel ou pela API, não gera este evento. O campo code traz o código técnico do encerramento, útil para separar instabilidade de rede de recusa do servidor.

{ "event": "session_disconnected", "phoneNumber": "5511999998888", "reason": "disconnected", // ou "logged_out" "code": 428 // código técnico do encerramento (401 no logged_out) }
Evento: scheduled_message_sent

Enviado quando um disparo agendado é entregue com sucesso. O messageId permite acompanhar o status (entregue/lido) pelos eventos message_status_updated.

{ "event": "scheduled_message_sent", "phoneNumber": "5511999998888", "scheduledId": 42, "messageId": "BAE5F2A7C4B8D123", "to": "5521988887777", "type": "text" }
Evento: scheduled_message_failed

Enviado quando um disparo agendado falha definitivamente (número não existe no WhatsApp, ou offline após esgotar a janela de retentativa).

{ "event": "scheduled_message_failed", "phoneNumber": "5511999998888", "scheduledId": 42, "to": "5521988887777", "type": "text", "errorCode": "SESSION_NOT_READY", "error": "Session not ready" }

Trate a falha sempre pelo errorCode, que é estável e nunca muda de valor. O campo error é só para exibir, e o texto acompanha o idioma escolhido para o número:

errorCodeQuando ocorreerror (en)error (pt-BR)
SESSION_NOT_READYO número estava desconectado no horário do disparo e a janela de retentativa acabouSession not readyO número não está conectado ao WhatsApp
NOT_ON_WHATSAPPO destinatário não tem WhatsAppThe number 5521988887777 is not registered on WhatsAppO número 5521988887777 não tem WhatsApp
MISSING_MEDIAA mídia do agendamento não foi encontrada na hora do envioScheduled media is missingA mídia do agendamento não foi encontrada
UNSUPPORTED_AUDIO_FORMATO áudio está num formato que o WhatsApp não entregaWhatsApp does not deliver WebM audio. Convert the file to one of: OGG/Opus, MP3, MP4/AAC, AMRO WhatsApp não entrega áudio em WebM. Converta o arquivo para um destes: OGG/Opus, MP3, MP4/AAC, AMR
SEND_ERRORQualquer outra falha devolvida pelo WhatsAppTexto original do erro, sem tradução

O idioma é definido por número, no momento do pareamento, pelo campo language (en ou pt-BR). O padrão é en, então integrações existentes continuam recebendo exatamente o mesmo texto de antes.

Códigos de Erro
CódigoDescrição
400Requisição inválida (parâmetros faltando ou sessão não pronta)
403API Key inválida ou conta inativa
404Número não encontrado ou sessão pendente expirada
429Rate limit excedido — muitas requisições. Verifique os headers X-RateLimit-*
500Erro interno do servidor
Campo errorCode

Além do status HTTP, toda resposta de erro traz um errorCode estável, que nunca muda de valor. Decida pelo código e use o message apenas para exibir ao usuário, já que o texto acompanha o idioma do número.

{ "status": false, "errorCode": "NOT_ON_WHATSAPP", "message": "O número 5521988887777 não tem WhatsApp" }
errorCodeHTTPQuando ocorre
MISSING_TO400Parâmetro to não informado
INVALID_TO_DIGITS400O to tem caracteres que não são dígitos
INVALID_TO_LENGTH400O to não tem entre 7 e 15 dígitos
MISSING_MESSAGE400Parâmetro message ausente ou vazio
SESSION_NOT_READY400O número não está conectado ao WhatsApp
NOT_ON_WHATSAPP404O destinatário não tem WhatsApp
NO_FILE400Envio de mídia sem arquivo e sem base64
MEDIA_TOO_LARGE400Mídia acima do limite de 16MB
UNSUPPORTED_AUDIO_FORMAT400Áudio num formato que o WhatsApp não entrega, como WebM ou WAV
MESSAGE_NOT_FOUND404Mensagem não encontrada nesse número, ou fora da janela de retenção
MESSAGE_HAS_NO_MEDIA400Pedido de anexo numa mensagem que não tem anexo
MISSING_NUMBERS400Consulta de números sem o parâmetro numbers
TOO_MANY_NUMBERS400Mais de 50 números numa consulta só
SCHEDULED_AT_IN_PAST400O scheduledAt não é uma data futura
SCHEDULED_NOT_FOUND404Agendamento inexistente ou de outra conta
CANNOT_CANCEL400Só agendamentos pendentes podem ser cancelados
NUMBER_NOT_FOUND404Número não encontrado ou de outra conta
MISSING_API_KEY403Header x-api-key ausente
INVALID_API_KEY403API Key inválida ou desativada
RATE_LIMIT_EXCEEDED429Limite de requisições por minuto estourado
FEATURE_NOT_ENABLED403Funcionalidade não liberada para a conta
INTERNAL_ERROR500Erro interno

Os mesmos códigos aparecem no webhook scheduled_message_failed: uma falha de envio devolve NOT_ON_WHATSAPP tanto na resposta HTTP quanto no evento. Veja Webhooks.

Idioma das mensagens

O texto do message sai no idioma escolhido para o número no pareamento (campo language: en ou pt-BR). Para rotas que não têm número no path, como POST /session/connect, vale o header Accept-Language; sem ele, o padrão é en.