Webhooks
Tracium fires webhooks when traceability events happen. Examples:
- A trace event was recorded on-chain (
event.recorded). - A new NFT/lot was minted (
nft.created). - A lot was recalled (
nft.recalled). - A custody transfer completed (
custody.transferred).
This page covers payload structure, HMAC signature verification, retry semantics, and the available topics.
Topics emitted
Section titled “Topics emitted”Topics currently available:
| Topic | When emitted |
|---|---|
event.recorded | A captured trace event was recorded on-chain |
nft.created | An NFT was minted (new lot) |
nft.recalled | An NFT was recalled (TENANT_ADMIN action) |
custody.transferred | A custody transfer completed |
webhook.test | Manually fired via POST /api/v1/webhooks/:id/test for testing |
Wire format
Section titled “Wire format”Every delivery is POST with application/json body:
{ "event": "event.recorded", "timestamp": "2026-05-08T12:34:56.789Z", "data": { ... }}Headers:
| Header | Value |
|---|---|
X-Webhook-Id | unique delivery UUID |
X-Webhook-Event | the topic (e.g. event.recorded) |
X-Webhook-Signature | sha256=<hex-hmac> |
Content-Type | application/json |
Signature verification
Section titled “Signature verification”Tracium signs every payload with HMAC-SHA256 over the raw body using the subscription secret (returned only at creation).
The header is sha256=<hex> (simple format, not Stripe-style with
timestamp). Verify with constant-time comparison to prevent timing
attacks.
Node.js
Section titled “Node.js”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'); }}Python
Section titled “Python”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')Retry semantics
Section titled “Retry semantics”- Tracium dispatches delivery via a managed queue with per-subscription concurrency.
- If response is non-2xx, 3 retry attempts with exponential backoff (initial delay: 30s).
- After 3 failures the delivery is marked failed, visible in
GET /api/v1/webhooks/:id/deliverieswith the captured error.
Make your endpoint idempotent. Use X-Webhook-Id as a dedup key
since retries repeat the same id.
URL validation at registration
Section titled “URL validation at registration”When you register an endpoint via POST /api/v1/webhooks, the URL
must be valid + reachable. Standard server-side validation. HTTPS
recommended.
Managing subscriptions
Section titled “Managing subscriptions”Endpoints (all require TENANT_ADMIN role). Set BASE_URL to the
API base URL provided in your provisioning email.
# List tenant subscriptionscurl "$BASE_URL/api/v1/webhooks" \ -H "Authorization: Bearer $TOKEN"
# Create new subscriptioncurl -X POST "$BASE_URL/api/v1/webhooks" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-app.example.com/webhooks/darwin", "events": ["event.recorded", "nft.created"] }'# Returns: { id, url, events, active, createdAt, secret }# The secret is shown ONLY ONCE; store it now.
# Partial updatecurl -X PATCH "$BASE_URL/api/v1/webhooks/$ID" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "active": false }'
# Deletecurl -X DELETE "$BASE_URL/api/v1/webhooks/$ID" \ -H "Authorization: Bearer $TOKEN"
# View last 100 deliveriescurl "$BASE_URL/api/v1/webhooks/$ID/deliveries" \ -H "Authorization: Bearer $TOKEN"
# Fire test eventcurl -X POST "$BASE_URL/api/v1/webhooks/$ID/test" \ -H "Authorization: Bearer $TOKEN"What’s next
Section titled “What’s next”- Operational reports: reporting endpoints the platform exposes
- Reference: full payload schemas per topic
- Authentication: API keys for managing webhooks from your backend