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.