Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.travelbase.ai/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Travelbase removes all guesswork from retry logic. Every error response includes a retryable flag and a fault field — your code never needs to maintain a list of retryable status codes or infer blame from HTTP semantics.

retryable

A boolean telling you directly whether this exact error is safe to retry. If false, retrying the same request will produce the same error.

fault

"server" means Travelbase caused the failure. "client" means something in your request needs to be fixed before retrying.

Error Response Shape

Every error from the Travelbase API follows this consistent envelope. The retryable, errorCode, fault fields are your source of truth for retry decisions — not the HTTP status code.
{
        "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"
    }
Always drive retry decisions from retryable and fault — not the HTTP status code. The status code is a useful hint, but these two fields are the source of truth.

Decision Matrix

Use retryable and fault together to decide your next move.
retryablefaultWhat to do
true"server"Back off and retry — Travelbase had a transient fault.
true"client"Wait for the condition to clear (e.g. rate limit), then retry.
false"server"Do not retry. Contact support — this is an unexpected server-side error.
false"client"Fix your request (auth, validation, missing resource) and resubmit a corrected payload.

Backoff Strategy

For every retryable: true response, use exponential backoff with jitter. Always honour the Retry-After header when it is present.
async function requestWithRetry(fn, maxAttempts = 5) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    const res = await fn();

    if (res.success) return res;

    const { retryable } = res.error;
    if (!retryable || attempt === maxAttempts) throw res.error;

    const retryAfter = res.headers?.["retry-after"];
    const base = retryAfter
      ? retryAfter * 1000
      : Math.min(Math.pow(2, attempt) * 1000, 30_000); // 30s ceiling
    const jitter = Math.random() * 1000;

    await new Promise((resolve) => setTimeout(resolve, base + jitter));
  }
}
The retry condition collapses to a single field check — if (!retryable). Do not layer your own status code overrides on top; trust the field and keep the logic simple.

Idempotency Keys

retryable: true on a mutating request (POST, PATCH, DELETE) is only safe to act on when you include an Idempotency-Key. Without one, a retry may create duplicates or trigger the same side-effect twice.
curl -X POST "https://api.travelbase.ai/v1/bookings" \
  -H "Authorization: Bearer tb_live_xxxx" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{ ... }'
Generate the idempotency key once before your first attempt and reuse the same value on every retry. A new key on each attempt is treated as a brand-new operation — duplicates will be created.

Retry Limits

Max Attempts

Cap at 5 attempts per request. After that, surface the error to the caller rather than retrying indefinitely.

Max Backoff

Clamp the delay to a 30-second ceiling to prevent exponential growth from producing multi-minute waits.

Total Budget

Set a 2-minute total timeout across all attempts. Abort the entire operation once exceeded, regardless of attempt count.