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

# Keys and subscriptions

> Run the harnesses on your own keys and subscriptions, or on the keys of your users. DeepSeek, Amazon Bedrock and Mistral setup.

The harness reads its credentials from the environment and the auth files of the sandbox. Boat fills them from one of two sources:

| Source | How | Who sees the keys |
| - | - | - |
| **Your account** | Connect keys or subscriptions on the [Agents dashboard](https://boat.dev/dashboard?tab=agents) one time. Every sandbox gets them | You, on your own sandboxes |
| **Per sandbox, from your user** | `boat new --no-env -e ANTHROPIC_API_KEY=… -e OPENAI_API_KEY=…` (API: `noEnv: true` plus `env`) | Only that sandbox. Your account keys never go on it |

<Warning>
  Your account keys go on a sandbox only if its [environment](/environments) lets them in. If you chose the platform option at onboarding (the preselected option), your first environment is **Safe for third parties**. This setting keeps every key out.

  If a prompt fails with "no credential" but the Agents tab shows you as connected, do these steps:

  1. Open [Dashboard > Environment](https://boat.dev/dashboard?tab=environment).
  2. Turn off **Safe for third parties**.
  3. Keep **Agents credentials** on.

  New sandboxes get the change at once. For running sandboxes, press the upgrade button on that page. The Agents tab warns you when an environment blocks your keys.
</Warning>

## Connect a subscription

The Agents dashboard connects subscriptions the same way for all four vendors:

| Subscription | Plans |
| - | - |
| **Claude** | Pro or Max |
| **ChatGPT** | Plus, Pro or Team |
| **Kimi** | Kimi Code |
| **Mistral** | Le Chat Pro or Team, for Mistral Vibe |

1. Click **Sign in**.
2. Approve on the page of the vendor. ChatGPT and Kimi show a short code to confirm. Mistral needs no code.
3. The dashboard shows the subscription as connected.

The Mistral sign-in gives Boat a Mistral API key. This key bills against the Vibe quota of your plan, not against paid API credits. It does not expire.

Boat keeps the sign-in alive for you. The server refreshes the token before every prompt and every sandbox start. So a sandbox never starts on an expired token.

To remove a subscription from every sandbox, click **Sign out** on the same row.

## Use the keys of your users

`--no-env` keeps every one of your credentials out of the sandbox. The `-e` values are the only credentials the sandbox has. Use this for a product where each end user brings their own key or subscription and picks a harness in a selector:

```mermaid theme={null}
sequenceDiagram
  participant U as Your user
  participant App as Your app
  participant API as Boat API
  U->>App: pastes key, picks Claude Code
  App->>API: POST /sandboxes {noEnv: true, env: {ANTHROPIC_API_KEY}}
  API-->>App: sandbox id
  App->>API: POST /prompt {provider: "claude", prompt}
  API-->>App: events (streamed)
  App-->>U: live output
```

Keys are per sandbox, not per conversation. A sandbox that several users share runs on one set of credentials.

* When the keys of each paying user must stay apart, give each user their own sandbox.
* When the keys are yours, share one sandbox across conversations.

## Environment variables

The harnesses read these variables. Any subset works. If the vendor key of a harness is missing, that harness refuses the prompt with a credential error. The other harnesses keep working.

| Variable | Used by |
| - | - |
| `ANTHROPIC_API_KEY` | Claude Code, pi, OpenCode, Prime Agent |
| `OPENAI_API_KEY` | Codex, pi, OpenCode, Prime Agent |
| `OPENROUTER_API_KEY` | pi, OpenCode, Prime Agent |
| `LLMGATEWAY_API_KEY` | pi, OpenCode, Prime Agent |
| `CLAUDE_CODE_OAUTH_TOKEN` | Claude Code. A Claude Pro or Max subscription token from `claude setup-token` |
| `MOONSHOT_API_KEY` | Kimi Code. A Kimi open platform key (api.moonshot.ai) |
| `MISTRAL_API_KEY` | Mistral Vibe, pi, OpenCode, Prime Agent. A [Mistral API key](https://console.mistral.ai/api-keys), or the key the Mistral Vibe sign-in made |
| `KIMI_CODE_ACCESS_TOKEN`, `KIMI_CODE_REFRESH_TOKEN` | Kimi Code. A Kimi Code subscription token pair: the `access_token` and `refresh_token` that `kimi login` stores in `~/.kimi-code/credentials/kimi-code.json` |
| `DEEPSEEK_API_KEY` | Claude Code and Codex. A [DeepSeek platform key](https://platform.deepseek.com/api_keys). Read [DeepSeek](#deepseek) |
| `AWS_BEARER_TOKEN_BEDROCK` + `AWS_REGION` | Claude Code (with `CLAUDE_CODE_USE_BEDROCK=1`) and Codex (with `OPENAI_BASE_URL`). Read [Amazon Bedrock](#amazon-bedrock) |

Kimi Code does not read a credential from the environment itself. Before the first prompt, the sandbox writes these values into `~/.kimi-code/config.toml` and the token file. After that, the CLI refreshes the subscription token by itself.

## DeepSeek

Claude Code and Codex can run on the models of DeepSeek instead of Anthropic or OpenAI. One credential covers both: a [DeepSeek platform key](https://platform.deepseek.com/api_keys). It is a single string that starts with `sk-`.

DeepSeek publishes an Anthropic-shaped endpoint and an OpenAI-shaped endpoint for the same key. So Boat does not change either harness, and neither harness needs a gateway in front of it.

| Source | How |
| - | - |
| **Your account** | Agents dashboard, the DeepSeek API key row. Paste the key one time. Then point Claude Code or Codex (or both) at it and pick a DeepSeek model |
| **Per sandbox, from your user** | `boat new --no-env -e DEEPSEEK_API_KEY=sk-…`. This one variable sets up both harnesses. If you also pass the key of another vendor, that harness keeps its own credential |

What the sandbox gives each harness, and the model ids to pass:

| Harness | Environment | Model ids |
| - | - | - |
| Claude Code | `ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic`, `ANTHROPIC_AUTH_TOKEN` = the key, `ANTHROPIC_API_KEY` empty. If it is not empty, Claude Code sends an `x-api-key` header that the endpoint rejects | `deepseek-flash`, `deepseek-v4-pro`. DeepSeek also maps the Claude model names onto these. So the small, fast and subagent models of the harness work by themselves |
| Codex | `OPENAI_BASE_URL=https://api.deepseek.com/v1` and the key. Codex ignores `OPENAI_BASE_URL` itself, so the agent server writes a `model_providers.deepseek` block that points at `https://api.deepseek.com/` with `wire_api = "responses"`. Web search is off | `deepseek-flash`, `deepseek-v4-pro`. Reasoning levels are `low` and `high`, the two levels that both Codex and DeepSeek accept |

<CodeGroup>
  ```bash CLI theme={null}
  boat new --no-env -e DEEPSEEK_API_KEY=sk-…
  boat prompt --provider claude --model deepseek-flash "Summarize this repo"
  boat prompt --provider codex --model deepseek-v4-pro --reasoning-effort high "Now review the diff"
  ```

  ```bash curl theme={null}
  curl -sS -X POST "$BOAT_API_BASE/sandboxes" \
    -H "Authorization: Bearer $BOAT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"noEnv":true,"env":{"DEEPSEEK_API_KEY":"sk-…"}}'
  curl -sS -X POST "$BOAT_API_BASE/sandboxes/$BOAT_ID/prompt" \
    -H "Authorization: Bearer $BOAT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"provider":"claude","model":"deepseek-flash","prompt":"Summarize this repo"}'
  ```

  ```ts TypeScript theme={null}
  const created = await sandbox.create({ noEnv: true, env: { DEEPSEEK_API_KEY: "sk-…" } });
  await sandbox.prompt({
    sandboxId: created.sandbox.id,
    provider: "claude", model: "deepseek-flash", prompt: "Summarize this repo",
  });
  ```

  ```python Python theme={null}
  created = sandbox.create(CreateSandboxRequest(no_env=True, env={"DEEPSEEK_API_KEY": "sk-…"}))
  sandbox.prompt(created.sandbox.id, PromptRequest(provider="claude", model="deepseek-flash", prompt="Summarize this repo"))
  ```
</CodeGroup>

pi, OpenCode and Prime Agent get DeepSeek in a different way. They use your OpenRouter key, with no DeepSeek account. They need only `OPENROUTER_API_KEY`. The models are:

* `openrouter:deepseek/deepseek-v4.1-flash`
* `openrouter:deepseek/deepseek-v4-flash-0731`
* `openrouter:deepseek/deepseek-v4-pro-0813`

## Amazon Bedrock

Claude Code and Codex can run on models that your AWS account serves, instead of the Anthropic or OpenAI API.

The simplest credential is a [Bedrock API key](https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys.html):

* It is one string that starts with `ABSK`.
* You make it in the Bedrock console, under API keys.
* It is valid in every region.

IAM access keys also work: access key id, secret, and an optional session token.

| Source | How |
| - | - |
| **Your account** | Agents dashboard, the Bedrock row under Claude Code or Codex. Set the region, then the API key or the IAM keys. Point the harness at it and pick a Bedrock model |
| **Per sandbox, from your user** | `boat new --no-env -e AWS_BEARER_TOKEN_BEDROCK=ABSK… -e AWS_REGION=us-east-1 -e CLAUDE_CODE_USE_BEDROCK=1` |

### Claude Code on Bedrock

The sandbox gives Claude Code `CLAUDE_CODE_USE_BEDROCK=1`, `AWS_REGION`, and the key (or `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`).

The model ids are Bedrock cross-region inference profiles on the Anthropic line:

* `us.anthropic.claude-sonnet-4-6`
* `us.anthropic.claude-opus-4-6-v1`
* `us.anthropic.claude-sonnet-4-5-20250929-v1:0`
* `us.anthropic.claude-opus-4-5-20251101-v1:0`
* `us.anthropic.claude-haiku-4-5-20251001-v1:0`
* `us.anthropic.claude-sonnet-5`
* `us.anthropic.claude-opus-5`
* `us.anthropic.claude-fable-5-1`
* `us.anthropic.claude-fable-5`
* `us.anthropic.claude-opus-4-8`
* `us.anthropic.claude-opus-4-7`

When you use the Bedrock credential, these are the only Claude Code models that Boat offers. The default becomes Sonnet 4.6. The reason: inside Claude Code, the plain `sonnet` and `opus` aliases point to model ids that many AWS accounts have not enabled.

### Codex on Bedrock

The sandbox gives Codex `OPENAI_BASE_URL=https://bedrock-mantle.<region>.api.aws/v1` and the key. Codex ignores `OPENAI_BASE_URL` itself. So the agent server selects the built-in `amazon-bedrock` provider of Codex for that region. It uses the OpenAI-compatible [Mantle endpoint](https://docs.aws.amazon.com/bedrock/latest/userguide/bedrock-mantle.html) of Bedrock. Web search is off.

The model ids are the OpenAI line on Mantle:

* `openai.gpt-5.6-terra`
* `openai.gpt-5.6-sol`
* `openai.gpt-5.6-luna`
* `openai.gpt-5.5`
* `openai.gpt-5.5-2026-04-23`
* `openai.gpt-5.4`
* `openai.gpt-5.4-2026-03-05`

Codex on Bedrock needs the API key. IAM keys alone do not authenticate the OpenAI-compatible endpoint.

The list does not include the open-weight `gpt-oss` models. It also does not include the third-party models on Mantle (Kimi, MiniMax, Qwen, DeepSeek, GLM, Mistral). These models answer the `/v1` routes of Mantle, but not the `/openai/v1/responses` route that Codex calls.

### Example

<CodeGroup>
  ```bash CLI theme={null}
  boat new --no-env -e AWS_BEARER_TOKEN_BEDROCK=ABSK… -e AWS_REGION=us-east-1 -e CLAUDE_CODE_USE_BEDROCK=1
  boat prompt --provider claude --model us.anthropic.claude-sonnet-4-6 "Summarize this repo"
  ```

  ```bash curl theme={null}
  curl -sS -X POST "$BOAT_API_BASE/sandboxes" \
    -H "Authorization: Bearer $BOAT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"noEnv":true,"env":{"AWS_BEARER_TOKEN_BEDROCK":"ABSK…","AWS_REGION":"us-east-1","CLAUDE_CODE_USE_BEDROCK":"1"}}'
  curl -sS -X POST "$BOAT_API_BASE/sandboxes/$BOAT_ID/prompt" \
    -H "Authorization: Bearer $BOAT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"provider":"claude","model":"us.anthropic.claude-sonnet-4-6","prompt":"Summarize this repo"}'
  ```

  ```ts TypeScript theme={null}
  const created = await sandbox.create({
    noEnv: true, env: { AWS_BEARER_TOKEN_BEDROCK: "ABSK…", AWS_REGION: "us-east-1", CLAUDE_CODE_USE_BEDROCK: "1" },
  });
  await sandbox.prompt({
    sandboxId: created.sandbox.id,
    provider: "claude", model: "us.anthropic.claude-sonnet-4-6", prompt: "Summarize this repo",
  });
  ```

  ```python Python theme={null}
  created = sandbox.create(CreateSandboxRequest(no_env=True, env={"AWS_BEARER_TOKEN_BEDROCK": "ABSK…", "AWS_REGION": "us-east-1", "CLAUDE_CODE_USE_BEDROCK": "1"}))
  sandbox.prompt(created.sandbox.id, PromptRequest(provider="claude", model="us.anthropic.claude-sonnet-4-6", prompt="Summarize this repo"))
  ```
</CodeGroup>

### Model access is granted per AWS account and region

The lists above show what Amazon Bedrock offers for each harness. They do not show what your account can call today. Bedrock grants model access per AWS account and per region. Boat lists a model as soon as Bedrock serves it, including frontier models that AWS has not granted to you yet. Your access to a model is between your account and AWS.

To request access:

1. Open the [Amazon Bedrock console](https://console.aws.amazon.com/bedrock/) in the region that your credential uses.
2. Go to **Model catalog**.
3. Find the model.
4. Choose **Request model access**.

Some models ask for a short use case form. AWS approves these, so the approval is not instant. Access in one region does not carry to another region.

If you pick a model that your account cannot use, the prompt fails. The error names the model, the reason and the fix. For example:

```
Amazon Bedrock has not granted this AWS account access to anthropic.claude-opus-5. Bedrock grants
model access per AWS account and per region, so a model in the sandbox catalog still has to be enabled
on your side: open the Amazon Bedrock console in the region this sandbox's credential uses, find
anthropic.claude-opus-5 in the Model catalog, request access, and retry once AWS approves it. Pick a
model you already have access to in the meantime.
```

Two related failures show different errors:

| Cause | What you see |
| - | - |
| AWS rejects the key | An authentication error. The harness names the endpoint it called |
| The region prefix of the model id does not match the region of your credential | `The provided model identifier is invalid` |

The `us.` profiles above work only from US regions. From `eu-west-1`, pass `eu.anthropic.claude-sonnet-4-6` instead. The same applies to `au.` and `jp.`. A `global.` prefix works from any supported source region. It needs a wider IAM policy. Read [global cross-region inference](https://docs.aws.amazon.com/bedrock/latest/userguide/global-cross-region-inference.html).

## Mistral

Mistral Vibe runs the models of Mistral. pi, OpenCode and Prime Agent run the same models on the same credential. They also run them through OpenRouter or LLM Gateway, on those keys.

| Source | How |
| - | - |
| **Your account** | Agents dashboard: the Mistral Vibe subscription row (Sign in) or the Mistral API key row |
| **Per sandbox, from your user** | `boat new --no-env -e MISTRAL_API_KEY=…` |

| Harness | Model ids |
| - | - |
| Mistral Vibe | `mistral-large-4` (default), `mistral-medium-3.5`, `mistral-small-4`. Reasoning levels are `none` and `high`, the two levels the Mistral API accepts |
| pi, OpenCode, Prime Agent | The same ids plus `mistral-large-3` on the Mistral credential. `openrouter:mistralai/mistral-large-4-0` and the other OpenRouter ids on an OpenRouter key. `llmgateway:mistral-large-4` and the other LLM Gateway ids on an LLM Gateway key |

When both Mistral credentials are on, Boat uses the subscription key. So usage goes to the Vibe quota of your plan first.

```bash theme={null}
boat prompt --provider mistral "Summarize this repo"
boat prompt --provider pi --model mistral-large-4 "Now review the diff"
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.