Saltar al contenido
mini.casa
Desarrolladores

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.