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
| Evento | Quando |
|---|---|
link.created, link.updated, link.deleted | Mudanças no link |
link.blocked | Moderação, lista de bloqueio ou denúncia |
link.expired | Passou de expires_at ou atingiu max_clicks (reason) |
link.clicked | Um por clique humano |
clicks.batch | Cliques acumulados, até 500 por entrega, a cada minuto. Prefira para volume alto |
conversion.created | Conversã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
2xxem 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;
PATCHcom"status": "active"reativa. GET /webhooks/{id}/deliveriesmostra 30 dias de histórico;POST .../deliveries/{id}/retryreenvia;POST /webhooks/{id}/testmanda umwebhook.test.