wapp-api — mensagens recebidas
O que chega no seu número. É o par da rota de envio, e é com isto que se monta atendimento automático do outro lado.
Duas formas, e o recomendado é usar as duas: o callback avisa na hora, a rota de leitura recupera o que se perdeu enquanto seu sistema esteve fora do ar.
GET /api/mensagens/entrada
| Parâmetro | Observação |
|---|---|
depois_de |
id a partir do qual buscar (exclusivo). É o cursor |
instancia |
filtra por instância |
numero |
filtra por remetente |
limite |
padrão 100, teto 500 |
incluir_minhas |
1 = também o que saiu pelo celular (de_mim) |
incluir_grupos |
1 = também mensagens de grupo (sem ele, grupo nunca vem) |
200 → {"total":2,"limite":100,"ultimo_id":8,"mensagens":[...]}
Cada item traz id, numero_origem, nome_contato, tipo, conteudo,
id_mensagem_wa, recebido_em, instancia e, quando há anexo,
nome_arquivo, mimetype, tamanho_bytes e midia_status. Com
incluir_minhas=1 vêm também as linhas de_mim (digitadas no celular), com
status_entrega: enviado, entregue, lido ou nulo (sem notícia ainda).
Com incluir_grupos=1, a linha de grupo vem com e_grupo = 1,
numero_origem = JID do grupo (120363...@g.us), grupo_nome (cache de
grupos; nulo até o cache conhecer), participante (LID ou telefone de quem
falou), participante_telefone (quando o LID já foi mapeado) e o nome de
quem falou em nome_contato. Responder no grupo: POST /api/mensagens com o
JID — cliente com Grupos livres (docs/api-grupos.md).
Ordem crescente de id — atendimento lê na ordem em que a pessoa falou,
ao contrário da listagem de saída, que é histórico. Guarde o ultimo_id e
mande em depois_de na chamada seguinte: não reprocessa nem pula. Lote
vazio devolve ultimo_id nulo; mantenha o cursor antigo.
GET /api/mensagens/entrada/status?ids=12,15
Linhas de_mim: entrega/leitura do destinatário — você as leu pelo cursor
antes de serem entregues, e o ✓✓ chega depois. Linhas recebidas: lido
quando alguém leu no celular do número (nulo = ainda não), para tirar a
conversa de "não lidas" no seu sistema. Até 100 ids; conta no limite por
minuto, então pergunte só pelas recentes que ainda não estão lido.
200 → {"status": {"12": "entregue", "15": "lido"}} — id que não é seu
fica de fora. 400 → lista vazia, inválida ou > 100.
POST /api/mensagens/entrada/lidas
O caminho de volta: seu sistema leu, o WhatsApp marca como lida — quem
mandou vê o ✓✓ azul e o celular do número tira a conversa de "não lidas".
Corpo {"ids": [12, 15]} (até 100). Só linhas recebidas, suas e ainda sem
leitura; o resto é ignorado. Usa a chave original do webhook (@lid,
participant no grupo).
200 → {"marcadas": 2} (e elas passam a status_entrega = lido).
400 → lista vazia, inválida ou > 100. 502 → a Evolution recusou;
nada foi gravado, pode tentar de novo.
GET /api/mensagens/entrada/<id>/arquivo
Devolve o anexo com o Content-Type e o nome originais.
| Código | Quando |
|---|---|
| 200 | o arquivo |
| 404 | id não é seu, ou a mensagem não tem anexo (midia_status diz qual) |
| 500 | o banco diz que existe e o disco não — falha nossa, registrada aqui |
Existe só enquanto a retenção guardar (padrão 90 dias). Se você precisa do arquivo depois disso, copie para o seu lado quando o aviso chegar.
Callback mensagem_recebida
Ativado sob pedido. Assinatura, retentativa e o resto do contrato estão em
docs/api-callbacks.md — aqui fica só o formato.
{
"evento": "mensagem_recebida",
"id": 2,
"instancia": "principal",
"numero": "5547999998888",
"nome": "Rosana",
"tipo": "conversation",
"texto": "qual o horário de funcionamento?",
"arquivo": null,
"id_mensagem_wa": "AC93281C275089E2A229307DB0CA70D4",
"em": "2026-08-28T17:46:56"
}
| Campo | O que é |
|---|---|
id |
id da mensagem recebida; serve de cursor na rota de leitura |
numero |
quem mandou, E.164 sem + |
nome |
nome de perfil do WhatsApp, pode vir null |
tipo |
conversation, imageMessage, audioMessage, documentMessage, stickerMessage, reactionMessage, ... |
texto |
texto digitado, transcrição local ou legenda de imagem/vídeo/documento. null no resto |
arquivo |
dados do anexo, ou null |
em |
quando chegou aqui |
Com anexo:
"arquivo": {
"nome": "foto.jpeg",
"mimetype": "image/jpeg",
"tamanho_bytes": 228729,
"url": "/api/mensagens/entrada/1/arquivo"
}
Anexos
Imagem, vídeo, áudio e documento são baixados por nós antes de avisarmos.
Quando o aviso chega, o arquivo já está aqui — você não precisa ficar
tentando. Baixe em arquivo.url, autenticando com sua chave.
Exceção: áudio recebido em conversa direta, com transcrição habilitada,
é temporário e descartado. Seu callback tem arquivo: null e segue o
contrato de texto descrito abaixo; não há arquivo para baixar — a não ser
que o cliente tenha "Guardar o arquivo do áudio transcrito" ligado
(painel, tela do cliente): aí o áudio vem como anexo comum (nome_arquivo,
mimetype, rota /arquivo), junto com o texto, inclusive se a
transcrição falhar.
arquivo nulo junto com um tipo de mídia quer dizer que o download não
deu certo depois de 3 tentativas. Não é mensagem sem anexo, é anexo que
não veio. Distinga pelo tipo, não pelo arquivo.
Figurinha também é baixada, como imagem webp.
Áudio como entrada de texto
Com transcrição local habilitada no gateway, audioMessage/pttMessage
recebidos de terceiros em conversa direta chegam como tipo: conversation,
com texto preenchido, transcrito: true e tipo_original: audioMessage
(ou pttMessage). Mesmo evento mensagem_recebida, id e assinatura HMAC.
O consumidor encaminha texto ao mesmo interpretador das mensagens digitadas.
Não é preciso implementar reconhecimento de voz no aplicativo do robô.
Na rota de leitura, a transcrição está em conteudo e o tipo também é
adaptado para conversation. No banco/painel o tipo original é preservado.
midia_status: audio_pendente, transcrita ou audio_falhou.
Enquanto há áudio pendente, a rota retorna só as linhas anteriores a ele
nos filtros consultados, sem avançar o cursor sobre uma transcrição incompleta.
Em falha, o tipo continua sendo áudio e o texto fica nulo; não interpretar isso como comando. Onde há callback de entrada ativo, o gateway enfileira uma orientação para reenviar ou digitar, respeitando opt-out e limites. O aplicativo não precisa mandar uma segunda orientação para essa falha.
Callbacks posteriores da mesma conversa esperam a transcrição e a entrega do callback do áudio (ou sua desistência). As demais conversas continuam. Retentativas ainda exigem idempotência no consumidor, como antes.
O que não chega
- mensagem de grupo — é registrada, mas fica só no painel administrativo.
Não sai por esta rota nem por callback: o campo
numeroé sempre telefone, e um JID de grupo ali quebraria quem já consome; - o eco da sua própria mensagem — o que você manda não volta como recebido.
Cuidados para atendimento automático
Filtre pelo tipo. Figurinha e reação viram aviso. Responder a um
"joinha" é o erro mais fácil de cometer aqui.
Trave o laço. Se do outro lado houver outro robô, os dois se respondem indefinidamente. Ponha um teto de respostas automáticas por número por hora.
Receber é 24 h, responder não. O aviso chega a qualquer hora, mas o
envio respeita janela de horário e teto diário — ver docs/api.md.