Webhooks
Entrega, firma y reintentos de webhooks salientes.
Webhooks
ObjectOS utiliza un modelo de outbox persistente para los webhooks salientes. Cuando el plugin de webhooks está habilitado, los cambios de negocio encolan una fila de entrega y un despachador en segundo plano la entrega con reintentos, de modo que un receptor lento o no disponible nunca bloquea la transacción de origen.
Habilitar webhooks
Los webhooks son una capacidad opcional. La imagen de ObjectOS desplegada
debe incluir @objectstack/plugin-webhooks, y el artefacto de la
aplicación debe registrar las suscripciones de webhook (normalmente como
registros de un objeto sys_webhook).
Cuando están habilitados, aparecen dos objetos en la Console:
| Objeto | Propósito |
|---|---|
sys_webhook | Suscripción de webhook (URL de destino, filtro de eventos, secreto, estado) |
sys_webhook_delivery | Registro de entrega (URL, código de respuesta, número de intentos, marca de tiempo de reintento) |
Semántica de entrega
- Al menos una vez. Una entrega puede reintentarse tras un fallo transitorio; los receptores deben ser idempotentes.
- Persistente. Las entregas sobreviven a los reinicios de ObjectOS porque se almacenan en la base de datos de negocio.
- Particionada. Cada worker del despachador reclama una partición del outbox para que los despliegues puedan escalar horizontalmente el despacho sin entregas duplicadas.
- Reintentos acotados. Las entregas fallidas se reintentan con backoff
hasta un límite configurable; las filas agotadas permanecen en
sys_webhook_deliverypara su inspección.
Firma
Cada entrega lleva cabeceras de identificación para que los receptores puedan enrutarla, deduplicarla y verificarla:
X-Objectstack-Event: <event type, e.g. data.record.created>
X-Objectstack-Delivery: <delivery id — use as your idempotency key>
X-Objectstack-Attempt: <attempt number, starting at 1>Cuando una suscripción de webhook tiene un secret, ObjectOS también
firma cada solicitud:
X-Objectstack-Signature: sha256=<hex hmac>La firma es HMAC-SHA256(secret, body) calculada sobre el cuerpo crudo de
la solicitud. Verifícala en el receptor antes de confiar en el payload:
import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(body, signatureHeader, secret) {
const expected = 'sha256=' + createHmac('sha256', secret).update(body).digest('hex');
return timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));
}Rota los secretos emitiendo una nueva suscripción con el nuevo secreto, ejecutando ambas durante una ventana de transición y deshabilitando luego la antigua.
Expectativas del receptor
- Responde con
2xxen unos pocos segundos. Una respuesta408,429o5xx(o un timeout/error de transporte) es reintentable y se reintenta con backoff. Cualquier otro4xxse trata como un fallo permanente y se mueve adeadsin más reintentos. - El despachador no marca una entrega como exitosa hasta que ve un
2xx, por lo que un receptor fallido mantiene la fila ensys_webhook_deliverypara su inspección o reentrega. - Sé idempotente: deduplica usando la cabecera
X-Objectstack-Delivery(el identificador de entrega) o tu propio identificador de evento en el payload.
Manejo de fallos
Cuando algo falla:
- Revisa la fila en
sys_webhook_delivery: se registranstatus,response_code,response_bodyyattempts. - Confirma el acceso de red saliente desde ObjectOS al receptor.
- Si el receptor cambió de forma permanente, actualiza la URL de la suscripción y reentrega la fila desde la Console.
- Para la revisión de incidentes, los registros de auditoría
(
sys_audit_log) capturan las ediciones de las suscripciones, pero no los payloads; los payloads permanecen en el outbox.
Consejos operativos
- No pongas secretos en las URLs de los webhooks (las cadenas de consulta quedan registradas).
- Usa un nombre de host de receptor dedicado para poder descargar tráfico bloqueándolo en el borde sin afectar a la aplicación principal.
- Vigila el retraso del despachador: un outbox que crece normalmente significa que el receptor está degradado.