Saltearse al contenido

Webhooks

Tracium dispara webhooks cuando ocurren eventos de trazabilidad. Ejemplos:

  • Se grabó un nuevo evento on-chain (event.recorded).
  • Se acuñó un nuevo NFT/lote (nft.created).
  • Se hizo recall de un lote (nft.recalled).
  • Se transfirió custodia entre custodians (custody.transferred).

Esta página cubre estructura del payload, verificación de firma HMAC, semántica de retry, y los topics disponibles.

Los topics actualmente shipped:

TopicCuándo se emite
event.recordedUn trace event capturado fue grabado on-chain
nft.createdSe acuñó un NFT (lote nuevo)
nft.recalledUn NFT fue recalled (fuente del recall: TENANT_ADMIN)
custody.transferredSe completó una transferencia de custodia
webhook.testDisparado manualmente vía POST /api/v1/webhooks/:id/test para testing

Cada delivery de webhook es POST con body application/json:

{
"event": "event.recorded",
"timestamp": "2026-05-08T12:34:56.789Z",
"data": { ... }
}

Headers:

HeaderValor
X-Webhook-IdUUID único de delivery
X-Webhook-Eventel topic (ej. event.recorded)
X-Webhook-Signaturesha256=<hex-hmac>
Content-Typeapplication/json

Tracium firma cada payload con HMAC-SHA256 sobre el body raw usando el secret del subscription (devuelto solo al crear).

El header es sha256=<hex> (formato simple, no Stripe-style con timestamp). Verificá con comparación constant-time para prevenir ataques de timing.

import { createHmac, timingSafeEqual } from 'crypto';
function verify(req, secret) {
const header = req.headers['x-webhook-signature'];
// header format: 'sha256=<hex>'
const [, hex] = header.match(/^sha256=([a-f0-9]+)$/) || [];
if (!hex) throw new Error('invalid signature header');
const expected = createHmac('sha256', secret)
.update(req.rawBody)
.digest('hex');
if (!timingSafeEqual(Buffer.from(hex), Buffer.from(expected))) {
throw new Error('signature mismatch');
}
}
import hmac, hashlib
def verify(headers, raw_body, secret):
sig = headers['X-Webhook-Signature'] # 'sha256=<hex>'
if not sig.startswith('sha256='):
raise ValueError('invalid signature header')
received = sig[len('sha256='):]
expected = hmac.new(
secret.encode(),
raw_body,
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(received, expected):
raise ValueError('signature mismatch')
  • Tracium dispara delivery vía cola BullMQ (webhook-delivery).
  • Si la respuesta no es 2xx → 3 intentos de retry con exponential backoff (delay inicial: 30s).
  • Después de 3 fallos se marca como failed; queda visible en GET /api/v1/webhooks/:id/deliveries con el error capturado.

Hacé tu endpoint idempotente — usá el X-Webhook-Id como key de dedup ya que retries repiten el mismo id.

Cuando registrás un endpoint vía POST /api/v1/webhooks, la URL tiene que ser válida + alcanzable. Validación server-side básica estándar; HTTPS recomendado.

Endpoints (todos requieren rol TENANT_ADMIN):

Ventana de terminal
# Listar subscriptions del tenant
curl "$BASE_URL/api/v1/webhooks" \
-H "Authorization: Bearer $TOKEN"
# Crear nueva subscription
curl -X POST "$BASE_URL/api/v1/webhooks" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://tu-app.example.com/webhooks/darwin",
"events": ["event.recorded", "nft.created"]
}'
# Devuelve: { id, url, events, active, createdAt, secret } — secret SOLO una vez
# Update parcial
curl -X PATCH "$BASE_URL/api/v1/webhooks/$ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "active": false }'
# Borrar
curl -X DELETE "$BASE_URL/api/v1/webhooks/$ID" \
-H "Authorization: Bearer $TOKEN"
# Ver últimas 100 deliveries
curl "$BASE_URL/api/v1/webhooks/$ID/deliveries" \
-H "Authorization: Bearer $TOKEN"
# Disparar evento test
curl -X POST "$BASE_URL/api/v1/webhooks/$ID/test" \
-H "Authorization: Bearer $TOKEN"
  • Reportes operacionales — endpoints de reporting que el platform expone (alerts, documents, supplier performance, etc.)
  • Reference — schemas completos de payload por topic
  • Autenticación — API keys para gestionar webhooks desde tu backend