Skip to content

Start typing to search the documentation.

SDK error handling

The SDK throws three error classes. Catch them by type.

SelfApiError

Thrown when the API returns a non-2xx response. It carries:

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

try {
  await self.sessions.create({ flowId, externalUuid });
} catch (err) {
  if (err instanceof SelfApiError) {
    err.statusCode;  // number (HTTP status)
    err.code;        // string ('validation_failed', 'not_found', 'unauthenticated', ...)
    err.message;     // string (human-readable)
    err.details;     // Record<string, unknown> | undefined
    err.requestId;   // string | undefined (X-Request-Id; quote it to support)
  } else {
    throw err;       // network error, or something unexpected
  }
}

Switching on the error

Branch on statusCode for the HTTP-level case and on code for the specific reason:

try {
  await self.sessions.create({ flowId, externalUuid });
} catch (err) {
  if (!(err instanceof SelfApiError)) throw err;

  switch (err.statusCode) {
    case 400:
      return badRequest(err.details);          // details.issues lists the bad fields
    case 404:
      return notFound('That flow or session no longer exists');
    case 402:
      return paymentRequired(err.details);     // { balance, required, planTier }
    case 429:
      return tryLater();                        // back off and retry
    default:
      return serverError(err);
  }
}

Error codes

err.code is a stable, machine-readable string. The most common from sessions.create(...):

codestatusCodeWhenWhat to do
validation_failed400Request body didn’t match the schema. details.issues lists offending fields.Fix the request; do not retry.
unauthenticated401Missing, malformed, or revoked API key.Check your key.
unauthenticated402Org credit balance too low (hard credit gate). details carries balance, required, planTier.Top up or upgrade. Branch on statusCode 402, not the code.
forbidden403Key valid but not allowed for this resource (for example a test key against a live flow).Use a key in the right environment.
not_found404flowId / sessionId doesn’t exist, is archived, or belongs to another org.Verify the ID in the dashboard.
conflict409The flow exists but has no published version.Publish the flow, then retry.
rate_limited429Per-key rate limit exceeded.Honor Retry-After and back off.
vendor_unavailable503A dependency is temporarily unavailable.Retry with backoff.
internal_error500Server-side error.Retry with backoff; if it persists, contact support.

SelfValidationError

Thrown when the arguments you pass fail schema validation, before any request goes out. The most common cause is a flowId or externalUuid that isn’t a UUID. It also comes from SelfWebhooks.verify(...) when a verified payload doesn’t match any known event shape.

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

try {
  await self.sessions.create({ flowId, externalUuid });
} catch (err) {
  if (err instanceof SelfValidationError) {
    err.issues;   // ZodIssue[], which fields are wrong
    err.message;  // e.g. "Invalid sessions.create input: flowId (invalid uuid)"
  }
}

Fix the input; it’s a programming error, not a transient one.

WebhookVerificationError

Thrown by SelfWebhooks.verify(...) when the signature doesn’t match the body, the timestamp is too old, or required headers are missing.

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

try {
  const event = SelfWebhooks.verify(raw, headers, secret);
} catch (err) {
  if (err instanceof WebhookVerificationError) {
    // Respond 400; a bad signature won't get better on retry.
  }
}

A separate failure mode: if the signature checks out but the body doesn’t match any known event schema, verify(...) throws SelfValidationError. That usually means the server is sending a newer event type than your SDK version knows, upgrade the SDK.

Retries

The SDK does not retry. A failed request throws immediately. Handle transient responses yourself: retry 429 (after Retry-After) and 5xx with exponential backoff, and never retry a 4xx other than 429.

Logging

Log the status and code so failures are diagnosable:

log.error({
  msg: 'self_api_error',
  statusCode: err.statusCode,
  code: err.code,
  requestId: err.requestId,
  details: err.details,
});