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

# Hybrid: model keys and Bedrock

> Add or change LLM provider keys on a Hybrid install, set up Amazon Bedrock with IAM or a key, and name keys by environment variable.

Agents on a Hybrid install never use xpander's keys. Keys live in the organization (**Platform > AI governance > Models**) or on the install (`secrets.static`). This page covers the install side.

## Managing LLM API keys

Use `helm upgrade` with `--reset-then-reuse-values` (Helm 3.14 or later) to add or change API keys: it keeps the values you set and applies the chart's defaults for everything else:

```bash Single Key theme={"dark"}
helm upgrade xpander xpander/xpander \
  --namespace xpander --reset-then-reuse-values \
  --set secrets.static.anthropicApiKey=<KEY> \
  --timeout 5m
```

```bash Multiple Keys theme={"dark"}
helm upgrade xpander xpander/xpander \
  --namespace xpander --reset-then-reuse-values \
  --set secrets.static.anthropicApiKey=<KEY> \
  --set secrets.static.agentsOpenaiApiKey=<KEY> \
  --set secrets.static.fireworksApiKey=<KEY> \
  --timeout 5m
```

After the upgrade, pods that use the updated secrets will automatically restart. Verify with `kubectl get pods -n xpander`.

The `xpander-static` secret has a Helm resource keep policy, so `helm upgrade` may not update it on subsequent installs. If a key isn't picked up, patch the secret directly: see [API key not being picked up after helm upgrade](https://docs.xpander.ai/self-hosted/hybrid-troubleshooting.md#api-keys).

### Secret field names

> [accordion] Helm value to secret field mapping
>
> | Helm Value | Secret Field |
> | - | - |
> | `secrets.static.anthropicApiKey` | `ANTHROPIC_API_KEY` |
> | `secrets.static.agentsOpenaiApiKey` | `AGENTS_OPENAI_API_KEY` |
> | `secrets.static.fireworksApiKey` | `FIREWORKS_API_KEY` |
> | `secrets.static.awsBedrockApiKey` | `AWS_BEARER_TOKEN_BEDROCK` |
> | `secrets.static.googleApiKey` | `GOOGLE_API_KEY` |
> | `secrets.static.nebiusApiKey` | `NEBIUS_API_KEY` |
> | `secrets.static.heliconeApiKey` | `HELICONE_API_KEY` |
> | `secrets.static.openrouterApiKey` | `OPENROUTER_API_KEY` |
> | `secrets.static.deploymentManagerApiKey` | `DEPLOYMENT_MANAGER_API_KEY` |

### Using Kubernetes Secrets (recommended for production)

```bash theme={"dark"}
kubectl create secret generic ai-service-keys \
  --namespace xpander \
  --from-literal=openai-api-key=sk-your-openai-key \
  --from-literal=anthropic-api-key=sk-ant-your-anthropic-key
```

```yaml theme={"dark"}
# In your values file
agent-worker:
  envFromSecretKeys:
    AGENTS_OPENAI_API_KEY:
      secretName: "ai-service-keys"
      key: "openai-api-key"
    ANTHROPIC_API_KEY:
      secretName: "ai-service-keys"
      key: "anthropic-api-key"
```

### Your own keys and credits

Keys can also live in the product: per provider under **Platform > AI governance > Models**, or as an agent-level credential (checked first). A call on a key you brought is **never charged credits**, but xpander still measures it, so budgets and usage stay accurate. Claude Code, Codex and OpenCode turns follow the same rule.

## Amazon Bedrock: keyless IAM or API key

Bedrock calls take one of two paths, each with its own credentials:

