Subscribe to Webhooks

Receive real-time notifications when attestations and compliance checks complete

View as Markdown

This guide covers how to receive, verify, and process webhook events from Clossir. Webhooks deliver real-time notifications when attestations are created, compliance checks complete, and policies change — without polling.

Prerequisites

  • An organization with operations in progress (Onboard an Organization)
  • A publicly reachable HTTPS endpoint to receive webhook deliveries

1. Available event channels

Clossir delivers events across seven channels organized in two families:

Attestation channels

ChannelFires when
signum.attestation.createdA new attestation is issued
signum.attestation.revokedAn attestation is revoked
signum.attestation.sync_completedA cross-chain attestation sync completes (success or failure)

Compliance channels

ChannelFires when
signum.compliance.check_requestedA compliance check is queued
signum.compliance.check_completedA compliance check finishes
signum.compliance.rule_changedA compliance policy configuration is modified
signum.compliance.rule_syncedA compliance policy on-chain sync started, completed, or failed

For the full payload schemas, see the API Reference.

2. Configure your endpoint

Webhook subscriptions are configured through the Clossir dashboard. Navigate to your organization settings and add your webhook endpoint URL.

Your endpoint must:

  • Accept POST requests over HTTPS
  • Return a 2xx status code within 10 seconds to acknowledge receipt
  • Process events idempotently — the same event may be delivered more than once

There is no webhook subscription management API at this time. All webhook configuration is done through the dashboard.

3. Payload structure

Every webhook delivery is a JSON payload containing the event data. Here is a representative signum.attestation.created payload:

attestation-created-payload.json
1{
2 "attestationId": "att_abc123",
3 "userId": "usr_xyz789",
4 "issuerId": "iss_def456",
5 "attestationType": "kyc",
6 "claimHash": "0xabc...def",
7 "kycLevel": 2,
8 "investorClass": 2,
9 "countryCode": "US",
10 "expiresAt": 1735689600000,
11 "metadata": {},
12 "timestamp": 1720000000000
13}

Each event type has its own payload schema. See the API Reference for the full catalog.

4. Tracing headers

Every webhook delivery includes W3C Trace Context headers for distributed tracing:

HeaderRequiredDescription
traceparentYesW3C traceparent header (format: {version}-{trace-id}-{parent-id}-{flags})
tracestateNoW3C tracestate header with vendor-specific trace data

Use these headers to correlate webhook deliveries with your internal request traces:

extract-trace.ts
1function extractTraceContext(headers: Record<string, string>) {
2 const traceparent = headers["traceparent"];
3 // Format: 00-{32-char trace-id}-{16-char parent-id}-{2-char flags}
4 const parts = traceparent.split("-");
5
6 return {
7 traceId: parts[1],
8 parentId: parts[2],
9 flags: parts[3],
10 };
11}

5. Verify the payload

Webhook signature verification is coming soon. Until the verification mechanism is available, we recommend allowlisting the Clossir delivery IP range and validating payloads against the expected schema.

When signature verification launches, each delivery will include a signature header that you can verify using your webhook secret to confirm the payload originated from Clossir.

6. Build your webhook handler

Here is a complete webhook handler that receives, validates, and processes events:

webhook-handler.ts
1import { serve } from "@hono/node-server";
2import { Hono } from "hono";
3
4const app = new Hono();
5
6app.post("/webhooks/signum", async (c) => {
7 const payload = await c.req.json();
8 const traceparent = c.req.header("traceparent");
9
10 // Log with trace context for observability
11 console.log(`[${traceparent}] Received event`, payload);
12
13 // Route by event type based on payload shape
14 if ("attestationId" in payload && "claimHash" in payload) {
15 await handleAttestationEvent(payload);
16 } else if ("checkId" in payload) {
17 await handleComplianceCheckEvent(payload);
18 } else if ("changeType" in payload) {
19 await handleRuleChangeEvent(payload);
20 }
21
22 // Return 200 to acknowledge receipt
23 return c.json({ received: true });
24});
25
26async function handleAttestationEvent(event: any) {
27 console.log(`Attestation ${event.attestationId} for user ${event.userId}`);
28 // Process idempotently — use attestationId as dedup key
29}
30
31async function handleComplianceCheckEvent(event: any) {
32 console.log(`Check ${event.checkId} passed: ${event.passed}`);
33}
34
35async function handleRuleChangeEvent(event: any) {
36 console.log(`Rule ${event.changeType} changed for asset ${event.assetId}`);
37}
38
39serve({ fetch: app.fetch, port: 3000 });

7. Correlate with commands

When you issue a command that returns a resource ID in its 202 response (like attestationId, assetId, or checkId), you can match that ID to the corresponding webhook payload:

correlate.ts
1// 1. Issue a command and capture the resource ID
2const result = await signum.attestations.create({
3 walletAddress: "0xabc...def",
4 chainEid: 30101,
5});
6const attestationId = result.attestationId; // "att_xyz789"
7
8// 2. Track the pending operation
9await db.pendingOperations.insert({
10 attestationId,
11 type: "attestation_create",
12 createdAt: new Date(),
13});
14
15// 3. In your webhook handler, match by resource ID
16async function handleWebhook(payload: any) {
17 const pending = await db.pendingOperations.findByAttestationId(
18 payload.attestationId
19 );
20 if (pending) {
21 console.log(`Attestation ${pending.attestationId} created on-chain`);
22 await db.pendingOperations.markComplete(pending.attestationId);
23 }
24}

See Async Operations for the full correlation pattern.

Handle failures

  • Retries: If your endpoint returns a non-2xx status or times out, Clossir retries delivery with exponential backoff.
  • Idempotency: Always process events idempotently. Use the event’s unique ID (attestationId, checkId, etc.) as a deduplication key.
  • Ordering: Events may arrive out of order. Use the timestamp field to determine the sequence of events for a given resource.

Reference

Next steps