cpfhub/api
Documentação

Webhooks.

Receba notificações HTTP quando eventos ocorrerem na sua conta. Cada entrega é assinada com HMAC-SHA256 para você verificar autenticidade.

Configurar um endpoint

Em /webhooks, registre uma URL HTTPS pública e escolha os eventos. O segredo de assinatura aparece após criar — é o mesmo para todas as entregas daquele endpoint.

Eventos disponíveis

  • cpf.consultedConsulta concluída com sucesso.
  • cpf.failedConsulta falhou (CPF inválido, serviço indisponível, etc.).
  • key.createdNova chave de API foi gerada.
  • key.revokedChave de API foi revogada.
  • quota.warningAtingiu 80% da cota mensal.
  • quota.exceededCota mensal foi excedida.

Payload

Toda entrega usa POST com Content-Type: application/json e o cabeçalho CPFHub-Signature:

exemplo · cpf.consulted
{
  "id": "evt_01HM9Q2K3N4PXYZ",
  "type": "cpf.consulted",
  "created_at": "2026-04-17T14:32:11Z",
  "data": {
    "query_id": "qry_01HM9Q2K3N4PXYZ",
    "endpoint": "cpf.standard",
    "cpf": "111.444.777-35",
    "key_id": "k_2"
  }
}

Verificação de assinatura

O cabeçalho CPFHub-Signature contém o timestamp e a assinatura HMAC-SHA256 do corpo bruto, separados por vírgula:

cabeçalho
CPFHub-Signature: t=1729184832,v1=4f8a91bc22ef...4f2c91
import crypto from "node:crypto";

export function verify(rawBody, header, secret, tolerance = 300) {
  if (!header) return false;
  const parts = Object.fromEntries(
    header.split(",").map((kv) => kv.split("=")),
  );
  const ts = Number(parts.t);
  const sig = parts.v1;
  if (!ts || !sig) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${ts}.${rawBody}`)
    .digest("hex");

  if (Math.abs(Date.now() / 1000 - ts) > tolerance) return false;
  if (expected.length !== sig.length) return false;
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(sig),
  );
}
Use o corpo bruto (antes de qualquer parse JSON). Frameworks como Express e FastAPI consomem o stream — configure para preservar o raw body antes de validar.

Retries

Esperamos resposta 2xx em até 10 segundos. Qualquer outro status ou timeout dispara retry com backoff exponencial:

  • 1ª tentativa: imediata
  • 2ª tentativa: 30 segundos depois
  • 3ª tentativa: 5 minutos depois
  • 4ª tentativa: 30 minutos depois
  • 5ª tentativa: 2 horas depois

Após 5 tentativas, a entrega é marcada como falhada. Você pode reenviar manualmente em /webhooks/[id].

Idempotência

O mesmo evento pode ser entregue mais de uma vez (exatamente uma vez é garantia difícil de oferecer). Trate seu handler como idempotente usando o id do evento como chave.