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

# Configuration

> How the SDK resolves endpoints and retries: environment variables and sail.Config

Most applications configure the SDKs entirely through environment variables.
Set `SAIL_API_KEY` (or run `sail auth login` once) and you're done; the
endpoint variables below matter only for custom or self-hosted deployments.
Every SDK reads the same variables and the same credential store.

Use `export SAIL_API_KEY=sk_...` for SDK scripts, CI jobs, and background
agents. If `SAIL_API_KEY` is unset, the SDKs fall back to the credential
`sail auth login` stores under `~/.sail`. The environment variable always wins.

## Endpoint overrides

Each of these overrides one endpoint, for custom or self-hosted stacks:

* `SAIL_API_URL`: the main Sail API. Default
  `https://api.sailresearch.com`.
* `SAILBOX_API_URL`: the Sailbox API. Default
  `https://sailbox-api.sailresearch.com`.
* `SAILBOX_INGRESS_URL`: the base URL used to build an exposed listener's
  public address when the service does not return one.

Configuration is read when a client is created. To pick up changed variables
in a long-lived process: in Python, call `sail.reset_transports()` (the SDK
resolves once per process); in TypeScript, construct a new client with
`Client.fromEnv()` and repoint the object model with `setDefaultClient`; in
Rust, construct a new client with `Client::from_env()`.

## Worker threads

The Python and TypeScript SDKs run their network calls on a small pool of
background threads shared by the whole process, and the Rust SDK uses the same
pool for its blocking calls. The pool is sized to the machine: one thread per
CPU, with at least 2 and at most 8. That is plenty for most applications,
including ones that drive many Sailboxes concurrently, because the threads
spend nearly all of their time waiting on the network.

Rust code that awaits the async API does not use this pool. Those calls run on
your application's own async runtime, so the setting below does not affect
them.

Set `SAIL_RUNTIME_THREADS` to override the pool size, for example to give a
large fan-out workload more headroom or to keep a constrained process at
exactly one thread. Values from 1 to 256 are accepted; anything else is
ignored and the default applies. The variable is read once per process, when
the SDK first performs work, so set it before your program starts using the
SDK.

## Inspecting the configuration

Each SDK exposes the configuration it resolved from the environment:

<CodeGroup>
  ```python Python theme={null}
  import sail

  config = sail.Config.from_env()
  print(config.api_url)  # https://api.sailresearch.com
  ```

  ```typescript TypeScript theme={null}
  import { resolveConfig } from "@sailresearch/sdk";

  const config = resolveConfig();
  console.log(config.apiUrl); // https://api.sailresearch.com
  ```

  ```rust Rust theme={null}
  use sail::Client;

  let client = Client::from_env()?;
  println!("{}", client.config().api_url); // https://api.sailresearch.com
  ```
</CodeGroup>

### Python constructors

```python theme={null}
@classmethod
def from_env() -> Config

@classmethod
def from_env_optional_api_key() -> Config
```

* `Config.from_env()` resolves the configuration and **requires** an API key
  (from `SAIL_API_KEY` or the stored `sail auth login` credential), raising
  `ValueError` if none is found.
* `Config.from_env_optional_api_key()` is the same, but does not require an API
  key. Use it when you want a `Config` without a key, for example so
  `sail.voyage` can run in no-op mode.

<Note>
  For the Voyage and agent attribution environment variables, see the [Voyages
  environment table](/voyages-sdk#environment-variables).
</Note>

## Retries

The SDKs retry transient failures automatically. By default they make up to 3
attempts with exponential backoff and full jitter, honoring a server
`Retry-After` when one is present:

* **502 / 503 / 504** are retried.
* **429** is retried only when the response includes a valid `Retry-After`.
* **500** and other statuses are surfaced immediately, without a retry.

Mutations that are not safe to repeat skip these retries and make a single
attempt. `exec` retries transient failures against a waking or migrating
Sailbox for up to ten minutes, reusing its idempotency key so a retried
launch still runs the command once.
