Skip to content

Start typing to search the documentation.

Node.js / TypeScript SDK

@selfxyz/enterprise-sdk is the official Node/TS client for Self Enterprise. It wraps session creation, retrieval, and webhook signature verification with typed payloads.

Install

npm install @selfxyz/enterprise-sdk

Node 20+. ESM-only.

Initialize

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

const self = new SelfClient({
  apiKey: process.env.SELF_API_KEY!,        // sk_test_... or sk_live_...
});

The API key is the only option you need to pass. Whether the client talks to test or live is determined by the key prefix (sk_test_ or sk_live_).

Sessions

Create

Get the flowId by publishing a configuration in the dashboard, it’s shown on the product’s Test and Live tabs. See Configure a product.

const session = await self.sessions.create({
  flowId: '9c0b4f1c-1d6c-4f1b-a8c4-9f0fa0a8d9e2',
  externalUuid: 'a1b2c3d4-5678-4e9a-b012-3456789abcde',  // a UUID, your stable id for the user
  // optional:
  expiresInSeconds: 3600,
  metadata: { campaign: 'winter-2026' },
  successUrl: 'https://app.example.com/verified',
  failureUrl: 'https://app.example.com/failed',
});

session.verificationUrl;  // give to the user
session.id;               // store on your side
session.expiresAt;        // ISO-8601

Returns Session (alias of CreateSessionResponse).

The most important fields:

FieldNotes
idPersistent session ID. Use this when calling sessions.get(...) or matching against webhook events.
verificationUrlThe URL to hand to the user. They open it in their Self app.
expiresAtISO-8601 timestamp. After this the session cannot be completed.
flowVersionIdImmutable pin to the flow version this session ran against. Publishing a new flow version doesn’t disturb in-flight sessions.

Get

const detail = await self.sessions.get('7f3b2a1e-9c4d-4b2a-8e1f-2c6d5a4b3c2d');  // the session id

detail.status;             // 'pending' | 'valid' | 'invalid' | 'error' | 'expired'
detail.proofAttributes;    // disclosed predicates, e.g. { age_gte_18: true }
detail.storage.state;      // 'pending' | 'committed' | 'failed'

Returns SessionDetail (exported from the SDK, an alias of SessionDetailResponse):

interface SessionDetail {
  id: string;
  status: 'pending' | 'valid' | 'invalid' | 'error' | 'expired';
  createdAt: string;                                  // ISO-8601
  completedAt: string | null;                         // ISO-8601, null until terminal
  expiresAt: string;                                  // ISO-8601
  flowVersionId: string;                              // the pinned flow version
  externalUuid: string;                               // your identifier from create()
  metadata: Record<string, unknown> | null;           // what you passed to create()
  predicatesConfig: Record<string, unknown> | null;   // the rules this ran against
  proofAttributes: Record<string, unknown> | null;    // disclosed results (null until valid)
  storage: {
    state: 'pending' | 'committed' | 'failed';
    uri: string | null;
    credentialId: string | null;
  };
}

Types

The SDK re-exports the canonical Zod schemas and inferred types from @self/schemas:

import type {
  CreateSessionInput,           // what you pass to .create()
  Session,                      // what .create() returns
  SessionDetail,                // what .get() returns
  WebhookEvent,                 // discriminated-union of webhook event payloads
  VerificationCompletedPayload, // the verification.completed payload
} from '@selfxyz/enterprise-sdk';

You can also import the schemas themselves for runtime validation:

import { createSessionBody, webhookEvent } from '@selfxyz/enterprise-sdk';

const parsed = createSessionBody.parse(input);

Webhook verification

See Verify webhooks.

Error handling

import { SelfClient, SelfApiError } from '@selfxyz/enterprise-sdk';

try {
  await self.sessions.create({ flowId, externalUuid });
} catch (err) {
  if (err instanceof SelfApiError) {
    err.statusCode;    // 400 | 401 | 402 | 403 | 404 | 409 | 429 | 5xx
    err.code;          // 'validation_failed' | 'unauthenticated' | 'not_found' | ...
    err.message;       // human-readable
    err.details;       // optional extra context (Record<string, unknown>)
    err.requestId;     // X-Request-Id, quote it to support
  }
}

Bad arguments (for example a flowId or externalUuid that isn’t a UUID) throw SelfValidationError before any request is sent. The SDK doesn’t retry, handle transient 429 and 5xx responses yourself (back off and retry). See Error handling for the full code catalog.