Skip to main content
The errorCode field is the most reliable signal for programmatic error handling. HTTP status codes indicate broad categories; error codes give you the exact diagnosis.

Error Response Format

All error responses share a common envelope:
{
  "success": false,
  "statusCode": 400,
  "errorCode": "TB_AUTH_101",
  "message": "Invalid API key",
  "fault": "client",
  "retryable": true,
  "suggestedAction": "Verify your API key and try again",
  "reason": "Invalid API key",
  "environment": "sandbox",
  "apiVersion": "1.0.0"
}

Field Reference


Validation Errors

When a request fails field-level validation — for example, submitting a booking without a passenger email — the reason field becomes an array of objects, each describing a specific field that failed:
{
  "success": false,
  "status": "error",
  "statusCode": 400,
  "errorCode": "TB_VAL_302",
  "message": "Validation failed",
  "fault": "client",
  "retryable": true,
  "reason": [
    {
      "field": "email",
      "message": "Required"
    },
    {
      "field": "password",
      "message": "Required"
    }
  ],
  "apiVersion": "1.0.0"
}
Use the reason array to surface field-specific feedback directly in your booking forms. Each object maps precisely to a form field, making inline validation straightforward:
function handleValidationError(error: TravelbaseError) {
  if (error.errorCode === "TB_VAL_302" && Array.isArray(error.reason)) {
    for (const fieldError of error.reason) {
      setFieldError(fieldError.field, fieldError.message);
    }
  }
}

Error Code Catalog

Error codes follow a structured namespace: TB_<CATEGORY>_<CODE>. This makes it easy to handle entire categories with a single prefix check before falling through to specific codes.

Authentication — TB_AUTH_*

Permissions — TB_PERM_*

Validation — TB_VAL_*

Resources — TB_RES_*

System — TB_SYS_*

Rate & Quota Limits — TB_LIM_*

Payments — TB_PAY_*

Payment errors require careful handling. Always check retryable before attempting a retry — retrying a non-retryable payment error can result in multiple authorisation holds on a traveller’s card.

Tenant Status — TENANT_*

These errors apply when your platform operates in a multi-tenant context. They reflect the operational status of the tenant account making the request.

Handling Errors in Code

The following patterns cover the full error handling lifecycle for a Travelbase integration.
import type { TravelbaseError, FieldError } from "@travelbase/sdk";

async function createBooking(payload: BookingPayload) {
try {
return await travelbase.bookings.create(payload);
} catch (err) {
if (!isTravelbaseError(err)) throw err;
return handleTravelbaseError(err);
}
}

function handleTravelbaseError(error: TravelbaseError) {
const { errorCode, retryable, suggestedAction } = error;

// Authentication — token has lapsed mid-session
if (errorCode === "TB_AUTH_102" || errorCode === "TB_AUTH_104") {
return refreshTokenAndRetry();
}

// Validation — surface field-level errors back to the booking form
if (errorCode === "TB_VAL_302" && Array.isArray(error.reason)) {
for (const field of error.reason as FieldError[]) {
setFormError(field.field, field.message);
}
return;
}

// Payment declined — prompt traveller to use another card
if (errorCode === "TB_PAY_800") {
return showPaymentDeclinedModal();
}

// Rate limit — respect the Retry-After header
if (errorCode === "TB_LIM_700") {
const retryAfter = error.headers?.["retry-after"] ?? 60;
return scheduleRetry(retryAfter * 1000);
}

// Tenant issues — operational block, not a code bug
if (errorCode.startsWith("TENANT_")) {
return showTenantStatusBanner(errorCode);
}

// Server-side retryable errors
if (error.fault === "server" && retryable) {
return retryWithBackoff(createBooking, payload);
}

// Non-retryable client errors — log and surface the suggestion
console.error(`[Travelbase] ${errorCode}: ${suggestedAction}`);
throw error;
}

Retry Strategy

Not every error should be retried. Use retryable: true as your gate, then apply exponential backoff to avoid overwhelming the API during degraded conditions.
async function retryWithBackoff<T>(
    fn: () => Promise<T>,
        maxAttempts = 4,
        baseDelayMs = 300
        ): Promise<T> {
            for (let attempt = 1; attempt <= maxAttempts; attempt++) {
            try {
            return await fn();
        } catch (err) {
            const isLast = attempt === maxAttempts;
            const isRetryable = isTravelbaseError(err) && err.retryable;

            if (isLast || !isRetryable) throw err;

            const delay = baseDelayMs * 2 ** (attempt - 1); // 300ms → 600ms → 1.2s → 2.4s
            const jitter = Math.random() * 100;
            await sleep(delay + jitter);
        }
        }
            throw new Error("Unreachable");
        }
