wapp-api — callback

Em vez de você consultar de tempos em tempos, nós avisamos: fazemos um POST numa URL sua. Peça a ativação informando a URL — precisa ser https.

Dois eventos saem pela mesma URL, distinguidos pela chave evento:

evento Quando Ativado
status mudou o estado de uma mensagem que você mandou por padrão
mensagem_recebida alguém mandou mensagem para a sua instância sob pedido

Sempre ramifique pelo evento. Podem entrar eventos novos no futuro; um receptor que assume um formato só quebra quando isso acontecer.

evento status

POST https://seu-sistema.com.br/wapp/callback
Content-Type: application/json; charset=utf-8
X-Wapp-Signature: 9f2b...c41e

{
  "evento": "status",
  "id": 42,
  "status": "entregue",
  "numero_destino": "5547999998888",
  "instancia": "principal",
  "id_mensagem_wa": "3EB0F7BA9660A3BF84D92D",
  "erro": null,
  "em": "2026-08-28T17:10:00"
}
Campo O que é
id o mesmo id que o POST /api/mensagens devolveu
status enviado, entregue, lido ou falhou
numero_destino destino já normalizado
instancia o nome que você deu à instância
id_mensagem_wa id da mensagem no WhatsApp
erro preenchido só quando status é falhou
em quando a mudança foi registrada

Uma mensagem gera vários avisos ao longo da vida — normalmente entregue e depois lido. Trate cada um como uma atualização do mesmo id, não como mensagens diferentes.

evento mensagem_recebida

Chega quando alguém manda mensagem para a sua instância. Formato dos campos e cuidados de atendimento automático em docs/api-entrada.md; assinatura, retentativa e idempotência valem igual para os dois eventos.

Confira a assinatura

O corpo vai assinado com HMAC-SHA256 usando um segredo que só você e nós temos, no header X-Wapp-Signature. Sem conferir, qualquer um que descubra a URL do seu sistema poderia forjar um "foi entregue".

Assine os bytes crus do corpo, antes de qualquer parse:

import hashlib, hmac

def valido(corpo_bytes, assinatura_recebida, segredo):
    esperada = hmac.new(segredo.encode(), corpo_bytes,
                        hashlib.sha256).hexdigest()
    # compare_digest e não ==: comparação comum vaza tempo
    return hmac.compare_digest(esperada, assinatura_recebida or '')
<?php
function valido(string $corpo, string $assinatura, string $segredo): bool {
    $esperada = hash_hmac('sha256', $corpo, $segredo);
    return hash_equals($esperada, $assinatura);
}

O que esperamos da sua resposta

Devolva qualquer 2xx. O corpo é ignorado.

Qualquer outra coisa — 4xx, 5xx, timeout, conexão recusada — conta como falha e nós tentamos de novo:

Tentativa Espera
2ª 1 minuto
3ª 5 minutos
4ª 15 minutos
5ª 1 hora

Depois da 5ª desistimos daquele aviso. Os seguintes continuam sendo tentados normalmente.

Responda rápido: o timeout é de 10 segundos. Se o seu processamento for demorado, grave e devolva 200 na hora — processe depois.

Cuidados

Seja idempotente. Uma retentativa pode entregar duas vezes o mesmo aviso, se a primeira chegou mas a resposta se perdeu. Use o par id + status como chave, e id para mensagem_recebida.

Não confie na ordem. O normal é entregue antes de lido, mas com retentativa um aviso antigo pode chegar depois. Compare com o status que você já tem e ignore o que for para trás.

lido pode nunca chegar. Depende de o destinatário abrir a conversa e de ele não ter desligado a confirmação de leitura. entregue também não vem quando a mensagem é para o próprio número conectado.