Skip to content
Skip the search — install the Self skill Let your AI agent integrate Self for you.

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_...
});

apiKey is the only option, and the key is bound to one environment (test or live).

Sessions

Create

Get the flowId by deploying 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. Deploying 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;    // enforced predicate config, e.g. { minimumAge: 18, ofac: 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;    // enforced predicate config, echoed back (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 {
  SelfClientOptions,            // SelfClient constructor options
  CreateSessionInput,           // what you pass to .create()
  Session,                      // what .create() returns
  SessionDetail,                // what .get() returns
  WebhookEvent,                 // the webhook event payload type
  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 | 500 | 503
    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.

Was this page helpful?