Skip to content

Start typing to search the documentation.

Event catalog

Self delivers a single webhook event: verification.completed. Every registered endpoint receives it; there’s no per-event subscription.

verification.completed

Fires when off-chain verification finishes, either successfully or with a definitive failure. This is the event integrations care about.

Payload

{
  "type": "verification.completed",
  "verification_id": "7f3b2a1e-9c4d-4b2a-8e1f-2c6d5a4b3c2d",
  "external_uuid": "a1b2c3d4-5678-4e9a-b012-3456789abcde",
  "flow_id": "9c0b4f1c-1d6c-4f1b-a8c4-9f0fa0a8d9e2",
  "flow_version_id": "b1e2c3d4-5678-4abc-9def-0123456789ab",
  "environment": "live",
  "status": "valid",
  "proof_attributes": {
    "age_gte_18": true,
    "country_allowed": true,
    "ofac_clear": true
  },
  "proof": { /* raw ZK proof JSON, or null if status != 'valid' */ },
  "verified_at": "2026-05-29T17:33:21.412Z",
  "storage_state": "pending",
  "storage_uri": null
}

Fields

FieldTypeNotes
verification_idstringStable, unique. Use as your dedup key.
external_uuidstringThe identifier you passed when creating the session.
flow_id / flow_version_idstringWhich flow + frozen version this verification ran against.
environment'test' | 'live'Matches the API key environment that created the session.
status'valid' | 'invalid' | 'error' | 'expired'See statuses below.
proof_attributesobjectThe disclosed predicates (booleans for predicates, values for reveals). Empty object on non-valid statuses.
proofobject | nullRaw Groth16 proof JSON. Present when status === 'valid'. Most integrations ignore this; we already verified it.
verified_atISO-8601When verification completed.
storage_state'pending' | 'committed' | 'failed'Async storage state at the time the event fired, usually pending. For the final state, read the session with sessions.get(...).
storage_uristring | nullSet once storage has committed, usually null at fire time.

Statuses

  • valid: the proof verified and all flow predicates passed. This is the green-light case.
  • invalid: the proof verified but at least one predicate failed (e.g. user is 16, flow requires 18).
  • error: verification failed for a technical reason (unsupported document, malformed proof, signature mismatch). Not the user’s fault necessarily.
  • expired: session expired before completion.

Event ID (for deduplication)

The deduplication key is <verification_id>-completed (the same for every status, so a retry of this event reuses it).

Handling in TypeScript

The payload is typed as a discriminated union on event.type, so branch on it. Today verification.completed is the only type delivered:

import type { WebhookEvent } from '@selfxyz/enterprise-sdk';

function handle(event: WebhookEvent) {
  if (event.type === 'verification.completed') {
    // narrowed to VerificationCompletedPayload
    // event.verification_id, event.external_uuid, event.proof_attributes, event.status
  }
}

Forward compatibility

We may add fields to existing events without bumping major versions. The SDK’s webhookEvent schema uses .passthrough(), so unknown fields are preserved (and ignored by your existing code). We may also add new event types over time, so branch on event.type and let your handler ignore any type it doesn’t recognize.