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-sdkpnpm add @selfxyz/enterprise-sdkyarn add @selfxyz/enterprise-sdkNode 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:
| Field | Notes |
|---|---|
id | Persistent session ID. Use this when calling sessions.get(...) or matching against webhook events. |
verificationUrl | The URL to hand to the user. They open it in their Self app. |
expiresAt | ISO-8601 timestamp. After this the session cannot be completed. |
flowVersionId | Immutable 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.
Thanks for your feedback!