Skip to content

Configure OpenRouter Provider

OpenRouter is a unified gateway that exposes 100+ LLMs behind a single OpenAI-compatible API — Claude, GPT, Gemini, Llama, Mistral, and others — with one API key and one endpoint.

Omnia’s Provider custom resource treats OpenRouter like any other OpenAI-compatible endpoint. Two Omnia features combine to make this work:

  1. spec.baseURL — point the OpenAI wire format at OpenRouter.
  2. spec.headers — set the HTTP-Referer and X-Title attribution headers that OpenRouter uses for leaderboards and rate-limit tiers.
Terminal window
kubectl create secret generic openrouter-credentials \
--namespace agents \
--from-literal=OPENAI_API_KEY='sk-or-v1-...'

Omnia’s openai provider reads OPENAI_API_KEY by default; OpenRouter’s key slots in there directly. If you already use direct-OpenAI in another Provider, use a different secret name or key — see Migrate Provider Credentials for per-Provider isolation.

apiVersion: omnia.altairalabs.ai/v1alpha1
kind: Provider
metadata:
name: openrouter
namespace: agents
spec:
type: openai
model: anthropic/claude-sonnet-4 # OpenRouter model ID
baseURL: https://openrouter.ai/api/v1
headers:
HTTP-Referer: https://your-app.example.com # Required for leaderboard attribution
X-Title: omnia # Appears in OpenRouter analytics
credential:
secretRef:
name: openrouter-credentials
capabilities:
- text
- streaming
- tools
- json

When the runtime creates the underlying PromptKit provider, spec.headers is passed through providers.ProviderSpec.Headers. PromptKit’s openai provider (which type: openai resolves to) applies custom headers to every /chat/completions request via its ApplyCustomHeaders helper — including streaming and tool-calling requests. Collisions with built-in headers (e.g. Authorization) are rejected at request time so you can’t accidentally break auth.

Terminal window
kubectl get provider openrouter -n agents -o wide
kubectl get provider openrouter -n agents -o jsonpath='{.status.conditions}' | jq .

Both SecretFound and CredentialConfigured conditions should be True. OpenRouter does not respond to unauthenticated GET / requests, so the EndpointReachable condition may be omitted or show a 401 — either is considered reachable (a 401 proves the endpoint is up).

Reference the Provider from an AgentRuntime like any other:

apiVersion: omnia.altairalabs.ai/v1alpha1
kind: AgentRuntime
metadata:
name: my-agent
namespace: agents
spec:
promptPackRef:
name: my-pack
providers:
- name: default
providerRef:
name: openrouter
facade:
type: websocket

OpenRouter’s model IDs follow the <vendor>/<model> pattern. Examples:

Omnia spec.modelHosted model
anthropic/claude-sonnet-4Anthropic Claude Sonnet 4
openai/gpt-4oOpenAI GPT-4o
google/gemini-2.0-flash-expGoogle Gemini 2.0 Flash
meta-llama/llama-3.1-70b-instructMeta Llama 3.1 70B

Full list: openrouter.ai/models.

OpenRouter’s pricing can differ from the first-party API. Supply spec.pricing if you want Omnia’s cost tracking to reflect OpenRouter’s actual rates:

spec:
pricing:
inputCostPer1K: "0.003"
outputCostPer1K: "0.015"

If omitted, PromptKit falls back to built-in pricing for the underlying model family (e.g. Anthropic’s public list prices for a claude-sonnet-4 request). For production cost tracking against OpenRouter invoices, set the pricing explicitly.

401 Unauthorized on every request. Check that the secret value uses an sk-or-v1-... OpenRouter key — a direct-OpenAI sk-... key will not work against openrouter.ai. Verify with kubectl get secret openrouter-credentials -n agents -o jsonpath='{.data.OPENAI_API_KEY}' | base64 -d | head -c 12.

Rate limits / attribution missing. OpenRouter’s free tier and leaderboards key off the HTTP-Referer and X-Title headers. If you omit them, requests still succeed but you’ll hit the unattributed rate limit and your app won’t appear on the leaderboard.

headers map is immutable error at request time. This is PromptKit rejecting a custom header that collides with one the provider sets itself (most commonly Authorization). Remove that key from spec.headers.