# How to Configure Authentication Keys for Upstream Providers in Switchyard

> Learn how to configure upstream authentication keys in Switchyard. Reference environment variables in TOML for secure secret management and avoid embedding credentials.

- Repository: [NVIDIA-NeMo/Switchyard](https://github.com/NVIDIA-NeMo/Switchyard)
- Tags: how-to-guide
- Published: 2026-08-15

---

**Configure upstream authentication in Switchyard by referencing environment variables in your TOML configuration rather than embedding secrets directly.**

Switchyard, an open-source LLM routing layer from NVIDIA, handles authentication through **environment variable references** in deployment configuration files. This design keeps API keys out of version control while supporting flexible authentication patterns—from single static keys to per-call credential forwarding.

## Understanding the Two-Layer Authentication Model

The authentication flow in Switchyard operates across **two distinct layers**: the native server configuration and launcher-specific overrides. Understanding this separation helps you choose the right approach for your deployment.

### Layer 1: TOML Deployment Configuration

Each upstream provider is defined under the `[llm_clients]` table in your [`routes.toml`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/routes.toml). The **critical field** is `api_key_env`, which specifies the *name* of the environment variable containing the actual key—not the key itself.

In [`docs/reference/toml_schema.md`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/docs/reference/toml_schema.md) (lines 41–48), the schema defines this behavior:

- `api_key_env` = environment variable name to read at startup
- If **omitted**, requests are sent without any authentication header
- `forward_auth` = boolean to forward the caller's credentials instead

### Layer 2: Launcher-Specific Overrides

When Switchyard launches clients like Codex or Claude Code, it constructs **transient provider configurations** that override or supplement the TOML settings.

In [`switchyard/cli/launchers/codex_cli_launcher.py`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/switchyard/cli/launchers/codex_cli_launcher.py) (lines 78–90), the `_provider_overrides` dictionary is built with conditional logic:

- `requires_openai_auth` flag controls whether a key is injected
- `env_key` points to "OPENAI_API_KEY" when credential forwarding is desired
- `_codex_env` (lines 97–102) sets `OPENAI_API_KEY="switchyard"` as a fallback

For Claude Code, [`switchyard/cli/launchers/claude_code_launcher.py`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/switchyard/cli/launchers/claude_code_launcher.py) (lines 70–92) demonstrates a different pattern: `ANTHROPIC_API_KEY=""` suppresses conflict warnings while routing through the proxy.

## Configuration Methods for Different Use Cases

### Static Server-Owned Key (Most Common)

Use this pattern when the Switchyard server owns a single credential for an upstream provider.

**Step 1:** Define the environment variable name in your TOML:

```toml
[llm_clients.openai]
base_url = "https://api.openai.com/v1"
api_key_env = "OPENAI_API_KEY"
model = "gpt-4o"

```

**Step 2:** Export the actual key before starting Switchyard:

```bash
export OPENAI_API_KEY="sk-..."
switchyard-server --config routes.toml

```

The native server reads [`docs/reference/toml_schema.md`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/docs/reference/toml_schema.md), resolves `api_key_env` to the concrete value, and injects the `Authorization: Bearer ...` header on every request.

### Forward Caller's Credential (Multi-Tenant)

Use this pattern when each user presents their own API key and you want to proxy requests without server-side authentication.

```toml
[llm_clients.openai]
base_url = "https://api.openai.com/v1"
forward_auth = true

# api_key_env intentionally omitted

```

With `forward_auth = true`, Switchyard forwards the caller's `Authorization` header—or provider-specific headers like `X-API-Key`—to the upstream unchanged. This enables multi-tenant deployments where the server never handles raw credentials.

### Testing with Launcher-Provided Keys (Local Development)

Launchers automatically inject synthetic keys when appropriate, keeping secrets out of configuration during testing.

**Codex CLI pattern** ([`switchyard/cli/launchers/codex_cli_launcher.py`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/switchyard/cli/launchers/codex_cli_launcher.py)):

```python

# When forward_auth is disabled, inject placeholder

_codex_env["OPENAI_API_KEY"] = "switchyard"

```

**Claude Code pattern** ([`switchyard/cli/launchers/claude_code_launcher.py`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/switchyard/cli/launchers/claude_code_launcher.py)):

```python
_claude_env["ANTHROPIC_API_KEY"] = ""  # Silences conflict warning

```

These defaults allow local testing against rate-limited internal services that require a key without exposing actual credentials.

## Provider-Specific Environment Variables

Switchyard supports any provider following the Bearer token convention. Common variable names as implemented in the_source code:

| Provider | Typical `api_key_env` Value | Header Format |
|----------|----------------------------|---------------|
| OpenAI | `OPENAI_API_KEY` | `Authorization: Bearer sk-...` |
| Anthropic | `ANTHROPIC_API_KEY` | `x-api-key: sk-ant-...` |
| OpenRouter | `OPENROUTER_API_KEY` | `Authorization: Bearer sk-or-...` |
| NVIDIA | `NVIDIA_API_KEY` | `Authorization: Bearer nvapi-...` |

The exact variable name is **user-defined** in `api_key_env`—Switchyard does not enforce provider-specific conventions.

## Where Keys Are Consumed in the Codebase

| File Path | Authentication Role |
|-----------|---------------------|
| [`docs/reference/toml_schema.md`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/docs/reference/toml_schema.md) | Defines TOML schema including `api_key_env` and `forward_auth` |
| [`switchyard/cli/launchers/native_server.py`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/switchyard/cli/launchers/native_server.py) | Starts server that reads TOML and resolves environment variables |
| [`switchyard/cli/launchers/codex_cli_launcher.py`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/switchyard/cli/launchers/codex_cli_launcher.py) | Constructs `_provider_overrides` with `env_key` and `"switchyard"` fallback |
| [`switchyard/cli/launchers/claude_code_launcher.py`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/switchyard/cli/launchers/claude_code_launcher.py) | Builds `_claude_env` with empty `ANTHROPIC_API_KEY` workaround |

## Common Configuration Pitfalls

**Mistake: Embedding the key directly**

```toml

# WRONG - never do this

api_key_env = "sk-live-actual-key"

```

The `api_key_env` field expects a **variable name**, not the key value. Embedding secrets exposes credentials in logs and version control.

**Mistake: Setting both `api_key_env` and `forward_auth`**

```toml

# Undefined behavior - choose one pattern

api_key_env = "OPENAI_API_KEY"
forward_auth = true

```

These options are mutually exclusive. The server uses `api_key_env` if present; `forward_auth` only applies when `api_key_env` is absent.

## Summary

- **Never embed secrets** in TOML files—use `api_key_env` to reference environment variables
- **Export variables** before launching: `export PROVIDER_API_KEY="..."`
- **Use `forward_auth = true`** for multi-tenant credential forwarding instead of server-side keys
- **Rely on launcher defaults** (`"switchyard"`, empty strings) for local testing without real credentials
- **Verify in source**: [`docs/reference/toml_schema.md`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/docs/reference/toml_schema.md) defines the schema; launcher files show override behavior

## Frequently Asked Questions

### What happens if I omit `api_key_env` entirely?

Requests to that provider are sent **without any authentication header**. This works for open endpoints or when using `forward_auth = true` to pass through caller credentials. Per the TOML schema ([`docs/reference/toml_schema.md`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/docs/reference/toml_schema.md), lines 41–48), the field is optional and defaults to no authentication.

### Can I use the same environment variable for multiple providers?

Yes. Multiple `[llm_clients.<name>]` sections can reference the same `api_key_env` value. However, best practice is using **provider-specific variables** (e.g., `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`) to enable credential rotation per service without affecting others.

### How do I rotate credentials without restarting Switchyard?

You must **restart the native server**. Environment variables are read once at startup in [`switchyard/cli/launchers/native_server.py`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/switchyard/cli/launchers/native_server.py). There is no hot-reload mechanism for authentication configuration. For zero-downtime rotation, deploy a new instance with updated variables and migrate traffic.

### Why does Claude Code use an empty `ANTHROPIC_API_KEY`?

As implemented in [`switchyard/cli/launchers/claude_code_launcher.py`](https://github.com/NVIDIA-NeMo/Switchyard/blob/main/switchyard/cli/launchers/claude_code_launcher.py) (lines 70–92), the empty string suppresses a conflict warning from Claude Code's internal validation. The actual request routing goes through Switchyard's proxy, so the upstream never receives the empty value—authentication is handled by the proxy configuration instead.