Webhooks

POSThttps://api.curtei.com/v1/webhooks

Receba eventos de clique, expiração, bloqueio e conversão no seu servidor, assinados com HMAC-SHA256, com reenvio automático. Escopo: webhooks:manage.

curl -X POST https://api.curtei.com/v1/webhooks \
  -H "Authorization: Bearer $CURTEI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://meusistema.com.br/hooks/curtei", "events": ["link.clicked", "link.expired"]}'

A resposta traz o secret uma vez só. A URL precisa ser HTTPS (porta 443 ou 8443) e apontar para um IP público.

Eventos

EventoQuando
link.created, link.updated, link.deletedMudanças no link
link.blockedModeração, lista de bloqueio ou denúncia
link.expiredPassou de expires_at ou atingiu max_clicks (reason)
link.clickedUm por clique humano
clicks.batchCliques acumulados, até 500 por entrega, a cada minuto. Prefira para volume alto
conversion.createdConversão registrada

Verificar a assinatura

Cada entrega traz X-Webhook-Timestamp e X-Webhook-Signature: v1=<HMAC-SHA256(secret, timestamp + "." + corpo)>. Rejeite assinaturas diferentes e timestamps com mais de 5 minutos.

import crypto from "node:crypto";

export function webhookValido(rawBody, headers, secret) {
  const ts = headers["x-webhook-timestamp"];
  const esperado = "v1=" + crypto.createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
  const recebido = headers["x-webhook-signature"] ?? "";
  const recente = Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
  return recente && recebido.length === esperado.length && crypto.timingSafeEqual(Buffer.from(recebido), Buffer.from(esperado));
}

Entrega e reenvio

  • Responda 2xx em até 10 s. Redirecionamentos não são seguidos.
  • Falhou? Nova tentativa em 1 min, 5 min, 30 min, 2 h, 6 h e 24 h. Depois da 7ª, a entrega fica failed.
  • Depois de 50 falhas seguidas o webhook é desativado; PATCH com "status": "active" reativa.
  • GET /webhooks/{id}/deliveries mostra 30 dias de histórico; POST .../deliveries/{id}/retry reenvia; POST /webhooks/{id}/test manda um webhook.test.