SUQODocs
User GuideAPI ReferenceSDKs

Handle errors

The SuqoError hierarchy in the SUQO TypeScript SDK.

Every error the SDK throws is an instance of SuqoError. The base class itself is never thrown directly — always one of the subclasses below. Check instanceof, never the error's message string or the raw response shape — those aren't part of the contract and can change wording without notice.

import { SuqoError } from "@suqo/sdk";

try {
  await suqo.subscriptions.create({ /* ... */ });
} catch (err) {
  if (err instanceof SuqoError) {
    console.error(err.name, err.message, err.status);
  }
}

The hierarchy

ClassWhenNotes
SuqoConfigErrorConstructing SuqoClient with a malformed key, or a baseUrl that disagrees with itThrown synchronously, before any request — see Authentication
AuthenticationError401Key is missing, malformed, or inactive
KycRequiredError403Seller hasn't completed KYC. Carries kycStatus, if the response included one
ValidationError400See below — two different body shapes, normalized into one class
NotFoundError404Resource doesn't exist, or doesn't belong to your account
RateLimitError429Reserved — see Rate Limiting; never thrown until the API enforces limits
ServerError5xx (and any unmapped status)Unexpected failure on SUQO's side
NetworkErrorNo response at allCovers both a genuine network failure and a timeout — there's no separate timeout class

Every subclass carries whatever the base class does:

interface SuqoErrorOptions {
  status?: number; // HTTP status, if there was a response
  rawBody?: unknown; // the raw parsed response body, if any
  requestId?: string; // SUQO's request id for this call, if the response included one
}

ValidationError — two wire shapes, one class

A 400 comes back in one of two shapes on the wire; the SDK normalizes both so you only ever need instanceof ValidationError, never a shape check of your own:

import { ValidationError } from "@suqo/sdk";

try {
  await suqo.subscriptions.create({ /* ... */ });
} catch (err) {
  if (err instanceof ValidationError) {
    if (Object.keys(err.fieldErrors).length > 0) {
      // field-keyed body — e.g. { phone: ["This field is required."] }
      console.error(err.fieldErrors);
    } else {
      // detail-shaped body instead — e.g. the duplicate-active-subscription case
      console.error(err.message);
    }
  }
}

fieldErrors is always a plain object (never undefined) — empty when the failure used the detail shape instead. Nested validation (e.g. on subscriptions.create()'s customer payload) comes through as dot-path keys like customer.phone, already renamed from the wire's client.phone.

Writes aren't automatically retried

Reads (list, retrieve) retry on NetworkError, 429, and 5xx with backoff. Writes (create, cancel, updateBillingCycle, resume) never retry automatically — a blindly-retried write could double-act (e.g. a duplicate subscription) since the API has no idempotency-key support yet. See Idempotency for what changes once it does, and Rate Limiting for RateLimitError's retryAfter field once 429 responses start arriving.

On this page