API AutoConsulta
  • Começar
  • Catálogo
  • Webhooks
  • Canal de certificação
  • Servidor MCP
  • Referência da API
O que sãoConfigurar um endpointVerificar a assinaturaEventos e entrega
Webhooks

Verificar a assinatura

Toda entrega chega com o header:

Code
X-AutoConsulta-Signature: t=1756006190,v1=8f3a…c21b
  • t — o instante do envio, em segundos (epoch)
  • v1 — HMAC-SHA256 em hexadecimal

O que é assinado

Code
<t> + "." + <corpo cru>

O t do header, um ponto, e o corpo exatamente como chegou.

Use o corpo CRU, nunca o objeto reserializado

Se você fizer JSON.parse e depois JSON.stringify para conferir, os bytes mudam — espaçamento, ordem de chaves, escape de acento — e a assinatura nunca vai bater, mesmo estando correta.

Muitos frameworks já consumiram o corpo quando seu código roda. Configure o seu para guardar o corpo cru (em Express, a opção verify do express.json()).

O procedimento

Code
import crypto from 'node:crypto' function assinaturaConfere(corpoCru, headerAssinatura, segredos) { const partes = Object.fromEntries( headerAssinatura.split(',').map((p) => p.split('=').map((s) => s.trim())), ) const t = partes.t if (!t) return false // pode vir mais de um v1 (janela de rotação) — aceite se QUALQUER um casar const recebidas = headerAssinatura .split(',').map((p) => p.split('=').map((s) => s.trim())) .filter(([k]) => k === 'v1').map(([, v]) => v) const material = `${t}.${corpoCru}` return recebidas.some((recebida) => segredos.some((segredo) => { const esperada = crypto.createHmac('sha256', segredo).update(material).digest('hex') const a = Buffer.from(recebida, 'utf8') const b = Buffer.from(esperada, 'utf8') // comparação em tempo constante: `===` vaza, pelo tempo, quantos bytes bateram return a.length === b.length && crypto.timingSafeEqual(a, b) }), ) }

Se não conferir, responda com erro e descarte. Uma assinatura que não bate significa que o corpo não veio de nós, ou não veio íntegro.

Dois detalhes que quebram integrações

Aceite mais de um v1. Durante uma rotação de segredo vão duas assinaturas no mesmo header, a nova primeiro. Um código que leia só a primeira ocorrência funciona — mas um que valide apenas contra o segredo atual derruba toda entrega da janela de 24 horas.

Compare em tempo constante. Não use ===.

Sobre repetição de mensagem (replay)

O t entra no material assinado, então ninguém pode reaproveitar um corpo assinado alterando o horário: mudar o t invalida a assinatura.

Mas nós não impomos janela nenhuma — a decisão é sua

O servidor não rejeita uma entrega por causa do t. Se você quer proteção contra reenvio de uma mensagem capturada, é o seu código que precisa comparar o t com o horário atual e recusar o que estiver velho demais.

Recomendamos 5 minutos de tolerância. É recomendação, não comportamento do servidor.

O t é carimbado no instante do envio, então em condições normais ele chega com poucos segundos de idade. A folga de 5 minutos existe para atraso de rede e diferença de relógio entre servidores, não para o caminho normal.

Verificação em ordem

  1. Ler o corpo cru
  2. Conferir a assinatura (aceitando múltiplos v1, em tempo constante)
  3. Só então interpretar o JSON
  4. Responder 2xx rápido — e processar depois, se for demorado

O passo 4 importa: o tempo limite de cada entrega é 10 segundos.

Last modified on October 14, 2026
Configurar um endpointEventos e entrega
On this page
  • O que é assinado
  • O procedimento
  • Dois detalhes que quebram integrações
  • Sobre repetição de mensagem (replay)
  • Verificação em ordem
Javascript