| Path | Covers | Credentials |
| - | - | - |
| Chat and agent tasks | Chat, agent task executions and the summaries around them | `--bedrock-auth iam` (keyless, the pods' AWS identity) or `--bedrock-auth key` (a bearer token) |
| Claude Code on Bedrock | The CLI's model calls, straight to the in-cluster LLM Gateway, which gets the Bedrock credential from the Agent Controller | The controller's own AWS auth, or an organization Bedrock key under **Platform > AI governance > Models** |

**Chat and agent tasks.** `--bedrock-auth iam` signs calls with the pods' AWS identity (EKS Pod Identity or IRSA), in images built after 27 August 2026. The role needs the Bedrock permissions itself (`bedrock:InvokeModel`, `bedrock:InvokeModelWithResponseStream`, `bedrock:Converse`, `bedrock:ConverseStream` on `*`) and must be bound to both ServiceAccounts the LLM services run as: `default` and `agent-controller`. The chat and task paths do not assume a second role.

`--bedrock-auth key` (default when `--bedrock-key` is given) stores a bearer token. Either way, the account must be opted in to Bedrock's data retention mode for your models. The "IAM role" method under **Platform > AI governance > Models** covers only the Claude Code path below. If chat fails with a Bedrock entry set that way, remove it and restart the `agent-controller`, `agent-worker`, `agents`, `actions` and `functions` deployments.

**Claude Code on Bedrock.** The CLI calls the in-cluster LLM Gateway directly, and the gateway gets the Bedrock credential from the Agent Controller. The controller resolves AWS auth itself, in this order: `AWS_BEARER_TOKEN_BEDROCK`, then `BEDROCK_ASSUME_ROLE_ARN` (STS, self-refreshing), then static keys, then the pod's default credential chain (Pod Identity / IRSA). Set whichever you use as controller environment through the chart's env values, with `BEDROCK_REGION` or `AWS_REGION` next to it. An organization Bedrock key under **Platform > AI governance > Models** is preferred over the controller's credential.

> [!WARNING]
>
> **Fable- and Mythos-class Anthropic models on Bedrock need the AWS account's data-retention opt-in, per region.** With the default mode Bedrock answers `400 data retention mode 'default' is not available for this model`. There is no console switch: `PUT https://bedrock.<region>.amazonaws.com/data-retention` with `{"mode": "provider_data_share"}` (or `aws_review` where offered), SigV4-signed with `bedrock:PutAccountDataRetention`, in every region a `global.` inference profile can route to, on whichever account signs the call (the controller's, or the organization's own Bedrock key account). Details on [AI vendors](https://docs.xpander.ai/run/llm-gateway/providers-and-keys.md#amazon-bedrock).

## Key names read from an environment variable

An agent, a custom model provider or the organization's default model can name its API key by an environment variable on agent-controller (`agent-controller.env` or `global.env`, for example `MY_VLLM_TOKEN`). Such a name never reads one of the install's own credentials. Names that match the reserved list below read as no key.

The standard provider names (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `AWS_BEARER_TOKEN_BEDROCK`, `GOOGLE_API_KEY`, `AZURE_OPENAI_API_KEY`, `OPENROUTER_API_KEY` and the other provider keys) keep working, and so does any other name, such as `VLLM_API_KEY`. Rename a name that matches the list (for example `VLLM_SECRET` to `VLLM_API_KEY`) in both places: the agent-controller env that carries the value and the setting that names it.

> [accordion] Reserved names that read as no key
>
> * names starting with `AWS_` (except `AWS_BEARER_TOKEN_BEDROCK`), `XPANDER_`, `FLEET_`, `LLM_GW`, `LLM_GATEWAY`, `LITELLM_`, `GITHUB_APP`, `SUPABASE`, `REDIS`, `OAUTH`, `JWT`, `LICENSE`, `MONGO`, `POSTGRES`, `PG_`, `ON_PREM_PG`, `SMTP`, `REGISTRY`, `SANDBOX_`, `HARNESS_` or `KUBE`
> * names ending with `_SERVICE_API_KEY`, `_PASSWORD`, `_PRIVATE_KEY`, `_SECRET`, `_SECRET_KEY`, `_SESSION_TOKEN` or `_TOKEN_FILE`
> * the platform's own service keys (`DEPLOYMENT_MANAGER_API_KEY`, `AGENT_CONTROLLER_API_KEY` and the like)

**Model endpoints on metadata addresses are refused.** This applies to a model endpoint set on an agent, a custom provider, the organization's default model or an Azure key. xpander refuses it when it resolves to a metadata or link-local address (`169.254.0.0/16`, `fe80::/10`, the cloud metadata hosts). The check runs when the endpoint is saved and again on every call, against the address actually dialled. Private addresses (a vLLM or model gateway on your own network) keep working.


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