> ## Documentation Index
> Fetch the complete documentation index at: https://voyant.travel/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors and transport

> The request, response, and error behavior shared by every Voyant SDK.

The Voyant SDKs share one internal transport layer, so request handling, response parsing, and errors behave the same across Cloud, Connect, and Data.

## Defaults

| Setting     | Default                     |
| ----------- | --------------------------- |
| Base URL    | `https://api.voyant.travel` |
| Auth header | `authorization`             |
| Auth scheme | `Bearer`                    |
| SDK marker  | `x-voyant-sdk: voyant-sdk`  |

## Client options

Every client accepts transport-level options:

* `apiKey` for the bearer token.
* `baseUrl` to point at a different environment.
* `authHeader` and `authScheme` to change how the token is sent.
* `headers` for extra default headers.
* `fetch` to inject a custom fetch implementation.
* `userAgent` to set your own user-agent marker.

```ts theme={null}
const client = createVoyantDataClient({
  apiKey: process.env.VOYANT_API_KEY!,
  baseUrl: "https://api.voyant.travel",
  headers: { "x-tenant": "alpine-tours" },
});
```

## Request behavior

* Query params that are `null` or `undefined` are skipped.
* Arrays are serialized as repeated query params.
* Non-`BodyInit` objects are JSON-encoded automatically.
* `content-type: application/json` is set for JSON request bodies.

## Response behavior

* JSON responses are parsed automatically.
* Plain text that looks like JSON is parsed defensively.
* `{ data: ... }` envelopes are unwrapped by default. Opt out with `unwrapData: false`.

The Data SDK is the exception on unwrapping: it preserves the full `{ data, totalCount, nextCursor? }` envelope on list endpoints and `{ data }` on single-item endpoints, so you keep pagination metadata. See [Voyant Data](/docs/data/overview).

## Errors

Any non-2xx response throws `VoyantApiError`, which carries:

* `status`, the HTTP status code.
* `requestId`, the server-assigned request id.
* `body`, the parsed error body.

```ts theme={null}
import { VoyantApiError } from "@voyant-travel/cloud-sdk";

try {
  await client.sms.sendMessage({ to: "+10000000000", body: "test" });
} catch (error) {
  if (error instanceof VoyantApiError) {
    console.error(error.status, error.requestId, error.body);
  }
  throw error;
}
```

Always preserve the `requestId` in logs and support requests. It is how Voyant traces a specific call.

## Data API error codes

The Data API additionally returns a stable `code` on its error bodies, typed as `DataErrorCode`:

`NOT_FOUND`, `INVALID_REQUEST`, `INVALID_CURSOR`, `UPSTREAM_FAILED`, `INTERNAL_AUTH_REQUIRED`, `RATE_LIMITED`.

Branch on `code` rather than parsing error messages.

## Idempotency

Where a write can be safely retried, the SDK exposes an idempotency key. Connect's `bookings.create` accepts `{ idempotencyKey }`, sent as the `Idempotency-Key` header and deduplicated server-side. Pass a stable key per logical attempt so retries do not create duplicates. See [Connect SDK](/docs/connect/connect-sdk).
