Concepts
The gateway sits between your application and 30 LLM providers, speaking one OpenAI-compatible wire format on the way in and translating to each provider's native API on the way out. This page defines the vocabulary the rest of the docs use: providers, targets, strategies, plugins, and MCP servers.
OpenAI-compatible APIโ
The gateway speaks the OpenAI wire format for chat completions, embeddings, images, and model listing. Any client that works with OpenAI will work with the gateway after changing only the base_url. Provider credentials, model routing, and policy enforcement happen inside the gateway โ your application code is unaffected.
Providers and targetsโ
A provider is a registered AI API backend (e.g., OpenAI, Anthropic, Bedrock). The gateway supports 30 providers. A provider is registered when its required environment variable(s) are set โ no code changes needed.
A target is a config entry (targets[]) that references a registered provider by virtual_key. targets is an allowlist on every routed surface: setting a provider's env var only registers it โ the provider serves requests only if it also appears in targets[]. A model owned solely by a provider that is registered but not listed under targets returns 404 model_not_found.
Each target carries:
| Field | Purpose |
|---|---|
virtual_key | Names the registered provider this target routes to (required) |
weight | Relative share under loadbalance; 0 drains the target (ignored by every other mode) |
retry | attempts, on_status_codes, initial_backoff_ms โ honoured under every routing mode, not just fallback |
circuit_breaker | failure_threshold, success_threshold, timeout โ one breaker per target, shared across chat, streaming, embeddings, and images |
concurrency | max_concurrency, queue_size โ caps in-flight requests per target; overflow returns 429 |
models | Operator-declared model IDs this target serves, additive to the model catalog and live discovery; exact IDs only, no wildcards |
Routing strategiesโ
The strategy controls which target(s) a request is offered to, and in what order. Configure it with strategy.mode.
| Strategy | Description |
|---|---|
single | Always route to the first target. Simplest setup. |
fallback | Try targets in declared order; advance to the next target after a failover-safe failure. |
loadbalance | Distribute requests across targets by weight. |
conditional | Match model (exact) or model_prefix (prefix) against declared rules; first match wins. |
least-latency | Route to the compatible target with the lowest observed p50 latency. |
cost-optimized | Estimate input cost from the model catalog and route to the cheapest compatible target. |
content-based | Match user-message content by substring or regex; first rule match wins. |
ab-test | Split traffic across labeled variants by weight for comparison testing. |
One pipeline governs chat, streaming, embeddings, images, rerank, moderation, transcription and speech, and one ranker orders targets for all of them, so retry, circuit-breaking and candidate order behave identically across every surface. Strategies split into two families:
- Pool modes (
fallback,loadbalance,least-latency,cost-optimized,ab-test) advance past a target that failed in a failover-safe way โ transport error, attempt timeout,408/429/5xx, open circuit, saturation โ to the next candidate; any other4xxis returned to the client. - Named modes (
single,conditional,content-based) stay inside what was named:singlereports its one target's failure; a rule walks itstarget_keyschain on the same failover-safe failures and never reaches a target it did not name.
Every mode skips a target whose circuit is open, or that is parked after a 429, among the candidates it offers; if every candidate is unavailable, the request is still attempted and returns 503. A rule with one target offers no other candidate.
See Routing for per-strategy configuration and YAML examples.
Model aliasesโ
Aliases map short names to full model IDs. They are resolved before routing, so cheap can map to gemini-1.5-flash and every request to model: cheap is transparently sent to Gemini.
Capability matrixโ
GET /v1/capabilities reports, per provider, which OpenAI chat parameters it can express: forward (sent as-is), translate (mapped to an equivalent), or unsupported. When a request sets a parameter a routed provider can't express, compatibility.on_unsupported_param decides what happens: warn (default โ forward it anyway and log), drop (remove it and log), or reject (400 naming the parameter).
Pluginsโ
Plugins are global, configured under a single top-level plugins: list โ there is no per-route or per-target plugin field. Each entry runs at one of three stages: before_request, after_request, or on_error.
OSS pluginsโ
These 6 plugins ship with the open-source gateway. Three of them are multi-stage โ they must be listed once per stage with byte-identical config, or the gateway refuses to start:
| Plugin | Stage(s) | Purpose |
|---|---|---|
word-filter | before_request (optionally also after_request) | Reject request or response text containing a blocked substring |
max-token | before_request | Reject requests exceeding token, message-count, or input-length limits |
rate-limit | before_request | Token-bucket rate limiting (global, per-key, per-user) |
budget | before_request + after_request | Check and record per-API-key USD spend; over the cap returns 402 insufficient_quota |
response-cache | before_request + after_request | Serve identical repeated chat requests from an in-memory cache |
request-logger | before_request + after_request + on_error | Emit structured logs, optionally persist to SQLite/Postgres |
Failure policy: a plugin's Reject verdict is always honoured โ it becomes a 4xx, 429 (rate-limit), or 402 (budget) response. A plugin error (the plugin itself broke, not a deliberate rejection) fails closed (500) for guardrail, auth, ratelimit, and transform plugins, but fails open (logged, request continues) for logging and metrics plugins.
SkipProvider: when response-cache serves a hit, it sets SkipProvider on the request, which skips only the call to the upstream provider โ every remaining before_request plugin and the whole after_request stage still run, so rate limiting, budget checks, and logging behave the same whether or not the response came from cache.
Ferro Labs Managed pluginsโ
These 5 plugins require Ferro Labs Managed because they depend on ML inference services that run server-side:
| Plugin | Stage | Purpose |
|---|---|---|
pii-redact | before_request | Detect and redact PII entities using NER models |
secret-scan | before_request | Block requests containing leaked API keys or credentials |
prompt-shield | before_request | Score and block prompt injection attempts |
schema-guard | after_request | Validate model JSON output against a JSON Schema |
regex-guard | before_request | Block requests matching configurable regex patterns |
The 5 enterprise plugins require a Ferro Labs Managed account. Join the waitlist โ
See Plugins for full configuration examples.
MCP integrationโ
Model Context Protocol (MCP) lets you connect external tool servers to the gateway. Each mcp_servers[] entry sets exactly one of url (Streamable HTTP) or command (+args, a stdio subprocess); a stdio subprocess inherits no gateway environment โ only PATH/HOME/LANG/TMPDIR plus whatever you list explicitly under env.
Tool injection is conditional: the gateway injects available MCP tools into a chat completion only when the request carries no tools array of its own. A request that already supplies its own tools passes through untouched โ the gateway does not merge or de-duplicate. When the model responds with tool_calls the gateway owns, it calls the tool over MCP, appends the result as a tool message, and re-sends to the model โ up to a bounded depth โ invisibly to the caller. Guardrails and budget checks run on every loop turn, not just the first.
Setting required: true on a server gates GET /readyz on that server's initialize handshake โ if it isn't ready, the instance stops serving traffic entirely, including requests that need no tools.
See MCP integration for setup and examples.
Dashboardโ
The operations dashboard ships embedded in the OSS binary and is served from the gateway's own root โ there's no standalone dashboard container or separate image to deploy. It surfaces request logs, API key management, configured plugins, and config history.
Ferro Labs Managedโ
Ferro Labs Managed is the managed, multi-tenant version of the AI Gateway hosted by Ferro Labs. It wraps the same OSS engine with per-tenant isolation, durable billing, semantic caching, SSO/SAML, audit logs, and the 5 enterprise security plugins listed above. See OSS vs Ferro Labs Managed for a full comparison.
Observabilityโ
- Prometheus metrics โ scraped at
/metrics(requires a bearer token withread_onlyoradminscope). Includes request counts, latency histograms, token usage, and cache hit rates. - Structured JSON logs โ emitted to stdout, correlated by the
X-Request-IDtrace ID also returned on every response. - Health checks are split three ways:
GET /livezreports the process is alive;GET /readyzreports whether at least one target is routable (503, reasonno routable targets, when none is);GET /healthreturns a deeper diagnostic โ per-provider registration and circuit-breaker state.
See Monitoring for details.