# Providers

Pi supports subscription-based providers via OAuth and API key providers via environment variables or auth file. For each provider, pi knows all available models. The list is updated with every pi release.

## Table of Contents

- [Subscriptions](#subscriptions)
- [API Keys](#api-keys)
- [Auth File](#auth-file)
- [Cloud Providers](#cloud-providers)
- [Custom Providers](#custom-providers)
- [Resolution Order](#resolution-order)

## Subscriptions

Use `/login` in interactive mode, then select a provider, or pass one directly (for example `/login chatgpt` or `/login claude`):

- ChatGPT Plus/Pro (Codex)
- Claude Pro/Max
- GitHub Copilot
- xAI (Grok/X subscription) — `/login xai`. Signs in with SuperGrok or X Premium via device-code OAuth; `XAI_API_KEY` remains available as an API key under [API Keys](#api-keys) for metered access instead.
- Kimi Code (subscription) — `/login kimi-coding`.
- OpenRouter — `/login openrouter`. This OAuth flow exchanges a browser sign-in for a permanent OpenRouter API key rather than a metered/subscription credential, so it does not get the subscription-hop failover behavior described below.

Use `/logout` to clear credentials. Tokens are stored per provider in `~/.pi/agent/auth.json` and auto-refresh when expired.

If a subscription provider reports a terminal usage limit, Pi stops retrying that exhausted model, announces the condition, and switches the session to that provider's default model when it is authenticated and not already exhausted. API-key/metered quota failures do not auto-hop; Pi halts the turn and asks you to switch models, wait for the limit window, or resend explicitly.

## Provider Failure Evidence Corpus

Pi records local provider failures to `~/.pi/agent/state/failure-corpus.jsonl` so unclassified provider messages become reviewable evidence. Records contain `{ts, provider, modelId, reason, retryable, message}`; messages are truncated to 500 characters and redacted for `sk-...` keys, `Bearer ...` tokens, and long base64-like secrets. The file stays local, is never uploaded by pi, and is rotated to 512 KB / newest 1,000 records. You may delete it at any time.

Evidence grades for adding future provider signatures:

- Grade A: executable SDK fixture that constructs the vendor SDK error and classifies its actual `.message`.
- Grade B: redacted corpus capture from real traffic.
- Grade C: literal adapter throw-site in pi source.

Promotion procedure: evidence → provider signature row with source/grade → paired fixture tests with and without provider context → red-team ledger closure. Documentation memory alone is not admissible evidence.

## Running Multiple Providers at Once

Pi does not keep a single global active provider for running agents. Each Pi process/session has its own selected model, and `auth.json` can hold credentials for many providers at the same time. After logging in to both subscription providers, start separate terminals with explicit provider or model selection:

```bash
# ChatGPT Plus/Pro subscription via Codex
pi --provider openai-codex --name chatgpt-worker
# equivalent friendly alias:
pi --provider chatgpt --name chatgpt-worker

# Claude Pro/Max subscription
pi --provider anthropic --name claude-worker
# equivalent friendly alias:
pi --provider claude --name claude-worker
```

Or pin exact models:

```bash
pi --model openai-codex/gpt-5.5 --name chatgpt-worker
pi --model anthropic/claude-opus-4-8 --name claude-worker
```

Those instances can run concurrently. Switching `/model` in one instance only changes that session's in-memory model and its session history; it does not retarget already-running Pi processes.

### OpenAI Codex

- Requires ChatGPT Plus or Pro subscription
- Officially endorsed by OpenAI: [Codex for OSS](https://developers.openai.com/community/codex-for-oss)

### Claude Pro/Max

Anthropic subscription auth is active for Claude Pro/Max accounts. Third-party harness usage draws from [extra usage](https://claude.ai/settings/usage) and is billed per token, not against Claude plan limits.

### GitHub Copilot

- Press Enter for github.com, or enter your GitHub Enterprise Server domain
- If you get "model not supported", enable it in VS Code: Copilot Chat → model selector → select model → "Enable"

## API Keys

### Environment Variables or Auth File

Use `/login` in interactive mode and select a provider to store an API key in `auth.json`, or set credentials via environment variable:

```bash
export ANTHROPIC_API_KEY=sk-ant-...
pi
```

| Provider | Environment Variable | `auth.json` key |
|----------|----------------------|------------------|
| Anthropic | `ANTHROPIC_API_KEY` | `anthropic` |
| Azure OpenAI Responses | `AZURE_OPENAI_API_KEY` | `azure-openai-responses` |
| OpenAI | `OPENAI_API_KEY` | `openai` |
| Fugu | `SAKANA_API_KEY` or `FUGU_API_KEY` (`FUGU_BASE_URL` optional) | `fugu` |
| DeepSeek | `DEEPSEEK_API_KEY` | `deepseek` |
| Google Gemini | `GEMINI_API_KEY` | `google` |
| Mistral | `MISTRAL_API_KEY` | `mistral` |
| Groq | `GROQ_API_KEY` | `groq` |
| Cerebras | `CEREBRAS_API_KEY` | `cerebras` |
| Cloudflare AI Gateway | `CLOUDFLARE_API_KEY` (+ `CLOUDFLARE_ACCOUNT_ID`, `CLOUDFLARE_GATEWAY_ID`) | `cloudflare-ai-gateway` |
| Cloudflare Workers AI | `CLOUDFLARE_API_KEY` (+ `CLOUDFLARE_ACCOUNT_ID`) | `cloudflare-workers-ai` |
| xAI | `XAI_API_KEY` | `xai` |
| OpenRouter | `OPENROUTER_API_KEY` | `openrouter` |
| Vercel AI Gateway | `AI_GATEWAY_API_KEY` | `vercel-ai-gateway` |
| ZAI | `ZAI_API_KEY` | `zai` |
| OpenCode Zen | `OPENCODE_API_KEY` | `opencode` |
| OpenCode Go | `OPENCODE_API_KEY` | `opencode-go` |
| Hugging Face | `HF_TOKEN` | `huggingface` |
| Fireworks | `FIREWORKS_API_KEY` | `fireworks` |
| Together AI | `TOGETHER_API_KEY` | `together` |
| Kimi For Coding | `KIMI_API_KEY` | `kimi-coding` |
| MiniMax | `MINIMAX_API_KEY` | `minimax` |
| MiniMax (China) | `MINIMAX_CN_API_KEY` | `minimax-cn` |
| Xiaomi MiMo | `XIAOMI_API_KEY` | `xiaomi` |
| Xiaomi MiMo Token Plan (China) | `XIAOMI_TOKEN_PLAN_CN_API_KEY` | `xiaomi-token-plan-cn` |
| Xiaomi MiMo Token Plan (Amsterdam) | `XIAOMI_TOKEN_PLAN_AMS_API_KEY` | `xiaomi-token-plan-ams` |
| Xiaomi MiMo Token Plan (Singapore) | `XIAOMI_TOKEN_PLAN_SGP_API_KEY` | `xiaomi-token-plan-sgp` |

Reference for environment variables and `auth.json` keys: [`const envMap`](https://github.com/earendil-works/pi-mono/blob/main/packages/ai/src/env-api-keys.ts) in [`packages/ai/src/env-api-keys.ts`](https://github.com/earendil-works/pi-mono/blob/main/packages/ai/src/env-api-keys.ts).

#### Auth File

Store credentials in `~/.pi/agent/auth.json`:

```json
{
  "anthropic": { "type": "api_key", "key": "sk-ant-..." },
  "openai": { "type": "api_key", "key": "sk-..." },
  "fugu": { "type": "api_key", "key": "$SAKANA_API_KEY" },
  "deepseek": { "type": "api_key", "key": "sk-..." },
  "google": { "type": "api_key", "key": "..." },
  "opencode": { "type": "api_key", "key": "..." },
  "opencode-go": { "type": "api_key", "key": "..." },
  "together": { "type": "api_key", "key": "..." },
  "xiaomi": { "type": "api_key", "key": "..." },
  "xiaomi-token-plan-cn":  { "type": "api_key", "key": "..." },
  "xiaomi-token-plan-ams": { "type": "api_key", "key": "..." },
  "xiaomi-token-plan-sgp": { "type": "api_key", "key": "..." }
}
```

The file is created with `0600` permissions (user read/write only). Auth file credentials take priority over environment variables.

#### Fugu

Sakana Fugu uses the OpenAI Responses-compatible Sakana API at `https://api.sakana.ai/v1`.

```bash
export SAKANA_API_KEY=...           # preferred; matches Sakana's Codex/Fugu setup
# or export FUGU_API_KEY=...
# optional: export FUGU_BASE_URL=https://api.sakana.ai
pi --provider fugu --model fugu
pi --provider fugu --model fugu-ultra --thinking xhigh
```

Both `fugu` and `fugu-ultra` support image input and only the `high` and `xhigh` reasoning levels. `FUGU_BASE_URL` is normalized to include `/v1`. Complex `fugu-ultra` tasks may need higher request timeouts.

### Key Resolution

The `key` field supports command execution, environment interpolation, and literals:

- **Shell command:** `"!command"` at the start executes the whole value as a command and uses stdout (cached for process lifetime)
  ```json
  { "type": "api_key", "key": "!security find-generic-password -ws 'anthropic'" }
  { "type": "api_key", "key": "!op read 'op://vault/item/credential'" }
  ```
- **Environment interpolation:** `"$ENV_VAR"` or `"${ENV_VAR}"` uses the value of the named variable. Interpolation works inside larger literals.
  ```json
  { "type": "api_key", "key": "$MY_ANTHROPIC_KEY" }
  { "type": "api_key", "key": "${KEY_PREFIX}_${KEY_SUFFIX}" }
  ```
  `$FOO_BAR` is the variable `FOO_BAR`; use `${FOO}_BAR` when `BAR` is literal text. Missing environment variables make the value unresolved.
- **Escapes:** `"$$"` emits a literal `"$"`; `"$!"` emits a literal `"!"` without triggering command execution.
  ```json
  { "type": "api_key", "key": "$$literal-dollar-prefix" }
  { "type": "api_key", "key": "$!literal-bang-prefix" }
  ```
- **Literal value:** Used directly
  ```json
  { "type": "api_key", "key": "sk-ant-..." }
  { "type": "api_key", "key": "public" }
  ```

Legacy uppercase env-var-like values such as `MY_API_KEY` are migrated to `$MY_API_KEY` on startup. OAuth credentials are also stored here after `/login` and managed automatically.

## Cloud Providers

### Azure OpenAI

```bash
export AZURE_OPENAI_API_KEY=...
export AZURE_OPENAI_BASE_URL=https://your-resource.openai.azure.com
# also supported: https://your-resource.cognitiveservices.azure.com
# root endpoints are auto-normalized to /openai/v1
# or use resource name instead of base URL
export AZURE_OPENAI_RESOURCE_NAME=your-resource

# Optional
export AZURE_OPENAI_API_VERSION=2024-02-01
export AZURE_OPENAI_DEPLOYMENT_NAME_MAP=gpt-4=my-gpt4,gpt-4o=my-gpt4o
```

### Amazon Bedrock

Amazon Bedrock requires an explicit verified region scope. A region from `~/.aws/config` is not
enough: Pi persists and sends `AWS_REGION` explicitly so discovery and runtime requests cannot drift.

```bash
# Option 1: AWS Profile (recommended for IAM Identity Center / SSO)
export AWS_PROFILE=your-profile

# Option 2: IAM Keys
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...

# Option 3: Bearer Token
export AWS_BEARER_TOKEN_BEDROCK=...

# Required AWS scope. US inference profiles in Ohio use us-east-2.
export AWS_REGION=us-east-2
```

For an SSO profile, AWS CLI v2 is required and the modern `sso-session` profile format is recommended.
Run `/login amazon-bedrock`, select SSO, and enter the exact profile and region. For IAM keys, bearer
tokens, ECS roles, or IRSA, select the ambient-credentials option and enter the exact region. Pi then:

1. verifies the selected profile and region with STS;
2. paginates region-scoped system inference profiles;
3. selects the current US/EU/APAC/global Claude candidate for each available tier;
4. runs a bounded one-token runtime probe for every candidate; and
5. persists the exact profile, region, verified model IDs, verification mode, and timestamp in the
   user settings file.

Bearer tokens skip STS and control-plane discovery because that auth mode cannot reliably use them;
Pi instead probes the bounded region-derived candidates directly. A credential alone exposes no
Bedrock models. If `AWS_PROFILE` or `AWS_REGION` later differs from the saved scope, the saved model
evidence is rejected until `/login amazon-bedrock` verifies the new scope. Direct Anthropic models are
unaffected.

Pi runs `aws sso login --profile <profile>` without a shell. The AWS SDK refreshes ordinary role
credentials; if the underlying SSO session later expires, Pi performs one foreground login and
replays the Bedrock request once, only before a response has started. Background and worker requests
never open a browser and instead return an authentication-required error to the owning session.

Also supports ECS task roles (`AWS_CONTAINER_CREDENTIALS_*`) and IRSA (`AWS_WEB_IDENTITY_TOKEN_FILE`).

```bash
pi --provider amazon-bedrock --model us.anthropic.claude-sonnet-5
```

Prompt caching is enabled automatically for Claude models whose ID contains a recognizable model name (base models and system-defined inference profiles). For application inference profiles (whose ARNs don't contain the model name), set `AWS_BEDROCK_FORCE_CACHE=1` to enable cache points:

```bash
export AWS_BEDROCK_FORCE_CACHE=1
pi --provider amazon-bedrock --model arn:aws:bedrock:us-east-2:123456789012:application-inference-profile/abc123
```

An explicitly configured application-inference-profile ARN remains selectable only when its ARN
region matches the verified scope. It does not expand the discovered system-model allowlist.

If you are connecting to a Bedrock API proxy, the following environment variables can be used:

```bash
# Set the URL for the Bedrock proxy (standard AWS SDK env var)
export AWS_ENDPOINT_URL_BEDROCK_RUNTIME=https://my.corp.proxy/bedrock

# Set if your proxy does not require authentication
export AWS_BEDROCK_SKIP_AUTH=1

# Set if your proxy only supports HTTP/1.1
export AWS_BEDROCK_FORCE_HTTP1=1
```

An explicit unauthenticated proxy (`AWS_BEDROCK_SKIP_AUTH=1` plus
`AWS_ENDPOINT_URL_BEDROCK_RUNTIME`) is outside AWS account/region verification and retains its custom
model surface.

### Cloudflare AI Gateway

`CLOUDFLARE_API_KEY` can be set via `/login`. The account ID and gateway slug must be set as environment variables.

```bash
export CLOUDFLARE_API_KEY=...           # or use /login
export CLOUDFLARE_ACCOUNT_ID=...
export CLOUDFLARE_GATEWAY_ID=...        # create at dash.cloudflare.com → AI → AI Gateway
pi --provider cloudflare-ai-gateway --model "claude-sonnet-4-5"
```

Routes to OpenAI, Anthropic, and Workers AI through Cloudflare AI Gateway. Workers AI uses the Unified API (`/compat`) and prefixed model IDs (`workers-ai/@cf/...`). OpenAI uses the OpenAI passthrough route (`/openai`) with native OpenAI model IDs such as `gpt-5.1`. Anthropic uses the Anthropic passthrough route (`/anthropic`) with native Anthropic model IDs such as `claude-sonnet-4-5`.

AI Gateway authentication uses `CLOUDFLARE_API_KEY` as `cf-aig-authorization`. Upstream authentication can be one of:

| Mode | Request auth | Upstream auth |
|------|--------------|---------------|
| Workers AI | Cloudflare token only | Cloudflare-native |
| Unified billing | Cloudflare token only | Cloudflare handles upstream auth and deducts credits |
| Stored BYOK | Cloudflare token only | Cloudflare injects provider keys stored in the AI Gateway dashboard |
| Inline BYOK | Cloudflare token plus upstream `Authorization` header | The request supplies the upstream provider key |

For normal pi usage, prefer unified billing or stored BYOK. Inline BYOK requires configuring an additional upstream `Authorization` header for the Cloudflare AI Gateway provider, for example via a `models.json` provider/model override.

### Cloudflare Workers AI

`CLOUDFLARE_API_KEY` can be set via `/login`. `CLOUDFLARE_ACCOUNT_ID` must be set as an environment variable.

```bash
export CLOUDFLARE_API_KEY=...           # or use /login
export CLOUDFLARE_ACCOUNT_ID=...
pi --provider cloudflare-workers-ai --model "@cf/moonshotai/kimi-k2.6"
```

Pi automatically sets `x-session-affinity` for [prefix caching](https://developers.cloudflare.com/workers-ai/features/prompt-caching/) discounts.

### Google Vertex AI

Uses Application Default Credentials:

```bash
gcloud auth application-default login
export GOOGLE_CLOUD_PROJECT=your-project
export GOOGLE_CLOUD_LOCATION=us-central1
```

Or set `GOOGLE_APPLICATION_CREDENTIALS` to a service account key file.

## Custom Providers

**Via models.json:** Add Ollama, LM Studio, vLLM, or any provider that speaks a supported API (OpenAI Completions, OpenAI Responses, Anthropic Messages, Google Generative AI). See [models.md](models.md).

**Via extensions:** For providers that need custom API implementations or OAuth flows, create an extension. See [custom-provider.md](custom-provider.md) and [examples/extensions/custom-provider-gitlab-duo](../examples/extensions/custom-provider-gitlab-duo/).

## Resolution Order

When resolving credentials for a provider:

1. CLI `--api-key` flag
2. `auth.json` entry (API key or OAuth token)
3. Environment variable
4. Custom provider keys from `models.json`