Add random jitter (a small random offset on top of the delay) to prevent a thundering herd — many clients retrying in perfect lockstep can amplify an outage rather than recover from it.

Decision Tree

When you receive an error, follow this path to resolution:
1

Check `fault`

"client" means your request needs to change before retrying. "server" means the problem is on Travelbase’s side and may resolve on its own.
2

Check `retryable`

If false, do not retry — fix the request first. If true, proceed to backoff.
3

Match on `errorCode`

Use the catalog above to understand the exact cause. Handle auth, validation, payments, and tenant errors with dedicated logic paths.
4

Use `suggestedAction`

Surface this field to internal support tooling or operations dashboards. It is written for humans, not machines.
5

Escalate if needed

For TB_SYS_600, TN_ISE_901, TB_PAY_802, or persistent TENANT_SUSPENDED, open a support ticket with the full error body and your request ID.

Common Mistakes

MistakeConsequenceFix
Retrying non-retryable errors in a loopDuplicate charges, burnt quota, account lockoutAlways check retryable: false before retrying
Swallowing TB_RES_402 (invalid resource state)Attempting to cancel a departed flight silently failsCheck the booking’s status field before mutating it
Ignoring TENANT_SUSPENDEDAll bookings for affected travellers are silently blockedMonitor for tenant codes and alert your operations team immediately
Using message for programmatic branchingProne to breaking if copy changesAlways branch on errorCode, never on message
Not reading the reason array on TB_VAL_302Generic “something went wrong” shown to travellersMap reason fields directly to your booking form for inline feedback
Retrying TB_PAY_800 automaticallyMultiple authorisation holds on the traveller’s cardPayment declines require human action — prompt for a different payment method

TypeScript Error Code Enum

Copy this enum directly into your project for compile-time safety across all error handling code:
export enum TravelbaseErrorCode {
    // Authentication
    AUTH_MISSING              = "TB_AUTH_100",
    AUTH_INVALID              = "TB_AUTH_101",
    AUTH_EXPIRED              = "TB_AUTH_102",
    AUTH_BLOCKED              = "TB_AUTH_103",
    SESSION_EXPIRED           = "TB_AUTH_104",

    // Permissions
    ACCESS_DENIED             = "TB_PERM_200",
    ACTION_NOT_ALLOWED        = "TB_PERM_201",

    // Validation
    INVALID_INPUT             = "TB_VAL_300",
    FIELD_FORMAT_INVALID      = "TB_VAL_301",
    VALIDATION_ERROR          = "TB_VAL_302",
    DUPLICATE_RESOURCE        = "TB_VAL_303",

    // Resources
    RESOURCE_NOT_FOUND        = "TB_RES_400",
    RESOURCE_EXISTS           = "TB_RES_401",
    RESOURCE_STATE_INVALID    = "TB_RES_402",

    // Bad Request
    BAD_REQUEST               = "TB_BR_403",

    // System
    INTERNAL_ERROR            = "TB_SYS_600",
    UNSUPPORTED_ACTION        = "TB_SYS_602",
    INTERNAL_SERVER_ERROR     = "TN_ISE_901",

    // Limits
    RATE_LIMIT_EXCEEDED       = "TB_LIM_700",
    QUOTA_EXCEEDED            = "TB_LIM_701",
    TOO_MANY_ATTEMPTS         = "TB_LIM_702",

    // Payments
    PAYMENT_FAILED            = "TB_PAY_800",
    PAYMENT_METHOD_UNSUPPORTED = "TB_PAY_801",
    REFUND_FAILED             = "TB_PAY_802",

    // Tenant
    TENANT_PENDING_APPROVAL   = "TENANT_PENDING_APPROVAL",
    TENANT_REJECTED           = "TENANT_REJECTED",
    TENANT_SUSPENDED          = "TENANT_SUSPENDED",
    TENANT_UNAVAILABLE        = "TENANT_UNAVAILABLE",
}