Skip to main content
Cada requisição inclui o header X-Cordialy-Signature com uma assinatura HMAC-SHA256 do corpo. Valide essa assinatura antes de processar qualquer evento.
X-Cordialy-Signature: sha256=abc123...

Webhook Secret

O secret é gerado automaticamente quando você cadastra um webhook em Integrações → Webhooks. Ele é exibido uma única vez logo após a criação — copie e guarde em um local seguro.
Se você perder o secret, não é possível recuperá-lo. Delete o webhook e crie um novo para gerar um secret diferente.

Como validar

A assinatura é calculada sobre o body bruto (bytes exatos recebidos na requisição).
Se você parsear o body antes de validar (ex: express.json() no Node.js), o body já foi transformado e a assinatura não vai bater. Leia o body como stream/raw antes de qualquer parsing.
import crypto from 'crypto';
import express from 'express';

const app = express();

// IMPORTANTE: use express.raw() na rota do webhook, não express.json()
app.post('/webhook/cordialy', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-cordialy-signature'] as string;
  const secret = process.env.CORDIALY_WEBHOOK_SECRET!;

  if (!validarAssinatura(req.body, signature, secret)) {
    return res.status(401).send('Assinatura inválida');
  }

  const evento = JSON.parse(req.body.toString());
  // processar evento...

  res.status(200).send('ok');
});

function validarAssinatura(rawBody: Buffer, signature: string, secret: string): boolean {
  const esperado = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(esperado)
  );
}

Por que timingSafeEqual / compare_digest / hash_equals?

Comparações simples (===, ==) são vulneráveis a ataques de timing — um atacante pode descobrir o secret medindo o tempo de resposta. As funções de comparação constante eliminam esse risco.