🇪🇸 Español 🇬🇧 English 🇧🇷 Português

Verificar a assinatura HMAC dos webhooks de saída (lado receptor)

Como validar no seu próprio servidor que um webhook de saída do Omnifox é autêntico, usando a assinatura HMAC-SHA256.

Jul 11, 2026

Ao configurar um Webhook de saída no Omnifox (Configurações > Webhooks), cada evento (contact.created, message.delivered, conversation.assigned etc.) é enviado por HTTP POST para a URL que você definiu. Para que seu servidor possa confirmar que aquele POST realmente veio do Omnifox e não de terceiros, cada entrega inclui uma assinatura HMAC-SHA256.

Requisitos

  • Um Webhook criado em Configurações > Webhooks, com URL HTTPS (obrigatório).
  • O signing_secret (com prefixo whsec_) que o Omnifox mostra uma única vez, ao criar o webhook ou ao rotacioná-lo (POST /api/v1/webhooks/{webhook}/rotate-secret). Guarde-o imediatamente em um lugar seguro: não é possível consultá-lo novamente.

Cabeçalhos recebidos em cada entrega

Cabeçalho Conteúdo
X-Omnifox-Event Nome do evento, ex. message.delivered
X-Omnifox-Delivery-Id ID único dessa entrega (útil para deduplicar novas tentativas)
X-Omnifox-Timestamp Timestamp Unix (segundos) em que o payload foi assinado
X-Omnifox-Signature sha256=<hex> — a assinatura HMAC
X-Omnifox-Attempt Número da tentativa (1 = primeira tentativa)

Como a assinatura é calculada

assinatura = HMAC_SHA256( "{timestamp}.{corpo_json_exato}" , signing_secret )

O timestamp é o mesmo valor do cabeçalho X-Omnifox-Timestamp, unido por um ponto (.) ao corpo JSON exatamente como recebido (sem reformatar nem reordenar as chaves).

Exemplo: verificação em Node.js

const crypto = require('crypto');

function verifyOmnifoxWebhook(rawBody, headers, signingSecret) {
  const signatureHeader = headers['x-omnifox-signature'] || '';
  const timestamp = headers['x-omnifox-timestamp'] || '';
  const [, providedHex] = signatureHeader.split('=');

  const expectedHex = crypto
    .createHmac('sha256', signingSecret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  const valid = providedHex &&
    crypto.timingSafeEqual(Buffer.from(expectedHex), Buffer.from(providedHex));

  return valid;
}

Sempre use o corpo bruto (raw body, string sem parsear) para calcular a assinatura; se seu framework já parseou o JSON, você precisa capturar o raw body antes disso.

Dicas

  • Rejeite (HTTP 401) qualquer entrega cuja assinatura não bata, antes de processar o payload.
  • Adicione uma validação opcional de replay: se X-Omnifox-Timestamp tiver mais de alguns minutos, rejeite a entrega.
  • Use X-Omnifox-Delivery-Id para deduplicar: o Omnifox tenta novamente as entregas com backoff exponencial se seu endpoint retornar erro ou timeout, então você pode receber o mesmo evento mais de uma vez.

Solução de problemas

  • A assinatura nunca bate: o erro mais comum é comparar contra o JSON reserializado pelo seu framework em vez do corpo bruto exato que chegou.
  • Os cabeçalhos de assinatura não chegam: se o webhook foi criado antes de gerar/rotacionar o signing_secret, algumas assinaturas antigas são enviadas sem assinatura (legado); rotacione o segredo em Configurações > Webhooks para ativá-la.
  • Você perdeu o signing_secret: não há como recuperá-lo; rotacione com POST /api/v1/webhooks/{webhook}/rotate-secret e atualize seu código de verificação com o novo valor.
Isso foi útil?

Artigos relacionados