Webhook assinado: o dia em que aceitei qualquer POST
Assinatura, verificação e idempotência explicados sem enrolação, com os erros de implementação que eu cometi e que quase ninguém conta.

Durante quase um ano, meu endereço de retorno aceitava qualquer coisa que chegasse. Sem verificação, sem segredo, sem nada. O argumento era: "ninguém sabe essa URL". Um estagiário curioso descobriu em vinte minutos olhando uma requisição no navegador e mandou um evento falso só pra provar que dava. Naquele dia eu entendi o que é webhook assinado.
Ele não fez estrago. Poderia. Um evento forjado consegue criar conversa, criar contato, disparar automação. Já vi isso dar errado em outro lugar, com alguém injetando lead falso em massa e sujando a base inteira de um cliente.
A boa notícia é que a verificação já existe e é configurável. O webhook de entrada do live chat aceita verificação HMAC. O webhook de leads dos anúncios é idempotente e valida HMAC quando o segredo do aplicativo está configurado. E o hub externo assina os eventos que envia ao ERP com HMAC SHA-256.
O que um webhook assinado realmente prova
A assinatura prova duas coisas: quem mandou tem o segredo combinado, e o conteúdo não foi alterado no caminho. Só isso. Ela não prova que o evento é verdadeiro, não prova que veio no momento certo e não impede alguém de reenviar o mesmo evento cem vezes. Confundir esses limites foi meu segundo erro.
- Prova a origem: quem assinou conhece o segredo compartilhado.
- Prova a integridade: o corpo chegou exatamente como saiu.
- Não prova frescor: evento antigo reenviado continua com assinatura válida.
- Não substitui idempotência: repetição precisa ser tratada separadamente.
- Não dispensa validação de conteúdo: campo malformado ainda chega assinado.
Os cabeçalhos que chegam junto
Quando o hub externo envia um evento para o ERP, ele manda a assinatura e mais dois cabeçalhos de identificação, um dizendo qual cliente está falando e outro dizendo que tipo de evento é aquele. O segredo usado na assinatura fica em uma variável de ambiente própria.
{
"X-Webhook-Signature": "assinatura HMAC SHA-256 do corpo",
"X-FluxHub-Client-Id": "identificador do ERP que deve receber",
"X-FluxHub-Event": "tipo do evento entregue"
}Existem ainda segredos separados para cada direção. O ERP se autentica no hub com um identificador e um segredo de cliente. O hub assina o retorno com o segredo de webhook. E o runtime que roda as sessões usa um token próprio para reportar ao hub. Três caminhos, três segredos. Eu já reaproveitei o mesmo valor nos três e não recomendo a ninguém.
Como validar sem cometer meus erros
A validação parece trivial e tem três armadilhas que eu caí em sequência, uma por semana. Vou listar na ordem em que doeram.
- Assine o corpo cruCalcule sobre o corpo exatamente como chegou, antes de qualquer decodificação. Reserializar muda bytes e quebra a comparação.
- Compare em tempo constanteUse comparação segura de strings. Comparação comum vaza informação pelo tempo de resposta.
- Rejeite antes de processarValide a assinatura na entrada, e não depois de já ter criado contato e conversa.
- Trate a repetiçãoGuarde o identificador do evento e ignore o que já foi processado. Reentrega é comportamento normal.
- Responda rápidoAceite, enfileire e processe depois. Endpoint lento gera retentativa e retentativa gera duplicidade.
- Registre o que rejeitouGuarde os eventos recusados. Foi olhando esse registro que eu descobri o teste do estagiário.
Idempotência é a metade que ninguém lembra
Reentrega acontece o tempo todo, e é bom que aconteça. Se o seu servidor demorou, se a rede oscilou, se a resposta se perdeu, o remetente tenta de novo. O hub mantém uma fila interna de tentativas e um histórico de entrega justamente porque isso é parte normal do funcionamento.
Do seu lado, isso significa guardar um identificador por evento e ignorar o que já foi processado. Sem isso, o mesmo lead vira três contatos e a mesma mensagem aparece três vezes na conversa. O webhook de leads dos anúncios já nasce idempotente, e o assunto aparece em Meta Ads e Lead Ads no CRM.
Antes de considerar seguro
- A verificação de assinatura está ligada em todos os endereços de retorno.
- Cada direção da integração usa um segredo diferente.
- Os segredos moram em variável de ambiente, e não no código.
- A assinatura é calculada sobre o corpo cru, sem reserialização.
- A comparação é feita em tempo constante.
- Eventos repetidos são identificados e ignorados sem gerar registro novo.
- Eventos rejeitados ficam registrados para você conseguir investigar.
URL secreta não é segurança. É só uma URL que ainda não descobriram.
Equipe Webajato
Onde isso se encaixa
Se você usa o hub externo, a assinatura é parte do desenho descrito em hub independente de WhatsApp. Se ainda está montando o canal, ligue a verificação já na configuração inicial, seguindo como conectar o WhatsApp ao CRM. E quando o retorno começar a alimentar automação, revise o desenho em automação e agentes de IA, porque evento forjado que dispara fluxo é bem pior que evento forjado que só cria registro.