Señales fiables para el trabajo que lleva tiempo
Solicitudes HTTPS firmadas cuando cambian trabajos, dominios, publicaciones o aprobaciones, entregadas al menos una vez.
Registra un endpoint
Añade un endpoint en la página Desarrolladores o con el permiso webhooks:write:
curl -s -X POST "$BASE/workspaces/$WS/webhooks" \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: $(openssl rand -hex 16)" \
-H "Content-Type: application/json" \
-d '{"url":"https://hooks.example.com/mini-casa","events":["job.updated","publication.updated"]}'La respuesta contiene signing_secret (whsec_...), que se muestra una sola vez. La URL debe ser HTTPS en el puerto 443 y resolver solo a direcciones públicas; se rechazan destinos privados, locales e internos, la comprobación se repite antes de cada entrega y nunca se siguen redirecciones. Un espacio puede tener hasta 10 endpoints activos.
Demuestra que lo controlas
Los endpoints nuevos empiezan como pending_verification. El botón Verificar (o POST /webhooks/{id}/verify) envía un webhook.challenge firmado. Responde 2xx con {"challenge": "<data.challenge>"} y el endpoint pasa a active.
Eventos
job.updated: un trabajo o una ejecución masiva cambia de estado.job.item.updated: un elemento de una ejecución masiva cambia de estado.domain.updated: un dominio propio avanza por sus comprobaciones.publication.updated: una publicación empieza, termina bien, falla o queda reemplazada.approval.updated: una aprobación se decide, caduca o se usa.billing.entitlement.updated: cambian el plan o los derechos.
Los cuerpos solo llevan ids y un estado, nunca credenciales, contenido ni datos personales. Consulta el estado actual con la API.
Verifica cada solicitud
Cada solicitud trae SmallWorld-Webhook-Id, SmallWorld-Webhook-Timestamp (segundos unix) y SmallWorld-Webhook-Signature (v1=<hex>,kid=<id de clave>). La firma es HMAC-SHA256 de <timestamp>.<cuerpo original> con tu secreto. Usa el cuerpo original antes de interpretar el JSON, compara en tiempo constante y rechaza marcas de tiempo con más de cinco minutos de diferencia con tu reloj.
import crypto from "node:crypto";
export function verify(secret, timestamp, rawBody, header) {
const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
const given = /v1=([a-f0-9]+)/.exec(header)?.[1] ?? "";
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
return fresh && given.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}Garantías de entrega
- Al menos una vez. Una solicitud se repite hasta que respondes
2xx. El id del evento es el mismo en cada reintento y reenvío, así que guárdalo e ignora los ids que ya viste. - Los reintentos se espacian de un minuto a un día durante 72 horas en total. Después, la entrega queda como fallida y puedes reenviarla desde la página Desarrolladores o con
redeliver_webhook. - Un endpoint con 10 entregas fallidas seguidas se pausa hasta que lo reanudes.
- El orden no está garantizado: toma el estado como una pista y lee el recurso.