Skip to main content

Virtual API keys

The gateway supports virtual API keys — gateway-issued bearer tokens that identify clients and optionally restrict what they can access. These are "virtual" because they're not upstream provider keys; they're managed entirely by the gateway.

Clients send the key in the standard Authorization: Bearer <key> header, matching the convention used with OpenAI and other providers. Existing tools (Open WebUI, LiteLLM clients, curl scripts) work without changes beyond configuring a key.

Configuration

Keys are defined in the gateway config file. Each key has a name (for logging and attribution), a secret value, and optional constraints:

api_keys:
enabled: true
keys:
- name: alice
key: "sk-gw-abc123..."
allowed_models: ["claude-*", "gpt-*"]
allowed_routes: ["coding", "general"]

- name: bob
key: "sk-gw-def456..."
allowed_models: ["llama3", "qwen3-vl:*"]

- name: ci-pipeline
key: "sk-gw-xyz789..."
allowed_models: ["*"]

When api_keys is omitted or enabled: false, the gateway operates without authentication — open access.

Keys support environment variable substitution:

keys:
- name: alice
key: "${ALICE_API_KEY}"

Key format

Keys use the prefix sk-gw- to distinguish them from upstream provider keys (OpenAI sk-, Anthropic sk-ant-). Generate a key with the CLI:

llm-gateway genkey
Output
sk-gw-a1b2c3d4e5f6...

Keys are stored as plaintext in the config file, consistent with how upstream API keys are stored.

Authentication flow

Every incoming request passes through the auth middleware before reaching any handler:

Client request
|
v
Auth middleware
|-- /health, /metrics -> pass through (no auth required)
|-- api_keys disabled -> pass through
|-- Authorization header missing -> 401
|-- Key not recognized -> 401
|-- Key valid -> attach identity to context, continue
|
v
Existing handler pipeline (unchanged)

Model restrictions

Each key can specify allowed_models as a list of glob patterns (e.g., claude-* matches any Claude model). A key with allowed_models: ["*"] or no allowed_models field has unrestricted access.

Model permission is checked after the model is fully resolved (including semantic routing) but before the request is dispatched to a provider. If the resolved model doesn't match any allowed pattern, the gateway returns 403.

Route restrictions

When semantic routing is enabled, a key can specify allowed_routes to limit which semantic routes it can use. If semantic routing selects a route the key can't access, the gateway returns 403 — it doesn't silently reroute to a fallback.

Error responses

Errors follow the OpenAI-compatible format:

ScenarioStatusError type
Missing Authorization header401authentication_error
Invalid key401authentication_error
Model not allowed403permission_error
Route not allowed403permission_error

Logging and metrics

The validated key name is included in:

  • Semantic routing log lines: semantic routing: key='alice' method=semantic route='coding' ...
  • Request metrics: key label on llm_gateway.requests and llm_gateway.request.duration