Skip to main content
The ClinikAPI SDK throws typed errors so you can handle different failure modes with precision. Every error class carries a requestId that uniquely identifies the failed request — always log this value so you can correlate client-side errors with server-side traces when filing a support ticket. By default, the SDK retries transient failures automatically using jittered exponential backoff.

Error types

The SDK defines four error classes:
ClassHTTP statusWhen thrown
ClinikValidationError422Request body failed schema validation
ClinikRateLimitError429Rate limit exceeded
ClinikNotFoundError404Resource does not exist
ClinikApiErrorAny otherAll other API errors

Catching errors

import {
  ClinikValidationError,
  ClinikRateLimitError,
  ClinikNotFoundError,
  ClinikApiError,
} from '@clinikapi/sdk';

try {
  const { data } = await clinik.patients.create({ firstName: '', lastName: '' });
} catch (err) {
  if (err instanceof ClinikValidationError) {
    // 422 — request body failed validation
    console.error('Validation issues:', err.issues);
  } else if (err instanceof ClinikRateLimitError) {
    // 429 — rate limit exceeded
    console.error('Retry after:', err.retryAfter, 'seconds');
  } else if (err instanceof ClinikNotFoundError) {
    // 404 — resource not found
    console.error('Not found:', err.message);
  } else if (err instanceof ClinikApiError) {
    // Any other API error
    console.error('Error code:', err.code);
    console.error('HTTP status:', err.status);
    console.error('Request ID:', err.requestId);
  }
}
Use instanceof checks rather than err.name === '...' string comparisons for reliable TypeScript narrowing.

Error properties

All SDK errors extend ClinikApiError and share these base properties:
name
string
The error class name, for example ClinikValidationError.
message
string
Human-readable description of the error. Never contains Protected Health Information.
code
string
Machine-readable error code, for example validation_failed, rate_limit_exceeded, not_found.
status
number
HTTP status code returned by the API.
requestId
string
Unique request identifier. Include this in support tickets so the ClinikAPI team can trace the exact server-side execution.

ClinikValidationError

ClinikValidationError adds an issues array that describes each individual field-level validation failure:
issues
Array
Array of validation issue objects. Each issue has a severity (always error), a code, and a diagnostics message that names the field and constraint.
// Example issues array
[
  {
    severity: 'error',
    code: 'invalid',
    diagnostics: 'firstName: String must contain at least 1 character(s)',
  }
]
Validation error diagnostics describe field names and constraints — never field values. This ensures that PHI is never included in error messages.

ClinikRateLimitError

retryAfter
number
Number of seconds to wait before retrying. The SDK respects this automatically when retries > 0.

Automatic retries

The SDK retries failed requests automatically for:
  • 5xx server errors — transient server-side failures
  • 429 rate-limit responses — waits for retryAfter seconds before retrying
  • Network failures — DNS errors, connection resets, and timeouts
Retry timing uses jittered exponential backoff to prevent thundering-herd behavior when multiple clients encounter failures simultaneously:
Attempt 1: random(0, 2000ms)
Attempt 2: random(0, 4000ms)
Attempt 3: random(0, 8000ms)
Cap: 10,000ms per retry delay

Configuring retries and timeout

const clinik = new Clinik(process.env.CLINIKAPI_SECRET_KEY!, {
  retries: 3,      // default: 2
  timeout: 15000,  // default: 30000ms
});
retries
number
default:"2"
Maximum retry attempts. Set to 0 to disable automatic retries entirely — useful for operations where idempotency is uncertain.
timeout
number
default:"30000"
Request timeout in milliseconds. The SDK cancels requests that exceed this limit and throws a ClinikApiError with code timeout.
Set retries: 0 for non-idempotent operations where retrying could produce duplicate records, such as creating a resource without a client-generated ID to deduplicate on.

Best practices

Follow these patterns to build resilient integrations:
async function createPatientSafely(payload: PatientCreateRequest) {
  try {
    const { data: patient, meta } = await clinik.patients.create(payload);

    // Always log the request ID for traceability
    logger.info({ requestId: meta.requestId }, 'Patient created');

    // Monitor rate limits proactively
    if ((meta.rateLimitRemaining ?? 100) < 10) {
      logger.warn({ rateLimitRemaining: meta.rateLimitRemaining }, 'Approaching rate limit');
    }

    return patient;
  } catch (err) {
    if (err instanceof ClinikValidationError) {
      // Surface field-level errors to the user
      return { error: 'validation', issues: err.issues };
    }

    if (err instanceof ClinikRateLimitError) {
      // The SDK already retried — this means retries are exhausted
      logger.error({ retryAfter: err.retryAfter }, 'Rate limit exhausted after retries');
      throw err;
    }

    // Log the requestId for all other errors
    if (err instanceof ClinikApiError) {
      logger.error({ requestId: err.requestId, code: err.code }, 'API error');
    }

    throw err;
  }
}
  • Always wrap SDK calls in try/catch — do not let unhandled SDK errors reach your users.
  • Log requestId on every error so you can cross-reference with ClinikAPI support.
  • Handle ClinikValidationError separately to present field-level feedback in your UI.
  • Monitor meta.rateLimitRemaining in high-throughput workflows to throttle before hitting the limit.
  • Use retries: 0 for idempotency-sensitive create operations.