multi-routerdocs

Getting Started

Overview

What Multi-Router is, and how its parts fit together.

Multi-Router is a platform for sending requests to AI models from many providers through one API, and for managing the routing, prompts, credentials, spend and records of those requests. Applications send OpenAI- or Anthropic-format requests to Multi-Router with one API key, and routers decide which model and provider serve each request. Teams change models, prompts and providers on the router, with no change to application code, and see every request in Traffic.

How it works

A request reaches a model in four steps:

  1. An organization adds provider keys for the providers it uses, such as Anthropic, OpenAI or AWS Bedrock.
  2. It creates routers, each with the model, provider and settings for one job, such as a support bot.
  3. Applications call Multi-Router with an API key, naming the router in the model field.
  4. Multi-Router serves the request from the router's provider, and records it in Traffic with its cost.

What you can do

Multi-Router covers these areas, each with its own section:

AreaCovers
Providers & ModelsProvider keys, the model catalog, regions and variants, model groups and custom models.
RoutingStatic, fallback and weighted routers, Claude Proxies and Pi Proxies, aliases and router settings.
PromptsVersioned prompts, written in Multi-Router or kept in GitHub, bound to routers.
Agents & EditorsClaude Code, Claude Desktop and Pi.Dev through Multi-Router.
API AccessAPI keys, scopes, router access and rate limits.
ObservabilityTraffic, request detail, request logging and cost.
Organizations & PeopleAccounts, organizations, people and roles.
Single Sign-OnSigning in through the company's identity provider.
User Provisioning (SCIM)Managing people from the identity provider.
Self-Service ProvisioningTools policies and the Portal, for setting up AI tools.
Machine IdentityWorkloads that authenticate with their platform identity.
Budgets & Spend ControlCaps, notifications and blocks on spend.
Self-HostingRunning Multi-Router in your own infrastructure.
API ReferenceEvery /v1 endpoint.

Next steps

The Quickstart sends a first request in a few steps. Teams moving an existing application start with Using an SDK, and Key Concepts defines the terms used across these docs.

Getting Started

Quickstart

Sending a first request through Multi-Router: a provider key, a router, an API key and a request.

This quickstart sets up a router for an Anthropic model and sends it a request. It takes a Multi-Router account and an Anthropic API key. Other providers follow the same steps, with the extra settings some need, such as a region for AWS Bedrock.

1. Add a provider key

A provider key is the organization's credential for a model provider, which Multi-Router uses to send requests there.

  1. Sign in to the dashboard at https://dashboard.multi-router.ai.
  2. Open LLM Provider Keys from the profile menu at the top right, and select Anthropic in the Providers list.
  3. In the form, or with Add Anthropic key, enter a Name, such as Anthropic production, paste the API key, and select Save key.

The first key for a provider becomes its default key. See Provider Key.

2. Create a router

A router holds the model and settings a request is served with.

  1. Open Routers in the top navigation, and select New router.
  2. Under Type, keep Static Router, which is selected.
  3. Under Details, name the router, such as Support bot.
  4. Under Routing, choose the model, such as Claude Sonnet 4.5, its provider Anthropic and variant standard, and the key from step 1, or Org-default key.
  5. Under Review & create, create the router, and copy its id with Copy router ID, such as rt_9Hk3Ws7PzQ.

See Static Router.

3. Create an API key

An API key authenticates your application to Multi-Router.

  1. Open API Keys from the profile menu, and select New API key.
  2. Name the key, such as quickstart, keep the Inference only access, and select Create.
  3. Copy the key with Copy, and set it in your shell as MR_API_KEY.

See API Key.

4. Send a request

Send a Chat Completions request that names the router in the model field:

Requestbash
curl https://api.multi-router.ai/v1/chat/completions \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "multi-router/rt_9Hk3Ws7PzQ",
    "messages": [{ "role": "user", "content": "Say hello in one sentence." }]
  }'

The response is a standard Chat Completions response, with headers such as x-mr-provider and x-mr-upstream-model showing how it was served.

5. See it in Traffic

Open Traffic in the top navigation. The request appears at the top, with its router, model, tokens, cost and latency, and selecting its request id opens its full detail. See Traffic.

Next steps

Each next step builds on the router and key from this quickstart:

GoalPage
Use the OpenAI or Anthropic SDKUsing an SDK
Add a fallback providerFallback Router
Attach a system prompt to the routerPrompt Binding
Give each application its own limited keyRouter Access
Cap spendCreating a Budget
Connect Claude CodeClaude Code

Getting Started

Using an SDK

Pointing the OpenAI and Anthropic SDKs at Multi-Router.

Multi-Router speaks the OpenAI Chat Completions and Anthropic Messages formats, so the official OpenAI and Anthropic SDKs work with it by changing two settings: the base URL and the API key. The model field then names a Multi-Router router, and every other part of the application stays as it is.

OpenAI SDK

The OpenAI SDK uses the base URL https://api.multi-router.ai/v1:

Pythonpython
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.multi-router.ai/v1",
    api_key=os.environ["MR_API_KEY"],
)

response = client.chat.completions.create(
    model="multi-router/rt_9Hk3Ws7PzQ",
    messages=[{"role": "user", "content": "Say hello in one sentence."}],
)
print(response.choices[0].message.content)
TypeScripttypescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.multi-router.ai/v1",
  apiKey: process.env.MR_API_KEY,
});

const response = await client.chat.completions.create({
  model: "multi-router/rt_9Hk3Ws7PzQ",
  messages: [{ role: "user", content: "Say hello in one sentence." }],
});
console.log(response.choices[0].message.content); // in an ES module

Anthropic SDK

The Anthropic SDK uses the base URL https://api.multi-router.ai:

Pythonpython
import os
import anthropic

client = anthropic.Anthropic(
    base_url="https://api.multi-router.ai",
    api_key=os.environ["MR_API_KEY"],
)

message = client.messages.create(
    model="multi-router/rt_9Hk3Ws7PzQ",
    max_tokens=200,
    messages=[{"role": "user", "content": "Say hello in one sentence."}],
)
print(message.content[0].text)

The model field

The model field names what serves the request:

Model fieldServed by
multi-router/rt_9Hk3Ws7PzQThe router, with its model, provider, prompt and settings.
multi-router/productionThe router the alias production points to.
anthropic/claude-sonnet-4.5The model, with the organization's default Anthropic key.

Naming a router keeps the model choice out of the application, so it changes on the router. See Chat Completions.

Multi-Router fields and headers

A few optional fields and headers add Multi-Router features to SDK requests:

Field or headerPurpose
prompt_variablesValues for the router's bound prompt. In Python, sent with extra_body; in TypeScript, as a field of the request.
x-mr-session-idGroups the requests of a conversation in Traffic. Sent as an extra header.
x-mr-correlation-idMatches requests to the application's own logs, and is returned on the response. Sent as an extra header.
userThe application's end user on Chat Completions, shown in Traffic's Customer column. On Messages, metadata.user_id.
Prompt variables and a session id with the OpenAI SDKpython
response = client.chat.completions.create(
    model="multi-router/rt_9Hk3Ws7PzQ",
    messages=[{"role": "user", "content": "How do I reset my password?"}],
    user="tenant_acme",
    extra_body={"prompt_variables": {"product": "Acme Cloud", "language": "English"}},
    extra_headers={"x-mr-session-id": "conv_8812"},
)

See Prompt Variables and Request Tracing.

Getting Started

Key Concepts

The terms used across the Multi-Router docs, each with where to read more.

Multi-Router's terms fall into four areas: where models come from, how requests are routed, who can do what, and the prompts, records and spend around each request. Each term below links to the page that covers it in full.

Providers and models

Models come from providers, through the organization's keys:

TermMeaning
ProviderA company or platform that serves models, such as Anthropic, OpenAI or AWS Bedrock.
Provider keyThe organization's credential for a provider, used to send it requests.
Model catalogThe list of models Multi-Router knows, with their offerings and prices.
Model tupleThe model, provider, variant and region that identify one way to serve a model.
VariantA provider's way of serving a model, such as standard or geo.
Model groupAn approved set of models, used by Claude Proxies and tools policies.

Routing

Routers decide how each request is served:

TermMeaning
RouterA named configuration that serves requests, holding the model, provider, prompt and settings.
Static routerA router that serves one model from one provider.
Fallback routerA router that tries its member routers in order until one succeeds.
Weighted routerA router that spreads requests across its members by weight.
Claude ProxyA router that serves Claude Code and Claude Desktop in their own format.
Pi ProxyA router that serves Pi.Dev with the developer's own provider login.
AliasA second name that points to a router, and can be repointed.

Access

Accounts, organizations and credentials decide who can do what:

TermMeaning
AccountThe company's top-level space, which is also an organization.
OrganizationA team's space inside the account, with its own resources.
API keyThe credential an application uses to call Multi-Router.
ScopeA permission on an API key to use a group of endpoints.
Tools policyAn organization's rules for the AI tools people set up from the Portal.
Machine identityA workload's platform identity, used in place of an API key.

Prompts, records and spend

Prompts shape requests, Traffic records them, and budgets cap their cost:

TermMeaning
PromptSaved, versioned instructions that a router applies to its requests, combined with the caller's own system message by the router's Prompt composition.
Prompt bindingThe link between a router and its prompt, at the latest or a pinned revision.
TrafficThe record of every request, with its routing, tokens, cost and latency.
BudgetCaps on spend over a period, with notifications and blocks.

Providers & Models

Overview

How Multi-Router organizes models, providers, variants and regions, and how it uses your provider keys to route each request.

Multi-Router sends each request to a model provider, such as Anthropic, OpenAI, AWS Bedrock or Google Vertex, using credentials from your own account with that provider. A router decides which model serves a request and on which provider, variant and region; the model catalog supplies the model id and price for that choice; and a provider key supplies the credentials.

Setting up routing takes three steps. You add a provider key for each provider you use (see Provider Key), create a router that targets a model on one of those providers (see Routing), and send requests to the router with a Multi-Router API key (see API Access).

Routers and API keys

Multi-Router separates what a request does from who sends it. A router is a named object in an organization that holds the behavior for the requests sent to it: which model and provider serve them, which provider key is used, which variant and region they run in, and the prompt, parameter defaults and logging applied to them. An API key holds identity: it identifies the organization a request belongs to and what the caller is permitted to do. Because an API key carries no routing configuration, the behavior of every request is set by the router it names, and changing a router changes the behavior for every caller of that router without new API keys or changes to application code.

Every router has a type, and each type encapsulates one routing behavior. A request names a router in its model field, and the router's type determines what happens next:

Router typeBehavior
Static routerSends every request to one model on one provider, in a set variant and region, with a set provider key.
Fallback routerTries its member routers in order and returns the first successful response.
Weighted routerSplits traffic across its member routers by weight.
Claude ProxyServes Claude Code and Claude Desktop, forwarding their requests to Anthropic, AWS Bedrock or Google Vertex in the native format.
Pi ProxyServes the Pi.Dev coding agent, forwarding its requests to the provider in the native format with the developer's own credentials.

An alias is a stable name that points to a router. A request can name the alias in place of the router, and repointing the alias moves every caller to a different router at once. See Routing.

A static router holds the following behavior, all of which can change without changing its callers:

SettingEffect
ModelThe model that serves requests.
ProviderThe provider that runs the model.
VariantThe way the provider offers the model, such as a Bedrock inference scope or a dated snapshot.
RegionWhere inference runs, which with the variant sets the model id and price.
Provider keyThe credentials and provider account used, or the organization's default key.
Model sourceWhether a request selected with the x-mr-router header uses the request's model or the router's model.
Prompt bindingA saved prompt applied to every request, merged with or replacing the caller's system message.
Parameter defaultsValues such as temperature, maximum tokens, top_p and stop sequences applied when the request omits them.
Request loggingHow much of each request Traffic records, from none to full payloads.
TagsLabels that group routers, used by budgets and saved views.
Enabled stateA disabled router refuses every request, which stops its traffic at once.

An API key holds the following identity and permissions:

PropertyEffect
OrganizationThe organization whose routers, provider keys and budgets the request uses.
Inference accessAny router, Specific routers, or None: the routers the key can send requests to.
Management scopesRead, Write or Delete access to routers, prompts, model groups, budgets and other resources through the API.

The same router can serve several applications, each with its own API key, and one application can use several routers by naming a different router in each request. See Routing and API Access.

The building blocks

The following terms are used throughout the Providers & Models documentation.

TermDefinition
ModelA model identified by its slug, such as anthropic/claude-haiku-4.5, independent of where it is served.
PublisherThe company that built a model, which forms the first part of its slug.
ProviderA company or cloud platform that serves models, such as Anthropic, OpenAI, AWS Bedrock or Google Vertex. Providers are also called vendors.
OfferingOne provider and variant of a model, with the regions where it is available.
VariantOne way a provider offers a model, such as its standard endpoint or a Bedrock global inference profile.
RegionA location where a variant runs, with its own model id and price.
Model idThe id Multi-Router sends to the provider for a catalog entry, shown as Wire id in Traffic.
Model catalogThe list of models Multi-Router can serve, organized by model, provider, variant and region, with the model id, list price, capabilities and lifecycle status of each entry.
Provider keyThe credentials for one of your provider accounts, which Multi-Router uses to call that provider on your behalf.
Default keyThe provider key an organization uses for a provider when a router names none.
Model groupA named list of approved models, each with a provider and optionally a variant and region, used by Claude Proxies and tools policies.
Static routerA router that sends requests to one model on one provider.
AliasA stable name that points to a router and can be repointed without changing callers. An alias is not a router type.
Fallback routerA router that tries its member routers in order until one succeeds.
Weighted routerA router that splits traffic across its member routers by weight.
Claude ProxyA router that forwards Claude Code and Claude Desktop traffic to Anthropic, AWS Bedrock or Google Vertex in its native format.
Pi ProxyA router that forwards Pi.Dev traffic to the provider in its native format, with the developer's own credentials.
TrafficThe dashboard log of requests, with the router, provider, model, variant, region, tokens, cost, latency and status of each.
BudgetA spending limit that refuses requests once it is reached. See Budgets & Spend Control.

How models are organized

The model catalog organizes each model in four levels: the model itself, the providers that serve it, the variants each provider offers, and the regions where each variant runs. Together, these four values identify exactly one entry in the catalog. The catalog entries for anthropic/claude-haiku-4.5 are organized as follows:

Model
Provider
Variant
Regions
anthropic/claude-haiku-4.5
anthropic
standard
global
20251001
global
bedrock
geo
us-east-1, eu-central-1, ap-northeast-1 and more
global
us-east-1, eu-central-1, ap-northeast-1 and more
vertex
standard
global, us, eu, us-east5

Models and slugs

A slug is the unique name of a model in Multi-Router, in the form publisher/name, such as anthropic/claude-haiku-4.5 or openai/gpt-5. The publisher and the provider are separate: Anthropic publishes anthropic/claude-haiku-4.5, and Anthropic, AWS Bedrock and Google Vertex all serve it. A slug identifies the model regardless of which provider serves it, is matched without regard to case, and is the name you use for a model when configuring routers and model groups.

GET /v1/models returns the chat models your organization can call: models that are active or deprecated and have an offering that is not retired from a provider for which the organization holds an enabled provider key. See Model Catalog.

List available modelsbash
curl https://api.multi-router.ai/v1/models \
  -H "Authorization: Bearer $MR_API_KEY"

Providers

A provider is the platform that runs the model and bills for its use. When a model is available from several providers, the provider determines which account is billed, the price, the regions available and the date the model is retired, and in some cases a smaller context window than the model supports elsewhere. Multi-Router uses Google Vertex for Anthropic models and serves Google's Gemini models through the Gemini provider. See Supported Providers.

Variants and regions

A variant is one way a provider offers a model. Most providers offer a single standard variant; AWS Bedrock offers in_region, geo and global inference scopes; and several providers offer dated snapshots and faster or quantized builds. A region is a location where a variant runs, and the model id and price can differ from one region to another. When a router leaves the region unset, Multi-Router uses the provider key's region on AWS Bedrock, the organization default region on xAI, and global elsewhere, and Google Vertex always uses the location set on the provider key. Multi-Router never moves a request out of the selected region. See Regions & Variants.

The model tuple

The tuple (model, provider, variant, region) identifies one catalog entry: a specific model, served by a specific provider, in a specific variant and region. Each tuple has its own model id, which is the id Multi-Router sends to the provider, and its own price. The following are some of the tuples for anthropic/claude-haiku-4.5:

ProviderVariantRegionModel id sent to the providerInput / output per 1M tokens
anthropicstandardglobalclaude-haiku-4-5$1.00 / $5.00
anthropic20251001globalclaude-haiku-4-5-20251001$1.00 / $5.00
bedrockgeous-east-1us.anthropic.claude-haiku-4-5-20251001-v1:0$1.10 / $5.50
bedrockgeoeu-central-1eu.anthropic.claude-haiku-4-5-20251001-v1:0$1.10 / $5.50
bedrockglobalus-east-1global.anthropic.claude-haiku-4-5-20251001-v1:0$1.00 / $5.00
vertexstandardus-east5claude-haiku-4-5@20251001$1.10 / $5.50

Routers, keys and the catalog

A router connects a request to providers, provider keys and the catalog, and each kind of router does so differently. A static router stores a model, provider and variant and optionally a region, so moving an application to a different provider, variant or region requires a change to the router and no change to the application.

RouterProvider and modelProvider keyCatalog checks
Static routerOne model on one provider, with a variant and an optional region.The router's key, or the organization's default key for the provider.Every request is checked against the catalog entry. A router on a custom endpoint skips the checks.
Fallback routerThose of each member static router, tried in order.Each member's key.Each member's checks.
Weighted routerThose of each member static router, chosen by weight.Each member's key.Each member's checks.
Claude ProxyForwards Claude Code and Claude Desktop traffic in its native format to the route's provider, variant and region.The Claude Proxy's key, or the developer's own Anthropic credential. See Provider Key.Requests are checked against the models the route serves. See Agents & Editors.
Pi ProxyForwards Pi.Dev traffic in its native format to Anthropic or OpenAI.The developer's own provider credential.None. See Pi.Dev.

A request sent through an alias uses the provider key and catalog checks of the router the alias points to.

Addressing a router

A request selects its router through the model field or the x-mr-router header:

RequestResult
"model": "multi-router/rt_9Hk3Ws7PzQ"The router with that id serves the request.
"model": "multi-router/production"The alias production resolves to the router it points to.
"model": "production"The router or alias with that id serves the request.
x-mr-router: rt_9Hk3Ws7PzQThe header's router serves the request when model names no router. A static router uses the request's model when its Model source is Use request model, and its own model when it is Override with router's model.

A router named in model takes precedence over the header. A disabled router returns router_disabled, and an unknown router returns router_not_found.

The request lifecycle

The request lifecycle is the sequence of checks and calls Multi-Router performs for each Chat Completions or Messages request:

  1. Rate limit. Multi-Router counts the request against the API key's rate limit, and returns 429 rate_limit_exceeded when it is reached.
  2. Body checks. The body must be valid JSON within the endpoint's size limit, with a non-empty list of messages.
  3. Router selection. Multi-Router selects the router as described in Addressing a router.
  4. API key scope. An API key limited to specific routers receives 404 for any other router.
  5. Budget check. A router over its budget returns 429 budget_exhausted with a Retry-After header.
  6. Attempt. For a static router, and for each member a fallback or weighted router tries, Multi-Router applies the router's bound prompt and parameter defaults, translates the request to the provider's API, loads the provider key, checks the request against the catalog entry (model, mode, lifecycle, provider, variant and request features), selects the region, and calls the provider with the entry's model id. It retries rate limits, overloads, temporary server errors, timeouts and connection failures, honoring the provider's retry delay.
  7. Response. Multi-Router converts the provider's response to the format of the request, OpenAI for Chat Completions and Anthropic for Messages, and adds the routing headers.
  8. Cost and Traffic. Multi-Router prices the token usage, counts the cost toward budgets, and records the request in Traffic according to the router's Request Logging settings.

Fallback behavior

Fallback behavior is how a fallback or weighted router handles a failed attempt: failures in the member's configuration, a budget refusal, a catalog check, a timeout, or a rate limit, authentication or server error from the provider move the request to the next member, while an invalid request returns its error at once. See Fallback Router for the full list.

Response headers

Multi-Router adds the following headers to Chat Completions and Messages responses:

HeaderValueSent
x-mr-request-idA unique id for the request, which support uses to trace it.On every response, including errors.
x-mr-correlation-idThe value the request sent in x-mr-correlation-id.When the request sent one.
x-mr-providerThe provider that served the request.On successful responses.
x-mr-upstream-modelThe slug of the model that served the request.On successful responses.
x-mr-upstream-wire-idThe model id sent to the provider.On successful responses.
x-mr-regionThe region the request was sent to.On successful responses from AWS Bedrock and Google Vertex, and from other providers when the model has more than one region.
x-mr-policyHow the request named its router: static, fallback or weighted for that kind of router named in model, and header for a router selected with x-mr-router.On successful responses.
x-mr-attemptsThe number of routers tried: 1 for a static router, and the number of members tried for a fallback or weighted router.On successful responses.
x-mr-composerThe fallback or weighted router the request entered through.On successful responses through a fallback or weighted router.
x-mr-model-deprecatedtrueWhen the model is deprecated.
x-mr-model-replaced-byThe slug of the successor model.When the model is deprecated and the catalog names a successor.
Retry-AfterThe seconds until the budget period resets.On 429 budget_exhausted.

Requests can carry the following headers:

HeaderUse
x-mr-routerSelects a router.
x-mr-session-idGroups requests into a session, shown as Session in Traffic.
x-mr-correlation-idYour own id for the request, shown as Correlation ID in Traffic and returned in the response.

Costs

Multi-Router calculates the cost of each request from the token counts the provider reports and the catalog price of the region the request was served in. Cache reads and writes, long context rates, the fast service tier and web search calls use their own prices, and for Anthropic the region is the one Anthropic reports the request ran in. A custom variant replaces list prices with negotiated prices. Each cost appears in Traffic and counts toward budgets, while the provider bills your account directly at your agreed rates. A streamed response is priced when the stream ends, including a stream the client stops early. See Model Catalog and Custom Models & Pricing.

Traffic

Traffic is the dashboard's record of each request that reaches a router. The router's Request Logging settings control what it keeps: Request metadata (None, Failures only, Anonymous Metadata or Full Metadata) and Request payloads (None, Failures only, Sample or All Payloads). Cost counts toward budgets at every logging level. The columns that relate to providers and models are:

ColumnValue
RouterThe router the request entered through.
TypeThe kind of that router.
AliasThe alias used to reach the router, when there is one.
ModelThe slug of the model that served the request.
VendorThe provider that served the request.
VariantThe variant of the catalog entry.
RegionThe region the request was billed at.
Wire idThe model id sent to the provider.
In/OutThe input and output tokens.
CostThe cost of the request.
AttemptsThe value of x-mr-attempts.
CustomerThe request's user field.

The request detail view also shows Model requested, Model served, Session, Correlation ID, TTFT and Router overhead. See Observability.

Example

This example sends a request to Claude Haiku 4.5 on AWS Bedrock in the United States and reads how it was served:

  1. In LLM Provider Keys, add an AWS Bedrock key with the AWS Region us-east-1 and Access key (SigV4). Its id is llpk_4Vd8Qm2RtL.
  2. Create a static router on Bedrock geo, which returns the router id rt_9Hk3Ws7PzQ.
  3. Send a Chat Completions request to multi-router/rt_9Hk3Ws7PzQ with a correlation id.
  4. Read the response headers, and find the request in Traffic.
Create the routerbash
curl -X POST https://api.multi-router.ai/v1/routers \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "static",
    "name": "haiku-us",
    "routing": {
      "provider": "bedrock",
      "model": "anthropic/claude-haiku-4.5",
      "variant": "geo",
      "region": "us-east-1",
      "provider_key_id": "llpk_4Vd8Qm2RtL"
    },
    "prompt": { "system_message_mode": "merge" }
  }'
Call the routerbash
curl https://api.multi-router.ai/v1/chat/completions \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "x-mr-correlation-id: order-4411" \
  -d '{
    "model": "multi-router/rt_9Hk3Ws7PzQ",
    "messages": [{ "role": "user", "content": "Hello" }]
  }'
Response headershttp
x-mr-request-id: req_8Fq2LmXo1c
x-mr-correlation-id: order-4411
x-mr-provider: bedrock
x-mr-upstream-model: anthropic/claude-haiku-4.5
x-mr-upstream-wire-id: us.anthropic.claude-haiku-4-5-20251001-v1:0
x-mr-region: us-east-1
x-mr-policy: static
x-mr-attempts: 1

The router names the key's region, us-east-1, and the geo variant keeps inference in the United States. In Traffic, the request's row shows Router rt_9Hk3Ws7PzQ, Vendor bedrock, Variant geo, Region us-east-1, the wire id above, the input and output tokens, and a cost at $1.10 per million input tokens and $5.50 per million output tokens.

Engineering · org_7Tq2Wd9KxP

Overview · Traffic

CustomizeSave view
Time:
1h24h7d30d
CustomFilters
Search any id (request/correlation/session)…
Export
Requests
128,403
Success rate
99.4%
5xx errors
142
Tokens
84.2M
Latency · p95
1,240ms
Latency · p99
3,180ms
Cost
$1,284.50
Customers
312
Requests · Count / time128,403
Outcome · 5xx / time142
Latency · p95 / time1,240ms
Cost · Total / time$1,284.50
Requests
Page 125 / page
Columns
DateTimeRequest IDRouterAliasModelVendorIn/OutCostLatencyStatus
2026-09-3014:32:11req_Lw4Tn8Vb2ksupport-bot—anthropic/claude-sonnet-4.5anthropic2,310/512$0.011.94s200
2026-09-3014:32:09req_9Pc1RmYe4srt_9Hk3Ws7PzQ—anthropic/claude-haiku-4.5bedrock860/210$0.0021640ms200
2026-09-3014:32:07req_8Fq2LmXo1crt_9Hk3Ws7PzQ—anthropic/claude-haiku-4.5bedrock1,240/380$0.0035808ms200
req_8Fq2LmXo1c200anthropic/claude-haiku-4.5bedrock
Full Request Payload →
Latency
808ms
Cost
$0.0035
Tokens
1,240 → 380
TTFT
402ms
Attempts
1
Router overhead
1.2ms
Request
Request ID
req_8Fq2LmXo1c
Timestamp
14:32:07
Organization
org_7Tq2Wd9KxP
Token
tok_4Rf8zLm2Qa
Correlation ID
order-4411
Customer
—
Session
—
Peer IP
203.0.113.24
Routing
Alias
—
Router ID
rt_9Hk3Ws7PzQ
Model requested
anthropic/claude-haiku-4.5
Model served
anthropic/claude-haiku-4.5
Vendor
bedrock
Variant
geo
Region
us-east-1
Wire id
us.anthropic.claude-haiku-4-5-20251001-v1:0
Prompt binding
Prompt ID
—
File path
—
Revision
—
Latency
Total
808ms
TTFT
402ms
Request upload
180µs
Router overhead
1.2ms
Upstream
806ms
Router response
240µs
Tokens
Prompt
1,240
Completion
380
Request bytes
6,412
Response bytes
2,186
Cost
Total
$0.0035
Input
$0.0014
Output
$0.0021
Cache read
$0
Cache write
$0
Provider response
Request upload
180µs
Router · overhead
1.2ms
Upstream200
806ms
Router · response
240µs
Total 807.6ms
2026-09-3014:32:04req_3Zr6KdWq8nclaude-code-eng—anthropic/claude-sonnet-4.5vertex5,120/1,034$0.033.21s200
2026-09-3014:31:58req_6Ym2HcPa5tsupport-bot—anthropic/claude-sonnet-4.5anthropic0/0$0212ms429
The request in Traffic, expanded to show the model, vendor, variant, region and wire id that served it, and its cost. Select to enlarge.

The Providers & Models pages cover the following:

PageCovers
Supported ProvidersEach provider's id, key settings, feature support and format translation.
Provider KeyAdding, rotating, transferring, disabling and choosing keys.
Regions & VariantsChoosing where a request runs and which build serves it.
AWS Bedrock, Google VertexCloud account setup, regions and errors for each platform.
Model CatalogModel entries, capabilities, prices, lifecycle and catalog updates.
Model GroupApproved model sets for Claude Proxies and tools policies.
Custom Models & PricingNegotiated prices and models served from your own endpoints.

Routing, Agents & Editors, Observability and Budgets & Spend Control cover routers, Claude Proxies, Traffic and spending limits.

Providers & Models

Supported Providers

The model providers Multi-Router routes to, the settings each provider key needs, the request features each provider supports, and how requests and responses are translated.

Multi-Router routes requests to several kinds of model provider. Model publishers such as Anthropic, OpenAI and Google serve their own models. Inference platforms such as Fireworks, Groq and Together serve open models. Cloud platforms such as AWS Bedrock and Google Vertex serve models from many publishers across regions. Multi-Router also routes to custom endpoints that implement the OpenAI Chat Completions API. Your application calls every provider through the same two endpoints, Chat Completions and Messages, and Multi-Router translates each request into the provider's own API and each response back.

Providers

A provider is a company or cloud platform that serves models to Multi-Router, identified by a provider id that router configurations use and the x-mr-provider response header returns. A router whose provider does not serve its model returns provider_not_offered.

ProviderProvider idPublishersProvider key settings
AnthropicanthropicAnthropicAPI key
OpenAIopenaiOpenAIAPI key and Region (data residency, default Global)
Google GeminigeminiGoogleAPI key
CoherecohereCohereAPI key
AWS BedrockbedrockAmazon, Anthropic, DeepSeek, Meta, Mistral, Moonshot, Nvidia, OpenAI, Qwen, Z.aiAWS Region, and an access key (SigV4) or Bedrock API key. See AWS Bedrock.
Google VertexvertexAnthropicService account, project and location. See Google Vertex.
DeepSeekdeepseekDeepSeekAPI key
FireworksfireworksDeepSeek, Moonshot, OpenAI, Z.aiAPI key
GroqgroqMoonshot, OpenAIAPI key
MistralmistralMistralAPI key
TogethertogetherDeepSeek, Moonshot, OpenAI, Z.aiAPI key
xAIxaixAIAPI key
Z.aizaiZ.aiAPI key
Custom endpointcustomAny model the endpoint servesBase URL, API format and API key. See Custom endpoints below.

Provider endpoints

The provider endpoints are the hosts Multi-Router calls for each provider, with the authentication it uses and the regions it serves. A self-hosted installation allows outbound HTTPS on port 443 to the hosts of the providers it uses.

ProviderUpstream endpointAuthenticationRegions
Anthropichttps://api.anthropic.com/v1/messagesx-api-keyglobal, us
OpenAIhttps://api.openai.com/v1, and https://us.api.openai.com/v1, https://eu.api.openai.com/v1 or https://ae.api.openai.com/v1 for data residencyBearer tokenglobal, us, eu, ae
Google Geminihttps://generativelanguage.googleapis.com/v1betax-goog-api-keyglobal
Coherehttps://api.cohere.com/v2/chatBearer tokenglobal
AWS Bedrockhttps://bedrock-runtime.{region}.amazonaws.comAWS SigV4 or Bedrock API keyAWS regions
Google Vertexaiplatform.googleapis.com, aiplatform.us.rep.googleapis.com, aiplatform.eu.rep.googleapis.com and {region}-aiplatform.googleapis.com, plus oauth2.googleapis.comAccess token from the service accountGoogle Cloud locations
DeepSeekhttps://api.deepseek.com/v1Bearer tokenglobal
Fireworkshttps://api.fireworks.ai/inference/v1Bearer tokenglobal
Groqhttps://api.groq.com/openai/v1Bearer tokenglobal
Mistralhttps://api.mistral.ai/v1Bearer tokenglobal
Togetherhttps://api.together.xyz/v1Bearer tokenglobal
xAIhttps://api.x.ai/v1Bearer tokenus-east-1, us-west-2, eu-west-1
Z.aihttps://open.bigmodel.cn/api/paas/v4Bearer tokenglobal
Custom endpoint{base URL}/chat/completionsBearer tokenSet by the custom model

DeepSeek, Fireworks, Groq, Mistral, Together, xAI, Z.ai and custom endpoints use the OpenAI Chat Completions format, and Multi-Router sends them the same request it sends OpenAI, with max_tokens sent as max_completion_tokens. Regions are described in Regions & Variants.

Provider-specific behavior

ProviderBehavior
AnthropicSystem and developer messages anywhere in the conversation become the system prompt, and tool results are sent as user messages. Claude requests on Google Vertex and AWS Bedrock follow the same behavior.
Google GeminiSystem and developer messages are combined into Gemini's system instruction. Gemini returns no tool call id, so Multi-Router uses the function name as the id, and a tool result is matched to its call by function name.
CohereCohere receives the text of each message. A json_schema response format is sent as Cohere's JSON mode with the schema, and top_p and top_k are sent as p and k.
AWS BedrockImage URLs are downloaded and sent to Bedrock as image data. See AWS Bedrock.
Google VertexServes Anthropic models only, and requests for other publishers' models return publisher_not_supported. See Google Vertex.
xAIThe region is a label for the request: every region of a model has the same price and is served from the same endpoint.

Custom endpoints

The custom provider sends requests to an endpoint that implements the OpenAI Chat Completions API, such as a self-hosted model server. Its provider key holds the Base URL, the API format (OpenAI Chat Completions) and the API key, and Multi-Router appends /chat/completions to the base URL, which must use HTTPS and a public DNS name, with no port, credentials, query string or fragment. A router on a custom endpoint can name any model the endpoint serves, and requests skip the catalog checks. Requests carry no price unless the model is registered with a price in Custom Models & Pricing.

Feature support

Feature support shows which request features each provider supports: streaming responses, tool calls, parallel tool calls, image input, structured output that follows a JSON schema, reasoning, and prompt caching. Per model means some of the provider's models support the feature, and the Models page lists the features of each model. A request that uses a feature its model lacks returns an error named for the feature; see Model Catalog.

ProviderStreamingToolsParallel tool callsImagesStructured outputReasoningPrompt caching
AnthropicYesYesYesYesClaude 4.5 and laterClaude 3.7 and laterYes
Google VertexYesYesYesBase64 onlyClaude 4.5 and laterClaude 3.7 and laterYes
AWS BedrockPer modelPer modelClaude modelsPer modelNoClaude 3.7 and laterClaude models
OpenAIPer modelPer modelPer modelPer modelPer modelPer modelPer model
Google GeminiPer modelYesNoYesPer modelPer modelPer model
CohereYesYesNoPer modelYesPer modelNo
DeepSeekYesYesNoNoYesYesYes
FireworksYesYesNoPer modelYesPer modelPer model
GroqYesYesNoNoYesPer modelNo
MistralYesYesNoPer modelYesNoNo
TogetherYesYesNoPer modelYesPer modelPer model
xAIYesYesYesYesYesPer modelYes
Z.aiYesPer modelNoPer modelPer modelPer modelPer model

Claude features by endpoint

Claude models on Anthropic, Vertex and Bedrock support some features on only one of the two endpoints, as follows:

Feature/v1/chat/completions/v1/messages
Structured outputresponse_format with json_schema, on Anthropic and VertexNot available
ReasoningNot availablethinking with type: "enabled" and budget_tokens, returned as thinking blocks
Prompt cachingNot availablecache_control on content blocks, system blocks and tools
Maximum output tokensmax_tokens or max_completion_tokens; 1,024 when neither the request nor the router sets onemax_tokens is required

Input content

Input content is the set of content types a request message can contain. Requests can contain text, images, tool calls and tool results; images are sent as a URL or, on Chat Completions, as a data: URL with base64 data. Google Vertex accepts base64 images only, AWS Bedrock receives URL images after Multi-Router downloads them, and Cohere receives text only. On Chat Completions, a tool message keeps its text parts, and a content part of any type other than text and image returns not_supported. The Messages endpoint also accepts thinking blocks, and removes other block types, such as redacted_thinking and search result blocks, before sending the request.

Translation between API formats

Translation is the conversion of a request into the provider's API and of the response back into the format of the request. Multi-Router accepts OpenAI Chat Completions requests at /v1/chat/completions and Anthropic Messages requests at /v1/messages, and either format can target any provider. Messages, system prompts, text, tools, tool choice, tool calls and results, stop sequences, temperature, top_p and maximum tokens are converted for every provider. Other fields are converted when the provider's API has an equivalent and removed otherwise, and fields Multi-Router does not recognize are removed.

EndpointProviderFields removed
Chat CompletionsOpenAIImage detail. Every other recognized field is sent.
Chat CompletionsOpenAI-compatible providersImage detail. Every other recognized field is sent.
Chat CompletionsClaude modelsreasoning_effort, seed, logprobs, top_logprobs, penalties, logit_bias, parallel_tool_calls, prompt_cache_key, prompt_cache_retention, service_tier, store, metadata, user, safety_identifier, verbosity, prediction, tool strict, image detail
Chat CompletionsGoogle Geminireasoning_effort, prompt cache fields, parallel_tool_calls, penalties, logprobs, top_logprobs, logit_bias, service_tier, store, metadata, user, safety_identifier, verbosity, prediction, tool strict, non-text tool results
Chat CompletionsCohereImages, reasoning_effort, prompt cache fields, parallel_tool_calls, logprobs, top_logprobs, logit_bias, service_tier, store, metadata, user, safety_identifier, verbosity, prediction, tool strict
Chat CompletionsNon-Claude models on BedrockEvery field outside the converted set.
MessagesEvery provideroutput_config, service_tier, container, mcp_servers, context_management, disable_parallel_tool_use
MessagesOpenAItop_k, thinking and thinking blocks, cache_control, metadata, non-text tool result content. A tool result with is_error is sent with a [Tool Error] prefix.
MessagesOpenAI-compatible providerstop_k, thinking and thinking blocks, cache_control, metadata, non-text tool result content. A tool result with is_error is sent with a [Tool Error] prefix.
MessagesGoogle Geminithinking and thinking blocks, cache_control, non-text tool result content
MessagesCoherethinking and thinking blocks, cache_control, non-text tool result content, images

The anthropic-beta request header is forwarded to Anthropic and Google Vertex. Reasoning text from the model is omitted from Chat Completions responses. Tool call ids pass through unchanged in both directions, so a tool result names the id the provider issued: toolu_ ids from Claude, call_ ids from OpenAI, and the function name from Gemini.

The following requests return an error:

RequestError
Chat Completions with n greater than 1not_supported
Chat Completions with a name on a messagenot_supported
Chat Completions with a tool other than a functionnot_supported
Chat Completions with a content part other than text and imagenot_supported
A json_object or json_schema response format sent to a Claude model before 4.5structured_output_not_supported
A json_object response format sent to Claude 4.5 or laterinvalid_request
Any response_format sent to Claude on AWS Bedrockinvalid_request
Messages with thinking enabled and no budget_tokensinvalid_request_error
Messages with a system role inside messagesinvalid_request_error

Streaming

Streaming returns the response as server-sent events in the format of the endpoint, whatever format the provider streams in. Multi-Router always asks the provider for token usage, so Traffic records token counts whether or not the request asks for them.

Chat CompletionsMessages
Eventsdata: {chat.completion.chunk} lines, ending with data: [DONE]message_start, content_block_start, content_block_delta, content_block_stop, message_delta, message_stop, and ping
KeepaliveA : keepalive comment after 15 seconds without dataThe same
UsageA final chunk with empty choices and usage, when the request sets stream_options.include_usagemessage_delta.usage
Tool callsThe first chunk of each call carries id, name and index, and later chunks carry argument fragmentscontent_block_start with tool_use, then input_json_delta
ReasoningOmittedthinking_delta and signature_delta
Errors after the stream startsdata: {"error": ...}, then data: [DONE]event: error

Token usage

Token usage is the count of tokens the provider reports, returned in the format of the endpoint:

Chat CompletionsMessages
prompt_tokensinput_tokens
Input tokens as the provider reports them. Claude providers exclude cache reads and writes, and OpenAI, OpenAI-compatible providers and Gemini include cached tokens.
completion_tokensoutput_tokens
Output tokens, including reasoning tokens.
total_tokensNot returned
Input and output tokens combined.
prompt_tokens_details.cached_tokenscache_read_input_tokens
Tokens read from the prompt cache.
Not returnedcache_creation_input_tokens
Tokens written to the prompt cache.
Not returnedcache_creation
Tokens written to the prompt cache, broken down by cache lifetime.
completion_tokens_details.reasoning_tokensNot returned
Reasoning tokens, when the provider reports them.
Not returnedinference_geo
The region Anthropic reports the request ran in.

Response fields

Response fields follow the format of the endpoint, with the following values set by Multi-Router. The model field returns the value the request sent, such as the router name, and the x-mr-upstream-model and x-mr-upstream-wire-id headers name the model that served it. The id is the provider's response id, prefixed with chatcmpl- on Chat Completions. created is the time Multi-Router built the response, and Messages responses return stop_sequence as null. Stop reasons are mapped as follows:

Provider reasonChat Completions finish_reasonMessages stop_reason
end_turnstop, or tool_calls when the response has tool callsend_turn
stopstop, or tool_calls when the response has tool callsend_turn
completestop, or tool_calls when the response has tool callsend_turn
stop_sequencestopstop_sequence
max_tokenslengthmax_tokens
lengthlengthmax_tokens
tool_usetool_callstool_use
tool_callstool_callstool_use
content_filtercontent_filterend_turn
safetycontent_filterend_turn
Any other reason, such as refusalstopend_turn

Provider errors and retries

A provider error is a failure the provider returns after Multi-Router sends the request. Multi-Router returns it with a Multi-Router error code and an upstream object holding the provider, the provider's error code and its HTTP status; the provider's own message is not passed through, because it can contain account details.

Provider error on Chat Completionsjson
{
  "error": {
    "message": "upstream provider rate limited this request after multiple retries",
    "type": "upstream_error",
    "code": "upstream_rate_limit",
    "upstream": { "provider": "anthropic", "code": "max_attempts_exceeded", "status": 529 }
  }
}
ErrorStatusProvider responses
upstream_invalid_request400The provider rejected the request as invalid.
upstream_context_length400The prompt exceeds the model's context window.
upstream_content_filter400The provider's content policy blocked the request.
upstream_model_not_found400The provider does not offer the model to the account.
upstream_invalid_credentials502The provider rejected the provider key.
upstream_insufficient_quota402The provider account has no remaining credit or quota.
upstream_rate_limit429The provider rate limited or was overloaded, and retries ran out.
upstream_transport503The connection failed while the response was being read.
upstream_otherThe provider's statusAny other provider error.

On the Messages endpoint, the same failures use Anthropic error types chosen by status: invalid_request_error for 400, rate_limit_error for 429, overloaded_error for 503 and api_error otherwise, with the upstream object included.

Multi-Router retries a provider request that fails with HTTP 429, 500, 502, 503, 504 or 529, a timeout or a connection failure, for up to five attempts. Waits double from one second with up to 25% random jitter, up to 60 seconds, and a whole-second Retry-After from the provider replaces the computed wait. Retries stop when another attempt would run past five minutes from the first, and a 429 that reports exhausted credit is returned at once. These rules apply to every provider and to custom endpoints, and Claude Proxy requests are sent once, without retries.

Limits

Limits are the size and time bounds Multi-Router applies to provider requests:

LimitValueWhen exceeded
Chat Completions request body4 MiB413 body_too_large
Messages request body32 MB413 request_too_large
Time to the provider's response headers, per attempt120 secondsThe attempt is retried. Long generations use streaming.
Total time across retries5 minutesRetries stop.

Claude Code and Claude Desktop

A Claude Proxy forwards requests from Claude Code and Claude Desktop to Anthropic, AWS Bedrock or Google Vertex in their native API formats, replacing only the model id, so every feature of those APIs is available. See Agents & Editors.

Providers & Models

Provider Key

Add, configure, rotate, transfer and remove the credentials Multi-Router uses to call model providers.

A provider key is the credential for one of your accounts with a model provider, such as an Anthropic API key or an AWS Bedrock access key, and Multi-Router uses it to call that provider on your behalf. Each provider key belongs to one organization, each organization holds its own keys, and each organization has one default key per provider. Provider keys are created and changed in the dashboard, on the LLM Provider Keys page in the profile menu, or through the API with /v1/provider-keys. Provider key secrets are encrypted at rest and in transit, and a saved secret is never displayed or returned.

Who can manage provider keys

Provider keys are managed by the admins of the organization that holds them. An admin of a named organization adds, edits, disables, deletes, transfers and sets default keys for that organization, and an account admin does the same for the keys held by the account itself. Organization members use the organization's keys through its routers.

The provider keys page

The provider keys page shows every key in the selected organization, grouped by provider. The Providers list on the left shows each provider with its number of keys and the name of its default key, or Not configured when it has none, and selecting a provider shows its keys with a summary such as 2 keys · default production. Each key row shows the following:

LLM Provider Keys

Upstream credentials used to call providers. The default key for each provider is what gets used when a request reaches that provider without a router preset overriding it. Secrets are encrypted at rest; the value can’t be retrieved after creation.

B
AWS Bedrock
2 keys · default Bedrock production
Add AWS Bedrock key
  • Bedrock productionDefault

    Primary Bedrock credentials for production traffic. Owner: platform team. Rotated quarterly.

    wJalrXUtnF••••••••••••••••••••sha256 3f9a2c1e…Created 9/12/2026
    TransferDisableDelete
  • Bedrock stagingDisabled

    Staging account. Disabled after the staging stack moved to us-west-2.

    Kd8xQm2PzR••••••••••••••••••••sha256 b71e04d9…Created 8/30/2026Disabled 9/20/2026
    TransferEnableDelete
The LLM Provider Keys page with AWS Bedrock selected: a default key and a disabled key. Select to enlarge.
ItemMeaning
NameThe key's label.
DescriptionShown below the name when set.
DefaultThe key is the organization's default key for the provider.
DisabledThe key is disabled and serves no requests.
Region badgeThe data residency of an OpenAI key set to US, EU or UAE.
Secret prefixThe first 10 characters of the secret, followed by dots.
sha256The beginning of the secret's fingerprint, with the full value in the tooltip. Two keys that hold the same secret have the same fingerprint.
CreatedThe date the key was added.
Disabled dateThe date the key was disabled, for a disabled key.

Each row offers the following actions:

ActionAvailableEffect
Make defaultOn enabled keys that are not the defaultMakes the key the provider's default key.
EditAlwaysOpens the edit panel.
TransferAlwaysCopies or moves the key to another organization.
DisableOn enabled keysStops new requests from using the key.
EnableOn disabled keysResumes the key's use.
DeleteWhile the key is disabledAsks to confirm, then removes the key permanently. The secret cannot be recovered.

Adding a provider key

Adding a provider key stores a credential for one provider in the selected organization:

  1. Open LLM Provider Keys from the profile menu.
  2. Select the provider in the Providers list. A provider with no keys opens the new-key form directly; otherwise select Add <Provider> key.
  3. Complete the form titled New <Provider> key, then select Save key.
ProviderFields
AnthropicName, API key, Description.
Google GeminiName, API key, Description.
CohereName, API key, Description.
DeepSeekName, API key, Description.
FireworksName, API key, Description.
GroqName, API key, Description.
MistralName, API key, Description.
TogetherName, API key, Description.
xAIName, API key, Description.
Z.aiName, API key, Description.
OpenAIName, Region (Global, United States, Europe or UAE), API key, Description. The Region is the data residency of the OpenAI project the key belongs to.
AWS BedrockAccess key (SigV4) or API key (bearer); Name, AWS Region, AWS Access Key ID, Secret access key or Bedrock API key, Description. See AWS Bedrock.
Google VertexName, Google Cloud project ID, Google Cloud location, Service-account JSON, Description. See Google Vertex.
Custom endpointName, Base URL, API format (OpenAI Chat Completions), API key, Description. See Supported Providers.

Name is required and up to 80 characters, names are labels and can repeat, and Description is optional and up to 2,000 characters. Multi-Router trims spaces from the secret and rejects an empty one, and checks every provider setting when the key is saved. A Google Vertex key is verified with Google when it is saved; the secrets of other providers are checked by the provider, so an invalid secret returns the provider's authentication error on the first request that uses it. A key added while its provider has no default becomes the default.

Obtaining a provider secret

Each provider issues the secret Multi-Router stores. Create a key that only Multi-Router uses:

ProviderWhere to create itTypical prefix
AnthropicClaude Console, Settings, API Keyssk-ant-
OpenAIOpenAI Platform, API keys, within a projectsk-proj-
Google GeminiGoogle AI Studio, Get API keyAIza
CohereCohere dashboard, API Keys (a production key)
DeepSeekDeepSeek Platform, API keyssk-
FireworksFireworks, Settings, API Keysfw_
GroqGroqCloud console, API Keysgsk_
MistralMistral console, API Keys
TogetherTogether, Settings, API Keys
xAIxAI Console, API Keysxai-
Z.aiZ.ai Open Platform, API Keys
AWS BedrockSee AWS Bedrock
Google VertexSee Google Vertex

The default key

The default key is the key an organization uses for a provider when a router names no key. A key added while its provider has no default becomes the default, so the first key for each provider is the default, and Make default on another enabled key of the same provider replaces it. Disabling the default key leaves it as the default, so requests that rely on it fail until it is enabled again or another key is made the default. Deleting the default key makes the oldest enabled key of the same provider the default, and leaves the provider with no default when it has no other enabled key; a key enabled later becomes the default only through Make default.

Which key a request uses

Multi-Router selects the provider key for each request from the router that serves it:

RequestKey used
Static router with a keyThe key set on the router.
Static router set to Org-default keyThe organization's default key for the router's provider.
AliasThe key of the router the alias points to.
Fallback or weighted routerThe key of each member router.
Claude Proxy with organization credentialsThe key set on the Claude Proxy, or, on the Custom route, the key the Claude Proxy assigns to each model in its model group.
Claude Proxy with developer credentialsThe developer's own Anthropic credential, sent with the request.
Pi ProxyThe developer's own provider credential, sent with the request.

Static routers created with POST /v1/routers name a key in routing.provider_key_id, which must be an enabled key of the router's provider in the same organization. Static routers created in the dashboard can select Org-default key instead, which follows the default as it changes, starting with the next request. See Routing.

ExampleResult
Router names openai-prod; the default is openai-devUses openai-prod.
Router set to Org-default key; the default is openai-devUses openai-dev, and openai-prod after Make default on it.
Router set to Org-default key; the default key is disabledReturns provider_key_disabled.
Router names a key that has been deletedReturns router_pinned_key_missing.
Fallback router whose members use different keysEach member uses its own key.

Editing a key

Editing changes a key's details without changing its secret. Edit opens a panel titled Edit <name> with Name and Description, plus Region for an OpenAI key, and Google Cloud project ID and Google Cloud location for a Google Vertex key; select Save to apply it. A changed Vertex project or location is verified with Google before it is saved. The provider, the secret, and the region and credentials of an AWS Bedrock key are fixed; to change them, add a new key.

Rotating a key

Rotating a key replaces it with a new key that holds a new secret, without interrupting requests:

  1. Add a new provider key with the new secret.
  2. If the old key is the default for its provider, select Make default on the new key.
  3. Change every router, Claude Proxy and model group that names the old key to the new key, and change the key in any policy that names it.
  4. Disable the old key and confirm that requests continue to succeed.
  5. Delete the old key, then revoke the old secret with the provider.

With the API, POST /v1/provider-keys/{id}/rotate replaces the secret of a key in place, keeping its id, so routers, Claude Proxies and model groups that name the key need no change.

Disabling and deleting a key

Disabling a key stops Multi-Router from using it while keeping it for later use. Requests already in progress complete, and new requests from routers that name the key, or that rely on it as the default, return provider_key_disabled. A key named by a Claude Proxy can be disabled only after the Claude Proxy's key is replaced or removed. Enabling a Google Vertex key verifies it with Google again.

Deleting a key removes it permanently. A key must be disabled before it is deleted, and a key named by a model in a model group can be deleted only after it is removed from the group. Routers that name a deleted key return router_pinned_key_missing until they are changed to another key.

Copying and moving keys between organizations

Transfer copies or moves a provider key to another organization in the same account. It requires the admin role in both organizations, and an account admin is an admin of every organization in the account.

  1. Select Transfer on the key's row, and choose Copy or Move.
  2. Choose the Destination org. The list shows the organizations in the account where you are an admin, other than the current one, and shows the account itself under the account's name.
  3. Change Name in destination and Description if needed; both start with the key's current values.
  4. Select Copy to <organization> or Move to <organization>.

LLM Provider Keys

Upstream credentials used to call providers. The default key for each provider is what gets used when a request reaches that provider without a router preset overriding it. Secrets are encrypted at rest; the value can’t be retrieved after creation.

B
AWS Bedrock
2 keys · default Bedrock production
Add AWS Bedrock key
  • Move Bedrock production to another org

    copymove
    Cancel
    Destination org
    Research
    org_R4kq8Vn2Ws

    Only orgs in your account. The current org is excluded; keys can’t cross accounts.

    Name in destination
    Bedrock production

    Prepopulated from this key — edit to rename or avoid a clash.

    Description
    Primary Bedrock credentials for production traffic. Owner: platform team. Rotated quarterly.

    Prepopulated; edit freely. The secret is carried over and re-encrypted — never shown.

    This key is moved to the destination and removed from this org. Anything here pointing at it must be repointed.

    This is the default bedrock key here — moving it promotes the next enabled key (or leaves this org with no default).
    An identical key already exists in Research as bedrock-research. You can still proceed — this would be a second copy of the same secret.
    CancelMove to Research
  • Bedrock stagingDisabled

    Staging account. Disabled after the staging stack moved to us-west-2.

    Kd8xQm2PzR••••••••••••••••••••sha256 b71e04d9…Created 8/30/2026Disabled 9/20/2026
    TransferEnableDelete
The Transfer panel moving the default Bedrock key to the Research organization, with the duplicate warning. Select to enlarge.
CopyMove
Source organizationKeeps its key unchanged.The key is removed.
Allowed whenAlways.No router or model group in the source organization names the key. Otherwise the panel lists them.
Source defaultUnchanged.The oldest enabled key of the same provider becomes the default, or the provider has no default.

The secret, provider settings, enabled state and fingerprint carry over. The transferred key becomes the destination's default when it is enabled and the destination has no default for its provider. When the destination already holds a key of the same provider with the same secret, the panel warns An identical key already exists in <organization> as <name>, and the transfer can still proceed.

Security practices

Security practices for provider keys follow from how Multi-Router uses them:

PracticeDescription
Grant inference onlyMulti-Router uses a key only to call models, so create the key with only that permission: the IAM policy on the AWS Bedrock page, the Vertex AI User role on Google Vertex, and a key restricted to model calls where the provider supports it.
Use a key per environmentAn organization holds several keys per provider and each router names its own key, so production and test traffic can use different secrets and be disabled independently.
Keep the secret in your secret managerMulti-Router never displays a saved secret. Record the key's owner and rotation schedule in its Description.
Rotate on a scheduleThe Created date shows each key's age. Rotate with the steps above.
Respond to exposureDisable the key in Multi-Router, which stops new requests from using it, then revoke the secret with the provider.
Check fingerprintsKeys with the same fingerprint hold the same secret, which shows where one secret is used across keys and organizations.

Errors

Requests and router changes return the following errors for provider keys, each with HTTP status 400:

ErrorReturnedCauseResolution
no_provider_key_configuredOn requestsThe router names no key and the organization has no default key for the provider.Add a key for the provider, or select Make default on an enabled key.
provider_key_disabledOn requests, and when saving a router with /v1/routersThe key named by the router, or the default key, is disabled.Enable the key, or change the router or the default to an enabled key.
router_pinned_key_missingOn requestsThe router names a key that has been deleted.Change the router to a current key.
provider_key_mismatchOn requests, and when saving a router with /v1/routersThe key belongs to a different provider than the router's model.Set a key of the correct provider.
provider_key_not_foundWhen saving a router with /v1/routersThe router names a key that is not in the organization.Use a key id from GET /v1/provider-keys.
key_residency_mismatchOn requests and router savesThe region of the router or request differs from the region of its OpenAI key, or the key's region is invalid.Use a key with the matching region, or edit the key.
key_not_foundWhen saving a Claude ProxyThe Claude Proxy uses organization credentials and names no key, a key not in the organization, or a disabled key.Set an enabled key on the Claude Proxy.
key_vendor_mismatchWhen saving a Claude ProxyThe key belongs to a different provider than the Claude Proxy's route.Set a key of the route's provider.
key_region_mismatchWhen saving a Claude ProxyA Bedrock API key was created for a region other than the Claude Proxy's region.Use a key for the Claude Proxy's region, or an access key (SigV4).
credential_not_allowedWhen saving a Claude ProxyDeveloper credentials are used on a route other than Anthropic, or the Custom route names a single key.Use organization credentials, with a key for each model on the Custom route.

The dashboard reports the following when a key action is refused:

ActionRefused when
AddA required field is empty, a field is too long, or a provider setting is invalid, such as a Bedrock region or a Vertex service account Google does not accept.
EditA field is too long, or a changed Vertex project or location fails verification.
Make defaultThe key is disabled, a Vertex key fails verification, or the default changed at the same time.
EnableA Vertex key fails verification.
DisableA Claude Proxy names the key.
DeleteThe key is enabled, a Claude Proxy or model group names it, or it changed at the same time.
TransferThe destination is the current organization, you are not an admin of both organizations, or, for a move, a router or model group names the key.

Managing keys with the API

The /v1/provider-keys endpoints act on the provider keys of the API key's organization. Reads never return a secret; a create or rotate returns the new secret once. Scopes are cumulative: provider_keys:delete includes write, and write includes read.

MethodPathDescriptionScope
GET/v1/provider-keysList keys, including disabled keys, paged with limit (1 to 200, default 50) and cursor.provider_keys:read
POST/v1/provider-keysAdd a key from name, provider, secret and the provider's settings. Accepts dry_run.provider_keys:write
GET/v1/provider-keys/{id}Get one key.provider_keys:read
PATCH/v1/provider-keys/{id}Change name, description, enabled, is_default, the Bedrock region, the Vertex project and location, or the OpenAI region. Accepts dry_run.provider_keys:write
POST/v1/provider-keys/{id}/rotateReplace the secret, keeping the key id.provider_keys:write
POST/v1/provider-keys/{id}/transferCopy or move the key to another organization.provider_keys:write; a move also provider_keys:delete
DELETE/v1/provider-keys/{id}Delete a disabled key that nothing uses.provider_keys:delete

Each key has its id, prefixed llpk_, used in provider_key_id, with name, description, provider, key_prefix, enabled, is_default, its etag, and the provider's section: bedrock with region, auth_mode and access_key_id; vertex with project_id and region; openai with data_residency; or custom with base_url and api_format. See Provider Keys in the API Reference for every field and error.

Requestbash
curl "https://api.multi-router.ai/v1/provider-keys?limit=50&cursor=$NEXT_CURSOR" \
  -H "Authorization: Bearer $MR_API_KEY"
Response 200json
{
  "organization_id": "org_7Tq2Wd9KxP",
  "data": [
    {
      "id": "llpk_4Vd8Qm2RtL",
      "object": "provider_key",
      "name": "Bedrock production",
      "description": null,
      "provider": "bedrock",
      "bedrock": { "region": "us-east-1", "auth_mode": "sigv4", "access_key_id": "AKIAIOSFODNN7EXAMPLE" },
      "key_prefix": "wJalrXUtnF",
      "enabled": true,
      "is_default": true,
      "etag": "\"3\"",
      "created_at": "2026-09-12T17:47:43Z",
      "updated_at": "2026-09-30T08:12:09Z"
    }
  ],
  "next_cursor": null,
  "has_more": false
}

Providers & Models

Regions & Variants

Choose which build of a model serves a request and where it runs, and keep inference within a region or geography.

A variant is one way a provider offers a model, and a region is a location where that variant runs. The variant and region determine the model id sent to the provider, the price, and where inference takes place, which makes them the settings that control data residency, latency and cost. A static router names its variant and can name its region; when the router leaves the region unset, Multi-Router takes it from the provider key or the organization.

Variants

A variant distinguishes offerings of the same model on the same provider that differ in where inference runs, in serving speed, or in the build of the model. Each variant has an id, which routers and the API use, and a display name, which the dashboard shows.

VariantDisplay nameOffered byDescription
standardStandardMost providersThe provider's ordinary endpoint for the model.
in_regionIn-regionAWS BedrockInference runs only in the AWS region that receives the request.
geoGeoAWS BedrockInference runs within one geography, such as the United States or Europe.
globalGlobalAWS BedrockInference can run in any AWS region, with no data residency guarantee.
fastFastFireworksA higher-throughput serving path, priced above the standard endpoint.
fp8FP8FireworksAn 8-bit quantized build.
fp8-fastFP8 FastFireworksAn 8-bit quantized build on the higher-throughput serving path.
fp4FP4TogetherA 4-bit quantized build. Some Together models are offered only in this variant.
Snapshots, such as 20251001Such as 1 Oct 2025 snapshotAnthropic, AWS Bedrock, Fireworks, Together, xAIA specific build of the model. See Snapshots below.

Snapshots

A snapshot is a variant pinned to one dated build of a model, for applications that need the model's behavior to stay fixed. On Anthropic, the standard variant sends the undated model id, such as claude-haiku-4-5, which Anthropic points at its current build, and the snapshot variant 20251001 sends the dated id claude-haiku-4-5-20251001 at the same price. On AWS Bedrock the snapshot is combined with the inference scope, as in geo-20241022 (Geo, Snapshot 22 Oct 2024) and in_region-20240620, and some Bedrock models offer revisions such as in_region-v1-0. Fireworks, Together and xAI name snapshots by build number, such as 0813 and 0309.

Variant lifecycle

Each variant has its own lifecycle status, so a model can be retired in one variant or on one provider and remain available in another, such as a model retired on Anthropic whose AWS Bedrock geo variant is deprecated. A deprecated variant continues to serve requests, and its responses carry the x-mr-model-deprecated header. A router on a retired variant returns router_variant_retired, and the error message lists the variants of the model that the provider still serves. A router always uses the variant it names, and Multi-Router does not substitute another variant.

Regions

A region is a location where a variant is offered, and each region has its own model id and price. What the region controls depends on the provider:

ProviderRegion valuesWhat the region controls
AWS BedrockAWS regions, such as us-east-1 and eu-central-1The AWS region that receives the request. The variant then sets where inference runs. See AWS Bedrock.
Google Vertexglobal, us, eu and single Google Cloud regions, such as us-east5The Google Cloud location that runs the request, which is always the location set on the provider key. See Google Vertex.
OpenAIglobal, us, eu, aeus, eu and ae (UAE) send the request to OpenAI's data residency endpoint for that region. Some OpenAI models offer these regions.
Anthropicglobal, usus keeps inference in the United States. Some Anthropic models offer this region.
xAIus-east-1, us-west-2, eu-west-1A label for the request. Every region of a model has the same price, and xAI serves every region from the same endpoint.
Other providersglobalThe provider serves the model from a single endpoint.

Google Vertex locations

Google Vertex offers three types of location, and the models available differ from one location to another:

LocationWhere inference runs
globalIn a location Google chooses. This is usually the lowest price, and some models are offered only here.
usWithin the United States.
euWithin the European Union.
A single region, such as us-east5, europe-west1 or asia-southeast1Only in that region.

AWS Bedrock

AWS Bedrock offers each model in up to three inference scopes, and the AWS region that receives the request is the router's region or, when the router names none, the provider key's region. The inference scope then determines where the request runs:

VariantWhere inference runsPrice
in_regionOnly in the AWS region that receives the request.The same as geo in regions that offer both.
geoAnywhere within the geography of the receiving region.The same as in_region in regions that offer both.
globalIn any AWS region.Usually the lowest of the three.

The geo variant covers the following geographies. Some regions belong to two geographies, and the model determines which applies: ap-northeast-1, for example, is in the Japan geography for Claude Haiku 4.5 and in the Asia Pacific geography for other models. The Models page shows the model id for each region.

GeographyAWS regions
United Statesus-east-1, us-east-2, us-west-1, us-west-2, ca-central-1, ca-west-1
Europeeu-central-1, eu-central-2, eu-north-1, eu-south-1, eu-south-2, eu-west-1, eu-west-2, eu-west-3, il-central-1
Asia Pacificap-east-2, ap-northeast-1, ap-northeast-2, ap-northeast-3, ap-south-1, ap-south-2, ap-southeast-1, ap-southeast-2, ap-southeast-3, ap-southeast-4, ap-southeast-5, ap-southeast-7
Japanap-northeast-1, ap-northeast-3
Australiaap-southeast-2, ap-southeast-4, ap-southeast-6
Indiaap-south-1

The global variant is also offered in regions outside these geographies, such as sa-east-1, af-south-1, me-central-1 and mx-central-1. The in_region variant is offered for the fewest models; Claude Haiku 4.5, for example, is offered on Bedrock in geo and global only. A Bedrock provider key's region is chosen from the AWS regions listed in the dashboard's key form, and a router can name any region the catalog lists for its variant. A Bedrock API key works only in the region it was created for, so a router that uses one must use that region.

Choosing a variant and region

The variant and region to choose depend on the residency, availability and price the application requires:

RequirementVariants and regions
Inference in a single regionAWS Bedrock in_region, or a single Google Vertex region.
Inference within one geographyAWS Bedrock geo, Google Vertex us or eu, Anthropic us, or OpenAI us, eu or ae.
Lowest price and widest availabilityAWS Bedrock global, Google Vertex global, or the global region of any other provider.
A fixed model versionA snapshot variant.
Higher throughputFireworks fast or fp8-fast.

How the region is selected

Multi-Router selects the region for each request from the router, the provider key and the organization, in the following order for each provider:

ProviderRegion used
AWS BedrockThe router's region, otherwise the provider key's region.
Google VertexThe location set on the provider key.
OpenAIThe router's region, otherwise global. The router's region, including global when it is unset, must match the region of its provider key.
AnthropicThe router's region, otherwise global.
xAIThe router's region, otherwise the organization's default region.
Other providersglobal

Multi-Router sends every request to the selected region and never switches it to another region. When the model is not offered in the selected region, the request returns region_not_served and the error message lists the regions where the model is available.

Organization default region

The organization default region is the region used by routers on providers whose provider keys carry no region, such as xAI, when the router names no region. Organization admins set it in the Default region card on the Organizations page, which opens from Manage Organizations in the profile menu.

Examples

RouterResult
Bedrock geo, model anthropic/claude-haiku-4.5, no region, key region us-west-2Sent to us-west-2 with the model id us.anthropic.claude-haiku-4-5-20251001-v1:0. Inference stays in the United States.
The same router with region ap-northeast-1Sent to ap-northeast-1 with a jp. model id. Inference stays in Japan.
The same router with key region sa-east-1 and no router regionReturns region_not_served, because Claude Haiku 4.5 has no geo offering in sa-east-1. Set a router region, or use global.
xAI, no region, organization default us-west-2Served at us-west-2. With no organization default, returns region_not_served.
OpenAI, no region, key region euReturns key_residency_mismatch, because an unset region means global. Set the router region to eu.
Anthropic, no regionServed at global, whatever region the key or the organization names.

Configuring a router

A static router sets its variant in routing.variant and its region in routing.region. Through the API, both are required for every provider except custom endpoints; in the dashboard, the region can be left to inherit. Multi-Router checks both against the catalog when the router is created with POST /v1/routers or changed with PATCH /v1/routers/{id}, and rejects a variant the provider does not offer for the model and a region the variant does not offer. The offerings and regions of a model are read with GET /v1/catalog/models/{publisher}/{model}; see Catalog.

Create a static router on Bedrock geo in eu-central-1bash
curl -X POST https://api.multi-router.ai/v1/routers \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "static",
    "name": "haiku-eu",
    "routing": {
      "provider": "bedrock",
      "model": "anthropic/claude-haiku-4.5",
      "variant": "geo",
      "region": "eu-central-1",
      "provider_key_id": "llpk_4Vd8Qm2RtL"
    },
    "prompt": { "system_message_mode": "merge" }
  }'

Dashboard region picker

The dashboard's model picker lists every variant of a model with its display name and description, and the region picker lists every region of the selected variant with its model id and price. The region picker's options depend on the provider:

Routers›New router

New router

Cancel
Type
Details
Routing
Review & create

Routing

One model, served by one vendor, variant and region.
ModelsClaude Haiku 4.5Anthropic5 ways to call it
VendorVariantReleaseRegion5 shown
VendorVariantReleaseRegionsContextPrice · 1M
  • Anthropic1 Oct 2025 snapshotPinned to the 1 Oct 2025 model snapshot.snapshot1 region200K$1.00 in · $5.00 out$0.10 cache read · $1.25 5m cache write · $2.00 1h cache write
  • AnthropicStandardThe vendor's ordinary endpoint for this model.rolling1 region200K$1.00 in · $5.00 out$0.10 cache read · $1.25 5m cache write · $2.00 1h cache write
  • BedrockGeoInference stays inside one geography (US, EU, APAC, Japan or Australia).snapshot18 regions200K$1.10 in · $5.50 out$0.11 cache read · $1.38 5m cache write · $2.20 1h cache write
  • BedrockGlobalInference may run in any AWS region. Cheapest, with no residency guarantee.snapshot33 regions200K$1.00 in · $5.00 out$0.10 cache read · $1.25 5m cache write · $2.00 1h cache write
  • Vertex AIStandardThe vendor's ordinary endpoint for this model.snapshot4 regions200K$1.00–1.10 in · $5.00–5.50 out$0.10–0.11 cache read · $1.25–1.38 5m cache write · $2.00–2.20 1h cache writePrice varies by region
Region
us-east-1
  • Inherit from the key or org defaultResolved per request: client geo intent, then provider key region, then organization default
  • ap-northeast-1jp.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • ap-northeast-3jp.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • ap-southeast-2au.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • ap-southeast-4au.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • ap-southeast-6au.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • ca-central-1us.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • eu-central-1eu.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • eu-central-2eu.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • eu-north-1eu.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • eu-south-1eu.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • eu-south-2eu.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • eu-west-1eu.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • eu-west-2eu.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • eu-west-3eu.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • us-east-1us.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • us-east-2us.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • us-west-1us.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
  • us-west-2us.anthropic.claude-haiku-4-5-20251001-v1:0 · $1.10 / $5.50
Preselected from provider key Bedrock production · 1 of 18 served by Geo
us.anthropic.claude-haiku-4-5-20251001-v1:0
$1.10 in · $5.50 out · $0.11 cache read · $1.38 5m cache write · $2.20 1h cache write
Key
Bedrock production

Credential for this vendor. Defaults to the org key. The effective key carries us-east-1.

Resolves tobedrock · geo · us-east-1
Coordinate
bedrockClaude Haiku 4.5Geous-east-1
Outbound wire id
us.anthropic.claude-haiku-4-5-20251001-v1:0
Price · 1M
$1.10 in · $5.50 out · $0.11 cache read · $1.38 5m cache write · $2.20 1h cache write
Same price in every region this variant serves.
About this variant
Inference stays inside one geography (US, EU, APAC, Japan or Australia).
Billing account
AWS — charged to Bedrock production.
Context window
200K
Client call
POST https://api.multi-router.ai/v1/messages
X-MR-Router: rt_…

Rate limits, parameter defaults and request logging are configurable after create.

BackReview
A static router on Claude Haiku 4.5: the Bedrock Geo offering selected, and the region picker preselected from the provider key. Select to enlarge.
OptionShown forMeaning
Default (global)Providers with a default endpoint, such as AnthropicThe provider's ordinary endpoint. When the model is offered in one region only, the option shows that region instead of global.
Inherit from the key or org defaultAWS Bedrock, Google Vertex and xAILeaves the router region unset, so the region is selected for each request as described above.
A named regionAll regional providersSets the router region. For OpenAI, the picker lists only the region that matches the provider key.

For AWS Bedrock, Google Vertex and xAI, the picker preselects the provider key's region, then the organization default region, then the first region the variant offers, and a note under the picker states which source it used. When neither the router, the key nor the organization supplies a region, the picker warns that requests will fail.

Custom endpoints

Routers on a custom endpoint take no variant. They can set a region only when the model is a custom model registered with regions, and a custom model offered in several regions requires one; see Custom Models & Pricing.

Claude Proxy routes

A Claude Proxy route is the provider and variant a Claude Proxy sends Claude Code and Claude Desktop traffic to, and each route except Custom requires a region that the route serves:

RouteProvider and variantRegion
Anthropic APIAnthropic standardglobal or us
Bedrock (Global)AWS Bedrock globalAn AWS region the route serves
Bedrock (Geo)AWS Bedrock geoAn AWS region the route serves
Bedrock (In-region)AWS Bedrock in_regionAn AWS region the route serves
Vertex AIGoogle Vertex standardA Vertex location the route serves
CustomThe models in a model groupSet on each model in the group

A Bedrock API key used by a Claude Proxy must have been created for the route's region. See Agents & Editors.

Traffic

Traffic records the variant and the region of every request. The Variant and Region columns are hidden by default and are turned on in the Columns picker, and the request detail view shows both. The Region column shows the region the request was billed at, and for Anthropic this is the region Anthropic reports the request ran in. The x-mr-region response header reports the region the request was sent to.

Errors

Router changes and requests return the following errors for variants and regions:

ErrorCauseResolution
missing_variantA static router is created without routing.variant.Set a variant.
missing_fieldA static router is created or its routing changed without routing.region.Set a region.
invalid_variantThe router names a variant the provider does not offer for the model. The message lists the valid variants.Set one of the listed variants.
router_variant_missingA router created before variants were required has no variant.Set a variant on the router.
router_variant_not_foundThe catalog no longer offers the router's variant.Set a variant the catalog offers.
router_variant_retiredThe router's variant is retired. The message lists the variants still served.Set one of the listed variants.
region_not_servedThe model is not offered in the selected region, or no region is configured. The message lists the available regions.Set a router region from the list, or change the key or organization default region.
key_residency_mismatchAn OpenAI router's region differs from the region of its provider key.Set the router region to the key region, or use a key with the matching region.
region_not_applicableA router on a custom endpoint sets routing.region for a model that is not a custom model.Remove the region.
region_not_offeredA router on a custom endpoint sets a region its custom model does not offer.Set one of the custom model's regions.
router_region_requiredA custom model offered in several regions is used by a router with no region.Set a router region.
region_not_on_routeA Claude Proxy's region is missing or not served by its route.Set a region the route serves.
key_region_mismatchA Claude Proxy uses a Bedrock API key created for a different region.Use a key created for the route's region, or an access key (SigV4).

Providers & Models

AWS Bedrock

Connect an AWS account to Multi-Router and route requests to the models AWS Bedrock serves.

AWS Bedrock is Amazon's managed model platform, serving models from Anthropic, Amazon, Meta, Mistral, DeepSeek, Qwen, OpenAI, Moonshot, Nvidia and Z.ai in AWS regions under your AWS account. Multi-Router calls Bedrock with a provider key that holds AWS credentials and a default region. AWS bills usage to the account that owns the credentials, and Multi-Router records the cost of each request at the catalog price of the region and inference scope that served it.

Preparing your AWS account

The AWS account that serves requests needs model access and an IAM identity whose credentials Multi-Router uses:

  1. Bedrock enables serverless models automatically on first use. Before the first call to an Anthropic model, submit Anthropic's one-time use-case form in the Bedrock console. Models sold through AWS Marketplace subscribe on first use, so the identity that makes the first call needs aws-marketplace:Subscribe and aws-marketplace:ViewSubscriptions, or an administrator with those permissions calls each model once.
  2. Create an IAM user for Multi-Router and attach a policy that allows bedrock:InvokeModel and bedrock:InvokeModelWithResponseStream, such as the policy below. To use a Bedrock API key, also allow bedrock:CallWithBearerToken.
  3. Create the credential Multi-Router will use: an access key for the IAM user, or a long-term Bedrock API key.

These actions cover every request Multi-Router sends to Bedrock, including streaming. Requests through geo inference profiles are authorized against the profile and against the foundation model in every region of the geography, and requests through global profiles are also authorized against the region-less foundation model resource, which the policy lists. A service control policy that limits aws:RequestedRegion must allow every region of the geography for geo profiles, and the value unspecified for global profiles.

IAM policy for Multi-Routerjson
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "MultiRouterBedrockInvoke",
      "Effect": "Allow",
      "Action": [
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithResponseStream"
      ],
      "Resource": [
        "arn:aws:bedrock:*::foundation-model/*",
        "arn:aws:bedrock:::foundation-model/*",
        "arn:aws:bedrock:*:<ACCOUNT_ID>:inference-profile/*"
      ]
    },
    {
      "Sid": "MultiRouterBedrockApiKey",
      "Effect": "Allow",
      "Action": "bedrock:CallWithBearerToken",
      "Resource": "*"
    }
  ]
}

Adding a Bedrock provider key

A Bedrock provider key holds one set of AWS credentials and the AWS region requests go to by default. To add one, open LLM Provider Keys in the dashboard, add a key, select AWS Bedrock, and choose Access key (SigV4) or API key (bearer) at the top of the form:

LLM Provider Keys

Upstream credentials used to call providers. The default key for each provider is what gets used when a request reaches that provider without a router preset overriding it. Secrets are encrypted at rest; the value can’t be retrieved after creation.

B
AWS Bedrock
2 keys · default Bedrock production
Add AWS Bedrock key

New AWS Bedrock key

Access key (SigV4)API key (bearer)
CancelSave key
Name
bedrock-analytics

Short dashboard handle. Lowercase, hyphens.

AWS Region
us-east-1
US East (N. Virginia)

Bedrock runtime endpoint is region-scoped. Pick the region your model access is enabled in.

AWS Access Key ID
AKIAIOSFODNN7EXAMPLE

The Access Key ID half of your IAM credentials.

Secret access key
••••••••••••••••••••••••••••••••••••••••

Encrypted at rest. Can’t be retrieved later.

Description
Analytics team's Bedrock access in us-east-1. Owner: data platform. Rotate every 90 days.
Make this the default AWS Bedrock key(use “Make default” on the new key’s row after creation to promote it)
The New AWS Bedrock key form in Access key (SigV4) mode. Select to enlarge.
FieldDescription
NameRequired, up to 80 characters.
AWS RegionThe region requests go to when the router names none.
AWS Access Key IDFor Access key (SigV4): the access key id of the IAM user, 16 to 128 letters and digits.
Secret access keyFor Access key (SigV4): the secret access key of the IAM user. For API key (bearer), the field reads Bedrock API key and holds the API key.
Session tokenOptional, for Access key (SigV4) with temporary AWS credentials, up to 4,096 bytes.
DescriptionOptional.

The key's region is one of us-east-1, us-east-2, us-west-2, ca-central-1, sa-east-1, eu-central-1, eu-west-1, eu-west-2, eu-west-3, eu-north-1, eu-south-1, ap-south-1, ap-southeast-1, ap-southeast-2, ap-northeast-1, ap-northeast-2 and ap-northeast-3, and a router can name any region the catalog lists for its variant. When the key is saved, Multi-Router checks the region, the access key id format and the session token length, and AWS checks the credentials on the first request. Multi-Router uses a Bedrock API key only in the key's region.

Temporary credentials with a session token expire, and AWS short-term Bedrock API keys last at most 12 hours, so long-running routers use an IAM user access key or a long-term Bedrock API key. The region and credentials of a saved key are fixed, and changing them means adding a new key; see Provider Key.

Regions and inference scopes

Each Bedrock request goes to one AWS region, which is the router's region or, when the router names none, the provider key's region. The router's variant then sets where inference runs: in_region keeps it in that region, geo keeps it in that region's geography, and global lets AWS run it in any region. A model not offered in the selected region and variant returns region_not_served with the regions where it is available. See Regions & Variants.

Models and features

Bedrock features depend on the model's publisher:

FeatureClaude modelsOther publishers
StreamingYesYes, for models whose catalog entry lists streaming
System promptsYesYes
ToolsYesYes, for models whose catalog entry lists tools
Tool choiceYesYes, for models whose catalog entry lists tools
ImagesYesModels whose catalog entry lists image input, such as Amazon Nova, Llama 4 and Qwen3 VL
ReasoningYes, with thinking and budget_tokens on the Messages APINo
Prompt cachingYes, with cache_control on the Messages APINo
Structured outputNoNo
Samplingtemperature, top_p, top_k, stop sequencestemperature, top_p, stop sequences

A request with a json_object or json_schema response format returns an error for every Bedrock model. Multi-Router downloads images sent as URLs and passes them to Bedrock as image data: an image URL must use HTTPS and return HTTP 200, with a PNG, JPEG, GIF or WebP content type, and an image of at most 5 MiB, within 10 seconds.

Errors and retries

Bedrock errors reach the caller as Multi-Router errors, with the AWS error in the response's upstream object. Multi-Router retries HTTP 429, 500, 502, 503, 504 and 529 responses, timeouts and connection failures, with exponential backoff and any whole-second retry delay AWS sends, within five minutes of the first attempt. Each attempt must receive a response within 120 seconds, so long outputs use streaming. When retries are exhausted, the request returns 429 upstream_rate_limit with the upstream code max_attempts_exceeded.

AWS errorMulti-Router errorCommon cause
ThrottlingException429 upstream_rate_limit after retriesRequests exceed the account quota for the model and region.
ModelNotReadyException429 upstream_rate_limit after retriesThe model is not yet ready to serve requests.
InternalServerException429 upstream_rate_limit after retriesBedrock had an internal error.
ServiceUnavailableException429 upstream_rate_limit after retriesBedrock is temporarily unavailable.
ServiceQuotaExceededException402 upstream_insufficient_quotaThe account has reached a Bedrock service quota.
AccessDeniedException502 upstream_invalid_credentialsThe IAM policy does not allow the action, or the model has not been enabled for the account.
UnrecognizedClientException502 upstream_invalid_credentialsThe access key or Bedrock API key is invalid.
ExpiredTokenException502 upstream_invalid_credentialsThe temporary credentials or session token have expired.
ValidationException400 upstream_invalid_requestBedrock rejected the request, such as a parameter the model does not accept or a prompt longer than its context window.
ResourceNotFoundException400 upstream_model_not_foundThe model or inference profile is not available in the region.
ModelTimeoutException408 upstream_otherThe model timed out while processing the request.
ModelErrorException424 upstream_otherThe model failed while processing the request.

Errors that occur after a streaming response has started are returned as an error event in the stream.

Pricing

Bedrock prices differ by region and inference scope. Multi-Router records the cost of each request at the price of the region and variant that served it, including separate rates for prompt cache reads and cache writes on Claude models, and sends requests on Bedrock's standard on-demand tier. See Model Catalog.

Claude Code on Bedrock

A Claude Proxy on a Bedrock route serves Claude Code in its Bedrock mode. The Claude Proxy routes are Bedrock (Global), Bedrock (Geo) and Bedrock (In-region), each with a region set on the Claude Proxy, and the route and region decide where inference runs, whatever inference profile prefix the client sends. Multi-Router forwards the request body unchanged, replaces the model id with the route's id for the proxy's region, and signs the request with the organization's Bedrock key. Claude Proxy requests are sent to Bedrock once, without retries, and Bedrock's status and error body are returned to the client unchanged. The dashboard generates the environment for each developer:

Claude Code environmentbash
export CLAUDE_CODE_USE_BEDROCK=1
export CLAUDE_CODE_SKIP_BEDROCK_AUTH=1
export ANTHROPIC_BEDROCK_BASE_URL='https://api.multi-router.ai/bedrock'
export ANTHROPIC_AUTH_TOKEN='<Multi-Router API key>'
export ANTHROPIC_CUSTOM_HEADERS='X-MR-Router: <router id>'

See Agents & Editors.

Providers & Models

Google Vertex

Connect a Google Cloud project to Multi-Router and route requests to Anthropic's Claude models on Vertex AI.

Google Vertex AI is Google Cloud's managed model platform, and Multi-Router uses it to serve Anthropic's Claude models from your Google Cloud project. A Vertex provider key holds a service account, its project and a location, and Multi-Router calls Vertex as that service account. Usage is billed by Google to the project, and Vertex quotas apply per project, model and location. Google's Gemini models are served through the Gemini provider; see Supported Providers.

Preparing your Google Cloud project

The Google Cloud project that serves requests needs the Vertex AI API, access to the Claude models, and a service account Multi-Router can use:

  1. Enable the Vertex AI API in the project.
  2. In Vertex AI Model Garden, enable each Claude model you will use.
  3. Create a service account in the project. Its email ends in @<project-id>.iam.gserviceaccount.com; Google-managed accounts, such as the Compute Engine default service account, are not accepted.
  4. Grant the service account the Vertex AI User role (roles/aiplatform.user), which allows Vertex AI predictions.
  5. Create a JSON key for the service account and download it. If your organization enforces the iam.disableServiceAccountKeyCreation policy, an organization policy administrator must allow key creation for the project first.

Adding a Vertex provider key

A Vertex provider key holds one service account and the location requests run in. To add one, open LLM Provider Keys in the dashboard, add a key, and select Google Vertex:

FieldDescription
Google Cloud project IDThe project id, such as my-production-project: 6 to 30 lowercase letters, digits and hyphens, starting with a letter and ending with a letter or digit. It must match the project_id in the service account JSON.
Google Cloud locationThe location requests run in: global, us, eu or a Google Cloud region such as us-east5. The models you route must be offered in this location.
Service-account JSONThe complete JSON key file of the service account, up to 64 KB.

LLM Provider Keys

Upstream credentials used to call providers. The default key for each provider is what gets used when a request reaches that provider without a router preset overriding it. Secrets are encrypted at rest; the value can’t be retrieved after creation.

V
Google Vertex
0 keys · no default
Add Google Vertex key

New Google Vertex key

CancelSave key
Name
vertex-us

Short dashboard handle. Lowercase, hyphens.

Google Cloud project ID
acme-ml-prod

Must match the service account JSON. Multi Router overrides client project paths with this value.

Google Cloud location
us

Model availability and pricing must have an exact catalog row for this location.

Service-account JSON
{ "type": "service_account", "project_id": "acme-ml-prod", "private_key_id": "8c1f2e9a7b…", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASC…", "client_email": "multi-router@acme-ml-prod.iam.gserviceaccount.com", … }

Encrypted at rest. Can’t be retrieved later.

Description
Why this key exists. Owner. Rotation schedule. Cost center. Anything an operator should know in 6 months.
Make this the default Google Vertex key(forced — first key for this provider)
The New Google Vertex key form with the project, location and service account JSON. Select to enlarge.

The service account JSON must be the unmodified key file Google issues: its type is service_account, its token_uri is https://oauth2.googleapis.com/token, and it contains the project_id, the client_email of a service account in that project, a private_key_id and an RSA private_key. A file that breaks these rules is rejected with a message beginning Vertex service-account or metadata validation failed.

Multi-Router verifies the service account with Google when the key is saved and when its location is edited, and when it is enabled or made the default unless Multi-Router already holds a valid access token for it. A failed verification shows Google's response, such as Google OAuth token endpoint returned HTTP 400 (invalid_grant). Verification confirms that Google accepts the key file; the service account's role and model access are checked by the first request. A saved key's name, description and location can be edited, and later requests use the new location. The service account JSON is fixed, so rotating it means adding a new key and moving routers to it; see Provider Key.

Locations

The location is the Google Cloud location that runs each request, and for static routers it is always the location set on the provider key. Current Claude models are offered in the global, us and eu locations, and Claude Sonnet 4.6, Claude Opus 4.6 and earlier models are also offered in single regions such as us-east5, europe-west1 and asia-southeast1. A request for a model not offered in the key's location returns region_not_served with the locations where it is available.

To serve requests from several locations, add one provider key per location, using the same service account JSON in each, and set each router to the key for its location. Claude Proxies set the location on the Claude Proxy instead, so one key serves every location. See Regions & Variants for what each location type means.

Models and features

Multi-Router serves Anthropic's Claude models on Vertex, and a request for another publisher's model on Vertex returns publisher_not_supported. Claude models on Vertex support the following features:

FeatureSupport
StreamingYes.
ToolsYes.
ImagesYes, sent as base64 data. Vertex does not accept images by URL.
ReasoningYes, with thinking and budget_tokens on the Messages API.
Prompt cachingYes, with cache_control on the Messages API.
Structured outputClaude 4.5 and later models, with a json_schema response format on the Chat Completions API. A json_object response format is rejected.
PDF inputThrough a Claude Proxy.
Token countingThrough a Claude Proxy, for Claude Code.

Access tokens

An access token is the short-lived credential Multi-Router obtains from the service account to call Vertex, and Multi-Router renews it automatically before it expires. When the service account key is deleted in Google Cloud, requests continue until the current access token expires, within an hour, and then fail with a 503. To stop access immediately, disable the service account in Google Cloud or disable the provider key in Multi-Router.

Errors and retries

Vertex errors reach the caller as Multi-Router errors with Google's status in the response's upstream object. Multi-Router retries rate limits, server errors, timeouts and connection failures with exponential backoff, honoring any retry delay Google sends, for up to five attempts within five minutes.

ErrorCause
503, Google Vertex OAuth token is not ready; retry shortlyMulti-Router has no valid access token for the key at that moment, or the service account key no longer works. Retry the request, and check the service account if the error persists.
502 upstream_invalid_credentialsGoogle rejected the service account credentials.
403 upstream_other, upstream status PERMISSION_DENIEDThe service account lacks the Vertex AI User role, or the Vertex AI API is not enabled.
400 upstream_model_not_foundThe model is not enabled in Model Garden, or is not available in the project and location.
429 upstream_rate_limitRate limits, server errors or timeouts continued after retries. Request quota increases in the Google Cloud console.
400 publisher_not_supportedThe model is not an Anthropic model.
400 region_not_servedThe model is not offered in the key's location.

Pricing

Vertex prices differ by location: for Claude Sonnet 4.5 and later models, the us, eu and single-region locations cost more than global, and earlier models cost the same in every location. Multi-Router bills each request at the price of the location that served it, including prompt cache reads, 5-minute and 1-hour cache writes, and long-context rates for prompts above the model's long-context threshold. See Model Catalog.

Claude Code on Vertex

A Claude Proxy on the Vertex AI route serves Claude Code in its Vertex mode. Multi-Router forwards the request body unchanged and sends it with the organization's Vertex key, the key's project, the location set on the Claude Proxy and the model id of the routed model; Claude Code's project and region settings are required by Claude Code and do not select the project or location. Claude Proxy requests are sent to Vertex once, without retries, and errors use Google's error format. The dashboard generates the environment for each developer:

Claude Code environmentbash
export CLAUDE_CODE_USE_VERTEX=1
export CLAUDE_CODE_SKIP_VERTEX_AUTH=1
export ANTHROPIC_VERTEX_BASE_URL='https://api.multi-router.ai/vertex'
export ANTHROPIC_VERTEX_PROJECT_ID='<project id>'
export CLOUD_ML_REGION='<location>'
export ANTHROPIC_AUTH_TOKEN='<Multi-Router API key>'
export ANTHROPIC_CUSTOM_HEADERS='X-MR-Router: <router id>'

See Agents & Editors.

Providers & Models

Model Catalog

The catalog of every model Multi-Router serves: identities, capabilities, limits, prices, lifecycle and updates.

For each model, the catalog records the identity, mode, capabilities and limits, and for each offering of the model it records the provider, variant, regions, model ids and prices. Multi-Router uses the catalog to check every request before calling the provider, to select the model id sent upstream, and to compute the cost of each request. The catalog is versioned and published by Multi-Router, and every installation serves one catalog version at a time.

Model entries

A model entry describes one model independently of where it is served. The Models page shows these fields in the Baseline panel of each model:

FieldDescription
PublisherThe company that built the model, such as Anthropic.
SlugThe model's unique name, such as anthropic/claude-sonnet-4.6. See Overview.
ProductThe product line the model belongs to, such as Claude Sonnet or Qwen.
VersionThe model version and its format, such as 4.6 · semver, or a date or snapshot id.
ReleasedThe release date, when the publisher states one.
ModeThe type of model, such as Chat or Embedding.
ContextThe context window and the maximum output tokens, such as 1,000,000 · 128,000 out.
StatusThe lifecycle status: active, deprecated or retired.
Replaced byThe slug of the successor model, when the publisher names one.

The Baseline panel also lists the model's capabilities, input modalities and aliases, with a link to the publisher's documentation.

Modes

The mode is the type of work a model performs. The Chat Completions and Messages endpoints serve chat models, and a request for a model of another mode returns wrong_endpoint_for_mode.

ModeDashboard labelModels
chatChatConversational and instruction models.
responsesResponsesModels offered through the OpenAI Responses API.
embeddingEmbeddingText embedding models.
image_generationImage genImage generation models.
image_editImage editImage editing models.
audio_transcriptionAudio transcribeSpeech-to-text models.
audio_speechAudio speechText-to-speech models.
realtimeRealtimeLow-latency audio and text conversation models.
rerankRerankSearch reranking models.
moderationModerationContent moderation models.
ocrOCRDocument text extraction models.
video_generationVideo genVideo generation models.
completionCompletionLegacy text completion models.

Capabilities and modalities

Capabilities are the request features a model supports, and modalities are the content types it accepts and produces, such as text, image, audio, video and PDF. Multi-Router checks every chat request against the model's capabilities and its image input modality, and rejects a request that uses a feature the model lacks before calling the provider:

CheckRequest featureError
Streamingstream: truestreaming_not_supported
ToolsA non-empty tools listtools_not_supported
Parallel tool callsparallel_tool_calls: trueparallel_tool_calls_not_supported
Structured outputA json_object or json_schema response formatstructured_output_not_supported
Reasoningreasoning_effort or thinkingreasoning_not_supported
Prompt cachingprompt_cache_key or any cache_controlprompt_caching_not_supported
Image input modalityImage content in a messagemodality_not_supported

Offerings

An offering is one way to call a model: a provider and variant, with the regions where it is available. Each region carries the model id Multi-Router sends to the provider and the price. An offering can set limits or capabilities that differ from the model's baseline, such as a smaller context window on one provider, and an expanded offering on the Models page shows them as Capability overrides and as Context or Max output with the baseline value. The Offerings panel lists each offering's Vendor, Variant, Regions and Pricing, marks whether the variant is a fixed snapshot or a rolling version the provider updates, and marks custom offerings. Expanding an offering shows every region with its model id and prices. See Regions & Variants.

Prices

Prices are set for each region of each offering, in US dollars per million tokens unless noted, and are the providers' published list prices.

PriceDescriptionUsed in cost
InputPrompt tokens that are not read from or written to the cache.Yes
OutputCompletion tokens, including reasoning tokens.Yes
Cache readPrompt tokens read from the prompt cache.Yes
Cache write (5 minutes)Prompt tokens written to the cache with a 5-minute lifetime.Yes
Cache write (1 hour)Prompt tokens written to the cache with a 1-hour lifetime.Yes
Long contextRates that replace all of the above when the prompt, including cached tokens, reaches a threshold such as 200,000 tokens.Yes
Service tiersRates for the Batch, Priority, Flex and Fast tiers some providers offer, each with its own long context rates. The Fast tier applies when the provider reports that it served the request in fast mode.Fast tier
Tool feesFees for provider-run tools, such as web search per 1,000 calls.Web search
Cache storageThe cost of keeping one million cached tokens for one hour.No
Modality ratesInput or output rates for audio, image or video content that differ from the token rates.No
Unit pricesPrices per image, audio minute, page, video second, character, search or request, for models priced by unit.No

Multi-Router computes the cost of each request from the token counts, the number of web searches and the service tier the provider reports, and the price of the region the request was served in, and records it in Traffic, where it also counts toward budgets. A price part the catalog omits is charged at zero, and an offering whose provider publishes no price shows No pricing published on the Models page. Negotiated rates are applied with Custom Models & Pricing.

Lifecycle

The lifecycle status records whether a model is still offered. Each model has its own status, and each offering of a model can also have a status of its own:

StatusModelOffering
ActiveServed normally.Served normally.
DeprecatedServed, with the response headers x-mr-model-deprecated: true and, when the catalog names a successor, x-mr-model-replaced-by.Served normally.
RetiredRefused with model_retired, whose message names the successor when there is one.Refused with router_variant_retired for routers set to the retired variant.

A model or offering with a scheduled retirement time becomes retired at that time, without a new catalog version. Claude Proxies serve active models only, and refuse a deprecated or retired model with HTTP 404 and a message that names its status.

Browsing the catalog

The Models page in the dashboard lists every model in the catalog, including models from providers the organization holds no key for, together with the organization's model groups and the installation's custom models.

ControlUse
Pinned viewsAll models, Active, Reasoning, Vision and Tools.
My viewsSaved views. Save view stores the current filters, search, status and Show choice.
ShowAll, Models or Groups.
StatusAll, Active or Deprecated. All includes retired models.
FiltersPublishers, Vendors, Capabilities (Vision, Audio, Video, Tools, Reasoning, Caching, Structured, Parallel tools, Streaming) and Mode (Chat only, All modes or a single mode).
SearchMatches the slug, name, publisher, aliases and provider model ids.
ColumnsKind, Publisher, Vendors, Capabilities, Context and Price, kept for each browser.
Engineering · org_7Tq2Wd9KxP

All models· Models

New groupSave view
Show:
AllModelsGroups
Status:
AllActiveDeprecated
Filters1
5.
Models16 of 281 · 0 groups · 16 models
1–16 of 1625 / page
Columns
NameKindPublisherVendorsCapabilitiesContextPrice
Claude Opus 5.5AnthropicAnthropicBedrock×2Vertex AI1M$4.00–4.40 / $20.00–22.00
Claude Fable 5.1AnthropicAnthropicBedrock×2Vertex AI1M$10.00–11.00 / $50.00–55.00
GPT-5.1OpenAIOpenAI400K$1.25 / $10.00
GPT-5.2OpenAIOpenAI400K$1.75 / $14.00
GPT-5.2-proOpenAIOpenAI400K$21.00 / $168.00
GPT-5.3-codexOpenAIOpenAI400K$1.75 / $14.00
GPT-5.4OpenAIOpenAI1.1M$2.50–2.75 / $15.00–16.50
GPT-5.4-miniOpenAIOpenAI400K$0.75–0.82 / $4.50–4.95
GPT-5.4-nanoOpenAIOpenAI400K$0.20–0.22 / $1.25–1.38
GPT-5.4-proOpenAIOpenAI1.1M$30.00–33.00 / $180.00–198.00
GPT-5.5OpenAIOpenAI1.1M$5.00–5.50 / $30.00–33.00
GPT-5.5-proOpenAIOpenAI1.1M$30.00–33.00 / $180.00–198.00
GPT-5.6-cyberOpenAIOpenAI400K$12.50 / $75.00
GPT-5.6-lunaOpenAIBedrock×2OpenAI1.1M$0.20–0.22 / $1.20–1.32
GPT-5.6-solOpenAIBedrock×2OpenAI1.1M$4.00–4.40 / $20.00–22.00
GPT-5.6-terraOpenAIBedrock×2OpenAI1.1M$2.00–2.20 / $12.00–13.20
Capabilities:streamingtoolsparallel tool callsstructured outputreasoningprompt cachingGroup guarantees use capability intersection and minimum context. Input and output price envelopes stay separate.
The Models table filtered to active Anthropic and OpenAI models, searching for the latest Claude and GPT-5 releases. Select to enlarge.

The Price column shows the lowest-to-highest input and output prices across every region, a model priced by unit shows its unit price, and a model with no published price shows a dash. Selecting a model opens its Baseline and Offerings panels. In an expanded offering that publishes more than one rate, the Rate switch selects among the rates it publishes, such as Standard, Long Context, Batch, Priority, Flex, Fast and Tools.

Engineering · org_7Tq2Wd9KxP

All models· Models

New groupSave view
Show:
AllModelsGroups
Status:
AllActiveDeprecated
Filters
opus
Models9 of 281 · 0 groups · 9 models
1–9 of 925 / page
Columns
NameKindPublisherVendorsCapabilitiesContextPrice
Claude Opus 5.5AnthropicAnthropicBedrock×2Vertex AI1M$4.00–4.40 / $20.00–22.00
Baseline
Publisher
Anthropic
Slug
anthropic/claude-opus-5.5
Product
Claude Opus
Version
5.5 · semver
Released
2026-09-22
Mode
Chat
Context
1,000,000 · 128,000 out
Status
active
Capabilities
Streaming
Tools
Parallel tool calls
Structured output
Reasoning
Prompt caching
Modalities
textimagepdf
platform.claude.com/docs/en/about-cl… ↗
Offerings — 4 routable coordinates
VendorVariantRegionsPricing
AnthropicStandardsnapshot2 regionsPrice varies by region
BedrockGeosnapshot18 regionsSame price in every region

Inference stays inside one geography (US, EU, APAC, Japan or Australia).

ap-northeast-1jp.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
ap-northeast-3jp.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
ap-southeast-2au.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
ap-southeast-4au.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
ca-central-1us.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
ca-west-1us.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
eu-central-1eu.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
eu-central-2eu.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
eu-north-1eu.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
eu-south-1eu.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
eu-south-2eu.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
eu-west-1eu.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
eu-west-2eu.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
eu-west-3eu.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
us-east-1us.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
us-east-2us.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
us-west-1us.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
us-west-2us.anthropic.claude-opus-5-5$4.40 in · $22.00 out · $0.22 cache read · $5.50 5m cache write · $8.80 1h cache write
BedrockGlobalsnapshot33 regionsSame price in every region
Vertex AIStandardsnapshot3 regionsPrice varies by region

Each row is a curated, working way to call this model. Picking one (with a region) happens when you wire a router; cross-vendor fallback composes routers.

Claude Opus 5AnthropicAnthropicBedrock×2Vertex AI1M$5.00–5.50 / $25.00–27.50
Claude Opus 4.8AnthropicAnthropicBedrock×3Vertex AI1M$5.00–5.50 / $25.00–27.50
Claude Opus 4.7AnthropicAnthropicBedrock×3Vertex AI1M$5.00–5.50 / $25.00–27.50
Claude Opus 4.6AnthropicAnthropicBedrock×3Vertex AI1M$5.00–5.50 / $25.00–27.50
Claude Opus 4.5AnthropicAnthropic×2Bedrock×2Vertex AI200K$5.00–5.50 / $25.00–27.50
Claude Opus 4.1deprecatedAnthropicAnthropic×2BedrockVertex AI200K$15.00 / $75.00
Claude Opus 4deprecatedAnthropicAnthropicVertex AI200K$15.00 / $75.00
Claude Opus 3retiredAnthropicAnthropic200K—
Capabilities:streamingtoolsparallel tool callsstructured outputreasoningprompt cachingGroup guarantees use capability intersection and minimum context. Input and output price envelopes stay separate.
Claude Opus 5.5 expanded: the Baseline panel, its four offerings, and Bedrock Geo opened to its model id and price in each region. Select to enlarge.

Listing models with the API

GET /v1/models lists the chat models the API token's organization can call: models that are active or deprecated and have an offering that is not retired from a provider for which the organization holds an enabled provider key, together with custom endpoint models when the organization holds a Custom endpoint key. The endpoint requires the models:read scope and returns the OpenAI list format, sorted by publisher and slug. In each entry, owned_by is the publisher and created is the publication time of the catalog version in effect, which is the same for every model. The full catalog, with every model's offerings, regions and prices, is read with GET /v1/catalog/models and GET /v1/catalog/models/{publisher}/{model}, with the same scope; see Catalog.

Requestbash
curl https://api.multi-router.ai/v1/models \
  -H "Authorization: Bearer $MR_API_KEY"
Response 200json
{
  "object": "list",
  "data": [
    { "id": "anthropic/claude-haiku-4.5", "object": "model", "created": 1790812800, "owned_by": "anthropic" },
    { "id": "anthropic/claude-sonnet-4.6", "object": "model", "created": 1790812800, "owned_by": "anthropic" }
  ]
}

With an x-mr-router header that names a Claude Proxy, the endpoint returns the models that Claude Proxy serves in the Anthropic format, which Claude Code uses to list its models, sorted by id. Each entry carries the model id the proxy's route uses, the display name, the release date at midnight UTC, and the token limits. A header that names any other kind of router returns 404.

Response with x-mr-router 200json
{
  "data": [
    {
      "type": "model",
      "id": "claude-sonnet-4-6",
      "display_name": "Claude Sonnet 4.6",
      "created_at": "2026-02-17T00:00:00Z",
      "max_input_tokens": 1000000,
      "max_tokens": 128000
    }
  ],
  "has_more": false,
  "first_id": "claude-sonnet-4-6",
  "last_id": "claude-sonnet-4-6"
}

Catalog updates

Multi-Router publishes the catalog as numbered versions at models.multi-router.ai, and a published version never changes. A new version adds models and offerings, updates prices, and records deprecations and retirements. Multi-Router manages catalog updates on the hosted service. On a self-hosted installation, the server administrator controls them on the Catalog tab of the Admin page:

ControlUse
Catalog UpdatesTurns checking the publisher on or off. While it is on, the installation checks every 30 seconds; turning it off keeps the version in effect.
Publisher URLThe address the catalog is fetched from, which can point to a private mirror.
Rollout modeAuto applies each new version as it is fetched. Manual holds it for review, with Review diff, Approve go-live and Discard.
RefreshFetches the latest version immediately.
Recent versionsLists earlier versions, with Roll back. Rolling back sets Rollout mode to Manual.

Every fetched version is validated before use, a version that fails validation is skipped, and automatic updates only move forward. Upgrading the server installs the catalog bundled with the new release when it is newer than the version in effect. While an installation serves a catalog bundled with its release, the Models page shows a banner stating the version and that model data may be out of date. See Self-Hosting.

Errors

Catalog checks on chat requests return HTTP 400 with the following errors, in addition to the capability errors above. In a fallback router, a member that fails a catalog check is skipped and the next member is tried.

ErrorCause
model_not_foundThe model is not in the catalog.
wrong_endpoint_for_modeThe model is not a chat model.
provider_not_offeredThe router's provider does not offer the model.
model_retiredThe model is retired. The message names the successor when there is one.
router_variant_missingThe router names no variant for the model. The message lists the valid variants.
router_variant_not_foundThe router's variant is not offered for the model. The message lists the valid variants.
router_variant_retiredThe router's variant is retired. The message lists the variants still served.
publisher_not_supportedThe router's provider is Google Vertex and the model is not published by Anthropic.
region_not_servedThe model is not offered in the selected region.
catalog_unavailableThe catalog could not be read. Retry the request.

Providers & Models

Model Group

Define named sets of approved models, and use them to control which models people and Claude Code can call.

A model group is a named list of models that an organization approves for a purpose, such as the models engineers can use from Claude Code. Each member of a group is a model on a specific provider, optionally pinned to a variant and region, so a group describes exactly where each approved model runs. A model group belongs to one organization and has an id prefixed with mg_.

Members

A member is one approved model in a group, defined by the following fields:

FieldRequiredDescription
providerYesThe provider id, such as anthropic or bedrock, or custom for a custom endpoint. See Supported Providers.
modelYesThe model slug, such as anthropic/claude-sonnet-4.5. Multi-Router accepts any capitalization of the slug and returns the catalog's form.
variantNoThe variant of the model on the provider, such as standard or geo. See Regions & Variants.
regionNoThe region of the variant. A member with a region must also name a variant.
provider_key_idNoA provider key to pin to the member: an enabled key of the member's provider in the same organization. An OpenAI key must be set to the member's region.

Multi-Router checks every member against the catalog when the group is saved: the model must exist, the provider must offer it, and the variant and region, when given, must be an active offering. Members on a custom endpoint skip the catalog checks. A group holds at most 100 members and contains each model at most once, and an empty group is valid but serves no requests. Group names are up to 80 characters and descriptions up to 2,000 characters, names can repeat, and a group carries up to 20 tags of up to 80 characters each. Multi-Router trims every field, lowercases provider ids and tags, and removes duplicate tags.

On the Models page, a member whose model, offering or region is no longer in the catalog is marked Stale, and a custom endpoint member is marked Custom. A provider key pinned to a member cannot be deleted or moved to another organization until the member no longer names it.

Where model groups are used

A model group takes effect through the Claude Proxies and tools policies that name it:

Used byMeaning of membership
Claude ProxiesOn the Custom route, the models the Claude Proxy serves. A request for a model outside the group is refused.
Tools policiesOn the Custom route, the models the people assigned to the policy can call from Claude Code or Claude Desktop. The Portal lists them under Allowed models.

Both uses run on the Custom route with organization credentials, so a group used by either must contain only Claude models that Claude Proxies serve, and every member must name a variant and a region. The Claude Proxy or policy assigns a provider key to each model in the group, so each member can run on a different provider, variant and region, such as one model on Anthropic and another on AWS Bedrock geo in eu-central-1. A model added to a group that a Claude Proxy uses is served once the Claude Proxy assigns it a provider key.

Managing groups in the dashboard

Model groups are managed on the Models page. The Show filter switches between All, Models and Groups, and each group appears as a row marked Group with its name and id. Organization admins create, edit and delete groups; organization members can view them.

Engineering · org_7Tq2Wd9KxP

All models· Models• modified

New groupSave view
Show:
AllModelsGroups
Status:
AllActiveDeprecated
Filters
Search id / name / alias / vendor id…
Models2 of 2 · 2 groups · 0 models
1–2 of 225 / page
Columns
NameKindPublisherVendorsCapabilitiesContextPrice
Engineering modelsgroupmg_4kT9wQx2Lp1 publisherAnthropicBedrock2 models, 2 variants across 2 vendors200K$1.10–$3.00 in $5.50–$15.00 out
Group details
ID
mg_4kT9wQx2Lp
Members
2
Publishers
anthropic
Created
9/30/2026
Last changed
9/30/2026
Description
Approved for Claude Code
Used by
Claude proxies1
Auto routers0
Policies1

Changes here apply to all 2 consumers.

claude-code-engrt_Qm7c2HsP0a
Tags
productionAdd tag
Delete blocked
Remove this group from the consumers above before deleting it.
Members — what this group allows
Edit group
ModelPublisherVendorVariantRegionContextPrice
Claude Sonnet 4.5AnthropicAnthropicStandardglobal200K$3.00 · $15.00
Claude Haiku 4.5AnthropicBedrockGeoeu-central-1200K$1.10 · $5.50
Bedrock EUgroupmg_7HcXr2Nq0b1 publisherBedrock2 models, 1 variant across 1 vendor200K$1.10–$3.30 in $5.50–$16.50 out
The Models page filtered to Groups, with the Engineering models group expanded. Select to enlarge.
  1. Select New group, and enter the Group name and an optional Description.
  2. Select Add models. The picker lists models with their publisher, vendors, offerings, context window and price; open a model to choose an offering, which is a vendor and variant, and its region.
  3. Select Create group, which reads Add & create while picked models are still to be added.
Engineering · org_7Tq2Wd9KxP

All models· Models

New groupSave view
Show:
AllModelsGroups
Status:
AllActiveDeprecated
Filters
Search id / name / alias / vendor id…
Models281 of 281 · 0 groups · 281 models
1–25 of 28125 / page
Columns
New model group
Engineering models
Approved for Claude Code
ModelPublisherVendorVariantRegionContextPrice
Claude Sonnet 4.5AnthropicAnthropicStandardglobal200K$3.00 · $15.00
ModelsClaude Haiku 4.5Anthropic5 ways to call it
VendorVariantReleaseRegion5 shown
VendorVariantRegionsContextPrice
  • Anthropic1 Oct 2025 snapshotsnapshotPinned to the 1 Oct 2025 model snapshot.1 region200K$1.00 · $5.00
  • AnthropicStandardrollingThe vendor's ordinary endpoint for this model.1 region200K$1.00 · $5.00
  • BedrockGeosnapshotInference stays inside one geography (US, EU, APAC, Japan or Australia).18 regions200K$1.10 · $5.50
  • BedrockGlobalsnapshotInference may run in any AWS region. Cheapest, with no residency guarantee.33 regions200K$1.00 · $5.00
  • Vertex AIStandardsnapshotThe vendor's ordinary endpoint for this model.4 regions200K$1.00–1.10 · $5.00–5.50
Region is not picked here. It is resolved per call from whatever the chosen variant serves — client geo intent first, then the router region, then the provider key region, then the organization default.
1 variant ticked
CancelAdd to group
CancelAdd & create
NameKindPublisherVendorsCapabilitiesContextPrice
Claude Haiku 4.5AnthropicAnthropic×2Bedrock×2Vertex AI200K$1.00–1.10 / $5.00–5.50
Claude Opus 4.5AnthropicAnthropic×2Bedrock×2Vertex AI200K$5.00–5.50 / $25.00–27.50
Claude Sonnet 4.5AnthropicAnthropic×2Bedrock×2Vertex AI200K$3.00–3.30 / $15.00–16.50
The New model group panel with one member added and a Bedrock Geo offering staged from the picker. Select to enlarge.

Expanding a group's row shows Group details (id, members, publishers, creation and change dates, and description), Used by, with the number of Claude Proxies and policies that use the group and links to the routers, and Tags, where the group's tags are edited. Edit group changes the name, description and members, and shows Reach, the number of routers and policies a change affects, before it is saved.

Editing and deleting groups

An edit to a group applies to every Claude Proxy that uses it without editing the Claude Proxy, and must keep every member a model the Claude Proxy serves. A group named by a policy cannot be edited or deleted; to change the models a policy allows, select a different group in the policy. A group used by a Claude Proxy cannot be deleted until the Claude Proxy uses a different group.

API

The /v1/model-groups endpoints manage the model groups of the API token's organization. Scopes are cumulative: model_groups:delete includes write, and write includes read.

MethodPathDescriptionScope
POST/v1/model-groupsCreate a group. Returns 201 with the group. Accepts an Idempotency-Key.model_groups:write
GET/v1/model-groupsList groups, paged with limit (1 to 200, default 50) and cursor.model_groups:read
GET/v1/model-groups/{id}Get one group.model_groups:read
PATCH/v1/model-groups/{id}Change name or description; description: null clears it.model_groups:write
DELETE/v1/model-groups/{id}Delete a group. Accepts dry_run.model_groups:delete
GET/v1/model-groups/{id}/routersList the routers that use the group.model_groups:read
GET/v1/model-groups/{id}/membersList the members.model_groups:read
POST/v1/model-groups/{id}/membersAdd a member.model_groups:write
PATCH/v1/model-groups/{id}/membersChange the member named by ?model=: its provider, variant, region or provider_key_id.model_groups:write
DELETE/v1/model-groups/{id}/membersRemove the member named by ?model=.model_groups:delete
GET/v1/model-groups/{id}/tagsList the group's tags.model_groups:read
POST/v1/model-groups/{id}/tagsAdd a tag, sent as tag.model_groups:write
DELETE/v1/model-groups/{id}/tags/{key}Remove a tag.model_groups:delete
Create a model groupbash
curl -X POST https://api.multi-router.ai/v1/model-groups \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Engineering models",
    "description": "Approved for Claude Code",
    "members": [
      { "provider": "anthropic", "model": "anthropic/claude-sonnet-4.5", "variant": "standard", "region": "global" },
      { "provider": "bedrock", "model": "anthropic/claude-haiku-4.5", "variant": "geo", "region": "eu-central-1" }
    ],
    "tags": ["production"]
  }'
Response 201json
{
  "organization_id": "org_7Tq2Wd9KxP",
  "data": {
    "id": "mg_4kT9wQx2Lp",
    "object": "model_group",
    "etag": "\"1\"",
    "name": "Engineering models",
    "description": "Approved for Claude Code",
    "members": [
      { "provider": "anthropic", "model": "anthropic/claude-sonnet-4.5", "variant": "standard", "region": "global", "provider_key_id": null },
      { "provider": "bedrock", "model": "anthropic/claude-haiku-4.5", "variant": "geo", "region": "eu-central-1", "provider_key_id": null }
    ],
    "tags": ["production"],
    "created_at": "2026-09-30T18:02:11Z",
    "updated_at": "2026-09-30T18:02:11Z"
  }
}

Members and tags change only through their own endpoints, each conditional on the group's ETag. A member is selected by its full model slug in the model query parameter:

Remove a memberbash
curl -X DELETE "https://api.multi-router.ai/v1/model-groups/mg_4kT9wQx2Lp/members?model=anthropic/claude-haiku-4.5" \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H 'If-Match: "1"'

The routers response lists each router that uses the group:

GET /v1/model-groups/mg_4kT9wQx2Lp/routersjson
{
  "organization_id": "org_7Tq2Wd9KxP",
  "data": [
    { "id": "rt_Qm7c2HsP0a", "object": "router", "name": "claude-code-eng", "kind": "claude_proxy" }
  ],
  "next_cursor": null,
  "has_more": false
}

Errors

Creating, editing and deleting model groups return the following errors:

ErrorStatusCause
invalid_model_group400The body is not valid JSON, is missing a required field such as a member's provider or model, or is over 64 KB.
invalid_name400The name is empty or over 80 characters.
invalid_description400The description is over 2,000 characters.
invalid_tags400A tag breaks the tag rules, or the group has more than 20 tags.
too_many_members400The group has more than 100 members.
invalid_member400A member has an empty model or names a provider Multi-Router does not support.
model_not_found400A member names a model that is not in the catalog.
provider_offering_not_found400A member's provider does not offer its model.
variant_required400A member names a region without a variant.
variant_not_found400A member's provider does not offer its variant.
offering_not_active400A member's variant is deprecated or retired.
region_not_served400A member's variant is not offered in its region. The message lists the available regions.
region_not_supported400The catalog lists a member's region, but Multi-Router cannot serve it.
duplicate_member400The group contains the same model twice.
provider_key_not_found400A member's provider key is not in the organization.
provider_key_disabled400A member's provider key is disabled.
provider_key_mismatch400A member's provider key is for a different provider.
key_residency_mismatch400An OpenAI member's provider key is set to a different region than the member.
model_group_not_found404The group does not exist in the organization.
model_group_referenced409The group is used by a Claude Proxy and cannot be deleted. The message lists the routers.
model_group_policy_managed409The group is named by a policy and cannot be edited or deleted.
model_group_incompatible_with_claude_proxy409An edit adds a model that a Claude Proxy using the group does not serve.
model_group_member_exists409A member add names a model that is already a member.
use_sub_collection400A PATCH sends members or tags, which change through their own endpoints. The valid field of the error lists the endpoint path template, such as /v1/model-groups/{id}/members; {id} is not replaced with the object id.
version_conflict412The If-Match ETag is stale. The response carries the current group.

Providers & Models

Custom Models & PricingEnterprise

Apply negotiated provider prices to catalog models, and add models served from your own endpoints with their own prices.

Custom models extend the model catalog with entries a server administrator defines. A custom variant copies a catalog offering, such as Claude Sonnet 4.5 on Google Vertex, and gives it the prices from your agreement with the provider, so the costs Multi-Router records match what you pay. A custom endpoint model adds a model the catalog does not carry, served by an endpoint you operate, with its own wire ids and prices.

Custom variants

A custom variant is a copy of one catalog offering, identified by the model, provider and variant, with new prices. The copy keeps the offering's regions, wire ids, capabilities, limits, lifecycle and retirement date, and is added to the model as a new variant on the same provider, so the catalog offering stays available beside it. Because it is a copy, later catalog changes to the source offering, including price changes and retirement, do not change it. A router or model group member that names the custom variant is billed at the negotiated prices, and routers that name the catalog variant continue at list price.

To create a custom variant in the dashboard:

  1. On the Models page, select Clone with new pricing.
  2. Under Model, search the catalog by name or slug and select the model.
  3. Under Offering to copy, select the offering. Each row shows its Vendor, Variant, Regions and List pricing.
  4. Under Name and prices, enter a name, such as Vertex (Negotiated Pricing), and edit the prices, which are prefilled with the list prices for every region. A price box left empty is not charged.
  5. Select Create custom variant.
Engineering · org_7Tq2Wd9KxP

All models· Models

Clone with new pricing New custom model New groupSave view
Show:
AllModelsGroups
Status:
AllActiveDeprecated
Filters
sonnet
Models7 of 281 · 0 groups · 7 models
1–7 of 725 / page
Columns
Clone with new pricing— server administrators · installation-wide
1Model
Claude Sonnet 4.5anthropic/claude-sonnet-4.5change
2Offering to copy
VendorVariantRegionsList pricing
Anthropic29 Sep 2025 snapshot1 regionSame price in every region
AnthropicStandard1 regionSame price in every region
BedrockGeo18 regionsSame price in every region
BedrockGlobal33 regionsSame price in every region
Vertex AIStandard4 regionsPrice varies by region
Everything about the picked offering is copied — wire ids, regions, limits, lifecycle. Only the name and the prices change.
3Name and prices
Name
Vertex (Negotiated Pricing)
Variant id vertex-negotiated-pricing · shown wherever a variant is picked
Prices
StandardLong ContextBatch
USD per 1M tokens
RegionInputOutputCache readCache write 5mCache write 1h
eu$2.8$14$0.33$4.13$6.6
global$2.55$12.75$0.3$3.75$6
us$2.8$14$0.33$4.13$6.6
us-east5$2.8$14$0.33$4.13$6.6
Prefilled with every list price; edit what was negotiated. Every price shown is saved; an empty box is not charged.
Create custom variantCancel
NameKindPublisherVendorsCapabilitiesContextPrice
Claude Sonnet 5AnthropicAnthropicBedrock×2Vertex AI1M$2.00–2.20 / $10.00–11.00
Claude Sonnet 4.6AnthropicAnthropicBedrock×3Vertex AI1M$3.00–3.30 / $15.00–16.50
Claude Sonnet 4.5AnthropicAnthropic×2Bedrock×2Vertex AI200K$3.00–3.30 / $15.00–16.50
Claude Sonnet 4deprecatedAnthropicAnthropicBedrock×2Vertex AI200K$3.00 / $15.00
Claude Sonnet 3.7deprecatedAnthropicAnthropic×2Bedrock×2200K$3.00 / $15.00
Claude Sonnet 3.5deprecatedAnthropicAnthropic×2Bedrock×3200K$3.00 / $15.00
Claude Sonnet 3deprecatedAnthropicAnthropicBedrock×2200K$3.00 / $15.00
Capabilities:streamingtoolsparallel tool callsstructured outputreasoningprompt cachingGroup guarantees use capability intersection and minimum context. Input and output price envelopes stay separate.
Clone with new pricing: the Vertex AI offering of Claude Sonnet 4.5 copied with negotiated prices per region. Select to enlarge.

The variant id is derived from the name: letters and digits are kept in lowercase and every other run of characters becomes a hyphen, so Vertex (Negotiated Pricing) becomes vertex-negotiated-pricing. The name is up to 80 characters and contains at least one letter or digit, and the derived id must differ from every catalog and custom variant of the model on that provider. Routers name the custom variant by its id in routing.variant. Any offering that is not retired can be copied, except custom variants and custom endpoint models.

A router body that uses a custom variantjson
{
  "kind": "static",
  "name": "sonnet-vertex-negotiated",
  "routing": {
    "provider": "vertex",
    "model": "anthropic/claude-sonnet-4.5",
    "variant": "vertex-negotiated-pricing",
    "region": "global",
    "provider_key_id": "llpk_V7xk2PqLmN"
  },
  "prompt": { "system_message_mode": "merge" }
}

Custom endpoint models

A custom endpoint model is a model the catalog does not carry, such as a fine-tuned or self-hosted model, served by an endpoint that implements the OpenAI Chat Completions API. Routers reach it through a provider key for the Custom endpoint provider, which holds the endpoint's base URL; see Supported Providers. To create one, select New custom model on the Models page and complete the following fields:

FieldDescription
PublisherThe publisher name, such as acme, up to 80 characters, stored in lowercase.
NameThe display name, up to 80 characters, containing at least one letter or digit.
SlugThe model's slug in the form publisher/name, such as acme/sim-echo: up to 120 characters, lowercase, with each part starting with a letter or digit and using letters, digits, periods, hyphens and underscores. The Slug follows the Publisher and Name until it is edited, and it must differ from every catalog and custom model.
ContextThe context window, entered as a number of tokens or with a suffix, such as 128k or 1m. The maximum output equals the context window.
Regions and pricesOne or more regions, starting with global. Each region has a name of letters, digits, hyphens and underscores, a Wire id, which is the model name the endpoint receives, and its own prices.

Custom endpoint models are chat models with text input and output. A router uses one by selecting Use a custom endpoint instead, choosing a Custom endpoint key, and entering the model's slug as the Model, and needs no variant. The router's region selects the wire id and price; when the router has no region, Multi-Router uses the model's only region or its global region, and a model with several regions and no global region requires the router to name one in lowercase. The endpoint handles feature support, so Multi-Router forwards streaming, tools and other request features without checking them against the catalog.

Prices

Prices are set per region in US dollars per million tokens, from 0 to 1,000,000. Input and output prices are required, every other price is optional, and a price left empty is not charged. Multi-Router computes the cost of each request from the following prices:

PriceApplies to
InputPrompt tokens that are not read from or written to the cache.
OutputCompletion tokens, including reasoning tokens.
Cache readPrompt tokens read from the prompt cache.
Cache write (5 minutes)Prompt tokens written to the prompt cache with a 5-minute lifetime.
Cache write (1 hour)Prompt tokens written to the prompt cache with a 1-hour lifetime.
Long contextInput, output and cache prices that replace the standard prices when the whole prompt, including cache reads and writes, reaches the threshold in tokens. A long context price needs both an input and an output price.
Fast service tierPrices for requests the provider reports it served in fast mode, such as Anthropic's fast mode.
Web searchThe fee for a fixed number of web search tool calls, shown on the Tools tab when the source offering has one.

The price editor also holds Batch, Priority and Flex tier prices, prices by input type, per-unit prices and cache storage, which Multi-Router stores and displays; request costs use the prices in the table above. Price changes apply to requests made after the change, and earlier requests keep the cost they were recorded with. The cost of each request appears in Traffic and counts toward budgets. Requests to a custom endpoint for a model that is not registered as a custom endpoint model carry no cost.

Where custom models appear

Custom variants and custom endpoint models appear on the Models page for every user, with a Custom chip in the Kind column. A custom variant is listed as an additional offering of its model, and a custom endpoint model is listed with the vendor Custom. The expanded offering shows its origin, such as Copied from vertex · standard, catalog 5.4.1, by admin@example.com on 14 Sep 2026 · prices edited 20 Sep 2026, or Custom endpoint model, by its creator and date. The router model picker lists custom variants with the other variants of the model, and GET /v1/models lists custom endpoint models for organizations that hold an enabled Custom endpoint key.

Engineering · org_7Tq2Wd9KxP

All models· Models

Clone with new pricing New custom model New groupSave view
Show:
AllModelsGroups
Status:
AllActiveDeprecated
Filters
sonnet
Models7 of 281 · 0 groups · 7 models
1–7 of 725 / page
Columns
NameKindPublisherVendorsCapabilitiesContextPrice
Claude Sonnet 5AnthropicAnthropicBedrock×2Vertex AI1M$2.00–2.20 / $10.00–11.00
Claude Sonnet 4.6AnthropicAnthropicBedrock×3Vertex AI1M$3.00–3.30 / $15.00–16.50
Claude Sonnet 4.5CustomAnthropicAnthropic×2Bedrock×2Vertex AI×2200K$2.55–3.30 / $12.75–16.50
Baseline
Publisher
Anthropic
Slug
anthropic/claude-sonnet-4.5
Product
Claude Sonnet
Version
4.5 · semver
Released
2025-09-29
Mode
Chat
Context
200,000 · 64,000 out
Status
active
Capabilities
Streaming
Tools
Parallel tool calls
Structured output
Reasoning
Prompt caching
Modalities
textimagepdf
platform.claude.com/docs/en/about-cl… ↗
Offerings — 6 routable coordinates
VendorVariantRegionsPricing
Anthropic29 Sep 2025 snapshotsnapshot1 regionSame price in every region
AnthropicStandardrolling1 regionSame price in every region
BedrockGeosnapshot18 regionsSame price in every region
BedrockGlobalsnapshot33 regionsSame price in every region
Vertex AIStandardsnapshot4 regionsPrice varies by region
Vertex AIVertex (Negotiated Pricing)snapshotCustom4 regionsPrice varies by region
Copied from vertex · standard, catalog 5.4.1, by admin@example.com on Sep 30, 2026Edit pricesDelete

The vendor's ordinary endpoint for this model.

Rate
StandardLong ContextBatch
euclaude-sonnet-4-5@20250929$2.80 in · $14.00 out · $0.33 cache read · $4.13 5m cache write · $6.60 1h cache write
globalclaude-sonnet-4-5@20250929$2.55 in · $12.75 out · $0.30 cache read · $3.75 5m cache write · $6.00 1h cache write
usclaude-sonnet-4-5@20250929$2.80 in · $14.00 out · $0.33 cache read · $4.13 5m cache write · $6.60 1h cache write
us-east5claude-sonnet-4-5@20250929$2.80 in · $14.00 out · $0.33 cache read · $4.13 5m cache write · $6.60 1h cache write

Each row is a curated, working way to call this model. Picking one (with a region) happens when you wire a router; cross-vendor fallback composes routers.

Claude Sonnet 4deprecatedAnthropicAnthropicBedrock×2Vertex AI200K$3.00 / $15.00
Claude Sonnet 3.7deprecatedAnthropicAnthropic×2Bedrock×2200K$3.00 / $15.00
Claude Sonnet 3.5deprecatedAnthropicAnthropic×2Bedrock×3200K$3.00 / $15.00
Claude Sonnet 3deprecatedAnthropicAnthropicBedrock×2200K$3.00 / $15.00
Capabilities:streamingtoolsparallel tool callsstructured outputreasoningprompt cachingGroup guarantees use capability intersection and minimum context. Input and output price envelopes stay separate.
The custom variant beside the catalog Vertex AI offering, with its origin and the Edit prices and Delete controls. Select to enlarge.

Editing and deleting

Edit prices changes the prices of a custom variant or custom endpoint model, and the prices must cover every region of the entry. The name, publisher, slug, provider, variant, context window, regions and wire ids are fixed; to change them, delete the entry and create it again. A custom entry can be deleted only when no router or model group in any organization uses it, and a refused deletion lists up to 20 of the routers and model groups that use it, each with its organization.

Who can manage custom models

Custom models are managed in the dashboard by server administrators and have no /v1 API. Only server administrators see Clone with new pricing, New custom model, Edit prices and Delete. On the hosted service, Multi-Router performs server administration. On a self-hosted installation, any server administrator manages custom models, including the administrator created during installation and accounts an administrator grants the role from the Admin page.

Errors

Routers and requests that use custom endpoint models return the following errors:

ErrorStatusCause
router_region_required400A router uses a custom endpoint model with several regions and no global region, and names no region.
router_region_not_offered400A router names a region the custom endpoint model does not offer.
region_not_offered400A router created or changed with /v1/routers names a region the custom endpoint model does not offer.
region_not_applicable400A router on a custom endpoint names a region for a model that is not a custom endpoint model.

In the dashboard, creating a custom variant whose derived id already exists, or a custom endpoint model whose slug already exists, shows the conflict and leaves the form open to change the name or slug.

Routing

Overview

Routers hold the behavior and routing rules for your requests; API keys identify who sends them.

A router is a named object in an organization that decides how requests sent to it are served: which model and provider answer them, which provider key pays for them, where inference runs, and which prompt, parameter defaults and logging apply. An API key identifies the organization a request belongs to and what the caller is permitted to do, and carries no routing configuration. Every request therefore takes its behavior from the router it names, and changing a router changes the behavior for every caller of that router without new API keys or changes to application code.

Router types

Every router has a type, chosen when it is created, and each type encapsulates one routing behavior:

Router typeBehavior
Static routerSends every request to one model on one provider, in a set variant and region, with a set provider key.
Fallback routerTries its member static routers in order until one succeeds.
Weighted routerPicks one member static router per request at random, in proportion to its weight.
Claude ProxyServes Claude Code and Claude Desktop, forwarding requests in their native format to Anthropic, AWS Bedrock or Google Vertex.
Pi ProxyServes the Pi.Dev coding agent, forwarding requests with each person's own credentials.

The dashboard groups static, fallback and weighted routers under Routers, which route requests to a model, and Claude Proxy and Pi Proxy under Proxies, which pass existing client traffic through. An alias is a second name that points to a router and can be repointed; it is not a router type. See Alias.

Router ids and names

Every router has an id, assigned by Multi-Router when the router is created, in the form rt_ followed by 10 characters, such as rt_9Hk3Ws7PzQ. The id never changes, and requests and Traffic refer to a router by its id or by one of its aliases. A router also has a name, which is a label for the dashboard; a request selects a router by its id or alias, and two routers can share a name. Writes to /v1/routers/{id} take the router's id. Aliases are labels on a router, listed in its aliases field and managed with /v1/routers/{id}/aliases; see Alias.

Selecting a router

A request selects its router through the model field or the x-mr-router header:

RequestResult
"model": "multi-router/rt_9Hk3Ws7PzQ"The router with that id serves the request.
"model": "multi-router/production"The router that the alias production points to serves the request.
"model": "rt_9Hk3Ws7PzQ"The router with that id or alias serves the request.
x-mr-router: rt_9Hk3Ws7PzQThe header's router serves the request when the model field names no router.

A router named in the model field takes precedence over the header. When a static router is selected with the header, its Model source decides whether the request's model or the router's model is used; see Static Router. A Claude Proxy is selected with the header on its endpoints, and a Pi Proxy with the header on /v1/messages, /v1/chat/completions or /v1/responses; naming a proxy in the model field returns router_misconfigured.

Send a request to a routerbash
curl https://api.multi-router.ai/v1/chat/completions \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "multi-router/rt_9Hk3Ws7PzQ",
    "messages": [{ "role": "user", "content": "Hello" }]
  }'

How a request flows through a router

The router is selected after the API key's rate limit and the request body are checked, and before budgets and the provider call. A static router serves the request itself; a fallback or weighted router hands it to one of its member static routers, and the member's model, provider key, region, prompt binding, parameter defaults and request logging apply. Each response carries headers that name the provider, model and region that served it, how the router was selected, and how many members were tried. See the request lifecycle in Providers & Models.

Errors

Selecting a router returns the following errors:

ErrorStatusCause
router_not_found400No router or alias with that id exists in the organization.
router_disabled400The router, the alias, or the router the alias points to is disabled.
router_misconfigured400The router has no model, a fallback or weighted router has no members, or a proxy was named in the model field.
model_not_found404Any routing error for an API key limited to specific routers, including a router outside its list, so its responses reveal nothing about other routers.

Routing

Static Router

The base router type: one model on one provider, in a set variant and region, with a set provider key.

A static router sends every request to one model on one provider. It stores the model, the provider, the variant, an optional region and a provider key, which together identify one catalog entry and the account that pays for it, along with the prompt binding, parameter defaults and request logging applied to each request. Static routers are the members of fallback and weighted routers, and moving an application to a different model, provider or region requires a change to its static router and no change to the application.

Fields

A static router has the following fields:

FieldRequiredDescription
nameYesA label shown in the dashboard, up to 80 characters.
routing.providerYesThe provider id, such as anthropic or bedrock. See Supported Providers.
routing.modelYesThe model slug, such as anthropic/claude-sonnet-4.5. A custom endpoint takes any model the endpoint serves.
routing.provider_key_idYes, through the APIThe provider key, or Org-default key in the dashboard. See Provider Key.
routing.variantYes, except on custom endpointsThe variant, such as standard or geo. See Regions & Variants.
routing.regionYes through the API, except on custom endpointsThe region. When unset in the dashboard, Multi-Router selects one as described in Regions & Variants.
routing.model_sourceNorequest (default) or router. See Model source below.
prompt.system_message_modeYes, through the APIHow a bound prompt combines with the caller's system message: merge, override or fallback.
prompt.bindingNoA prompt applied to every request. See Router Settings.
descriptionNoA description shown in the dashboard, up to 500 characters.
parameter_defaultsNoDefault request parameters. See Router Settings.
observability.metadata_levelNoRequest metadata logging. See Router Settings.
observability.request_captureNoRequest payload logging. See Router Settings.

Model source

Model source decides which model a static router uses when a request selects it with the x-mr-router header. A router named in the model field always uses its own model.

ValueDashboard labelBehavior
requestUse request modelUses the caller's model with this router's provider, provider key and settings.
routerOverride with router's modelIgnores the request's model and always uses this router's model.

With Use request model, the request's model must be one the router's provider serves, and the router's variant and region apply to it; a request model that names a different provider or variant returns router_vendor_conflict or router_variant_conflict. A fallback or weighted router selected with the header always uses its members' own models.

Creating a static router in the dashboard

A static router is created on the Routers page in four steps:

  1. Select New router, choose Static Router under Routers, and select Continue.
  2. Under Details, enter a Name, and optionally a Description, a Bound prompt and Tags.
  3. Under Routing, select Choose model, pick the model's offering (provider and variant), and choose the Key and Region. The Resolves to panel shows the model id, price, billing account and context window of the choice. To call your own endpoint, select Use a custom endpoint instead, enter the Model, and choose the Custom endpoint key.
  4. Under Review & create, check each row and select Create router.

A static router can be created without a model and configured later; until it has one, its status is unconfigured and requests to it return router_misconfigured. On the router's page, the Model panel shows the Provider, Model, Variant, Region (when one is set), Provider key and Model source. Edit changes the model, provider, variant, region and key, and Model source changes in place.

Back to Routers

Support bot

enabledstatic
productionsupportAdd tag

Answers customer questions in the help center chat widget.

rt_9Hk3Ws7PzQ·created Mar 4, 2026·updated Sep 30, 2026
Disable
OverviewTrafficActivity

Aliases

Add
production

Model

Edit
Provideranthropic
Modelanthropic/claude-sonnet-4.5
Variantstandard
Provider keyAnthropic productionllpk_8Rn3Kx5WbT
Model source
Use request modelUse the caller's model with this router's provider, credential & preset

Prompt

Edit
SourceInline
Promptsupport-system(ip_5Gk2Nw8QvL)
Revision@latestnow rev 7
Prompt composition
MergeBoth router and caller system apply (default)

Request Logging

What this router records about its requests. Capped by the administrator's Storage Policy.

Edit
Request metadataFull Metadata
Request payloadsFailures only

Settings

Per-router configuration. Persisted in the router's versioned config blob.

Parameter defaults

Merged into a request when the caller doesn't set the field. The caller's value always wins. Leave blank to not default a field.

Cancel Save settings
The page of a static router: its alias, model and provider key, bound prompt, request logging and parameter defaults. Select to enlarge.

Creating a static router with the API

POST /v1/routers creates a router. The routing goes in routing: provider, model, provider_key_id, and variant and region for every provider except custom endpoints; Prompt composition goes in prompt.system_message_mode. An optional Idempotency-Key header makes the create safe to retry. Keys do not expire, and a key that completed a request stays tied to it: a retry with the same key and request returns the original response, and a different request with the same key returns 409 idempotency_key_reused. A retry while the first request is still running returns 409 idempotency_in_progress. A failed create releases the key, so a corrected request can reuse it. dry_run=true validates the router without saving it. The response is 201 with the router, a Location header and its ETag. See Routers in the API Reference.

Create a static routerbash
curl -X POST https://api.multi-router.ai/v1/routers \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "static",
    "name": "Support bot",
    "routing": {
      "provider": "anthropic",
      "model": "anthropic/claude-sonnet-4.5",
      "variant": "standard",
      "region": "global",
      "provider_key_id": "llpk_4Vd8Qm2RtL"
    },
    "prompt": { "system_message_mode": "merge" }
  }'
Response 201json
{
  "organization_id": "org_7Tq2Wd9KxP",
  "data": {
    "id": "rt_9Hk3Ws7PzQ",
    "object": "router",
    "etag": "\"1\"",
    "kind": "static",
    "name": "Support bot",
    "description": null,
    "enabled": true,
    "tags": [],
    "aliases": [],
    "rate_limits": { "rpm": null, "tpm": null },
    "routing": {
      "provider": "anthropic",
      "model": "anthropic/claude-sonnet-4.5",
      "variant": "standard",
      "region": "global",
      "provider_key_id": "llpk_4Vd8Qm2RtL",
      "model_source": "request"
    },
    "prompt": { "binding": null, "system_message_mode": "merge" },
    "parameter_defaults": {
      "temperature": null, "max_tokens": null, "top_p": null, "stop_sequences": [],
      "response_format": null, "tool_choice": null, "parallel_tool_calls": null,
      "reasoning_effort": null, "seed": null, "verbosity": null,
      "presence_penalty": null, "frequency_penalty": null
    },
    "observability": { "metadata_level": "inherit", "request_capture": "inherit", "payload_sample_pct": null },
    "created_at": "2026-10-01T12:00:00Z",
    "updated_at": "2026-10-01T12:00:00Z"
  }
}

Changing a static router

PATCH /v1/routers/{id} changes a static router's name, description, enabled state, rate limits, routing, prompt, parameter defaults and request logging. A field left out keeps its value, nested objects such as routing merge field by field, and a change to routing includes routing.region, except on custom endpoints. An unknown or read-only field returns unknown_field, except etag, which returns field_read_only. members, tags and aliases also return unknown_field and change through their own endpoints. The router's id and type are fixed. An If-Match header with the router's ETag makes the change apply only if the router has not changed since it was read, and returns 412 version_conflict otherwise. Multi-Router validates the whole router after the change before saving it, and the change applies to the next request.

Move a static router to Bedrock in the EUbash
curl -X PATCH https://api.multi-router.ai/v1/routers/rt_9Hk3Ws7PzQ \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "routing": {
      "provider": "bedrock",
      "provider_key_id": "llpk_8Nc4Xr6YwB",
      "variant": "geo",
      "region": "eu-central-1"
    }
  }'

Errors

Creating and changing a static router returns the following errors, each with status 400 unless noted:

ErrorCause
missing_fieldA required field, such as provider, model, region or system_message_mode, is missing.
missing_variantA router on a catalog provider names no variant.
unknown_providerThe provider is not supported.
model_not_foundThe model is not in the catalog.
invalid_variantThe provider does not offer the variant for the model. The message lists the valid variants.
region_not_servedThe variant is not offered in the region. The message lists the available regions.
provider_key_not_foundThe provider key is not in the organization.
provider_key_mismatchThe provider key belongs to a different provider.
provider_key_disabledThe provider key is disabled.
field_not_supported_for_kindThe body sets a field that does not apply to a static router, such as routing.routers.
unknown_fieldThe body contains an unknown or read-only field other than etag. members, tags and aliases also return it.
field_read_onlyA change sends etag.
invalid_jsonThe body is not valid JSON.
region_not_supportedMulti-Router cannot serve the region.
key_residency_mismatchThe OpenAI key's region differs from the router's region.
region_not_applicableA router on a custom endpoint sets a region for a model that is not a custom endpoint model.
region_not_offeredThe custom endpoint model does not offer the region.
invalid_payload_sample_pctThe payload sample percentage is above 100.
prompt_not_foundThe bound prompt does not exist.
pinned_value_not_foundThe pinned prompt revision or commit does not exist.
router_not_found (404)The router does not exist.
policy_managed (409)A tools policy created the router.
version_conflict (412)The If-Match ETag is stale. The response carries the current router.
idempotency_in_progress (409)A request with the same Idempotency-Key is still running.
idempotency_key_reused (409)The Idempotency-Key was used with a different request.

Routing

Fallback Router

Try a list of static routers in order until one succeeds, for failover across models and providers.

A fallback router holds an ordered list of member static routers and tries them in order until one returns a response. When the first member's provider is rate limited, unavailable or misconfigured, the request moves to the next member, so callers are not affected when a member fails. Each member brings its own model, provider, provider key, region and settings, so a fallback router can fail over across models, providers and regions, such as from Claude on Anthropic to the same model on AWS Bedrock.

Members

A member is a static router in the same organization. A fallback router has between 1 and 50 members, each listed once; members are static routers only. Members are tried in the order they are listed, top first, and a disabled or deleted member is skipped. A member keeps serving its own callers when it is also part of a fallback router, and a static router can be deleted only after it is removed from every fallback and weighted router.

When a request moves to the next member

Each failure either moves the request to the next member or returns the error to the caller:

FailureResult
The member router is missing or disabled.Moves to the next member.
A budget refuses the member's model.Moves to the next member.
The provider key is missing, disabled or for another provider.Moves to the next member.
The bound prompt cannot be applied, such as missing_prompt_variables.Moves to the next member.
A catalog check fails, such as region_not_served or tools_not_supported.Moves to the next member.
The provider times out or the connection fails.Moves to the next member.
The provider returns 429.Moves to the next member.
The provider rejects the credentials (401).Moves to the next member.
The provider does not offer the model (404).Moves to the next member.
The provider returns a 5xx error after retries.Moves to the next member.
The request is malformed or uses an unsupported shape (invalid_request, not_supported).Returns the error.
The provider rejects the request as invalid (400).Returns the error.
The provider returns another 4xx error, such as 403, 409, 413 or 422.Returns the error.
The client disconnects.Ends the request.

When every member fails, the response carries the last member's error, or 429 budget_exhausted when budgets refused every member. A streamed response commits to a member when the provider sends its first event; an empty stream, or a failure from the table above, before then moves to the next member, and an error after it arrives as an error event in the stream.

Settings

A fallback router holds its name, description, tags and members. The model, provider key, region, prompt binding, parameter defaults and request logging of a request come from the member that serves it, and the router's page shows Model, Prompt and Request Logging panels marked Configured on each member. When a fallback router is selected with x-mr-router, the request's model is ignored and each member uses its own model.

Headers and Traffic

Successful responses through a fallback router carry x-mr-policy: fallback (or header when selected with x-mr-router) and the provider, model and region headers of the member that served the request, along with the following:

HeaderValue
x-mr-composerThe fallback router's id, also when the request named it through an alias.
x-mr-attemptsThe number of members tried.

In Traffic, the Router column shows the member that served the request, the Type column shows fallback, and the Attempts column shows the members tried; the Type and Attempts columns are hidden by default and turned on with Columns. Budgets apply to the fallback router and the model of each member it tries.

Creating a fallback router

A fallback router is created on the Routers page:

  1. Select New router, choose Fallback Router under Routers, and select Continue.
  2. Under Configure, enter a Name, an optional Description and Tags, and add members with Add Static router. Order them with Move up and Move down.
  3. Select Review, check the numbered members, and select Create router.

On the router's page, the Members panel lists the members in order; Edit adds, removes and reorders them, and Save members applies the change. With the API, send POST /v1/routers with the member router ids in order in routing.routers:

Back to Routers

claude-fallback

enabledfallback
productionsupportAdd tag

Serves the support bot from Anthropic and fails over to Bedrock in eu-central-1.

rt_6Fp1Zr4MxJ·created Jun 12, 2026·updated Oct 1, 2026
Disable
OverviewTrafficActivity

Aliases

Add
support-ha

Members (2)

Edit

Members are tried in order; failures advance to the next.

1
Support botrt_9Hk3Ws7PzQ
2
Support bot · Bedrock EUrt_2Lw6Yd8NcV

Model

Configured on each member.

A composer doesn't call a model itself — it routes each request to one of its Static router members. The model, provider, and credential are set on each member.

Prompt

Configured on each member.

Request Logging

Configured on each member.

A fallback router's page: its members in the order they are tried, with model, prompt and logging configured on each member. Select to enlarge.
Create a fallback routerbash
curl -X POST https://api.multi-router.ai/v1/routers \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "fallback",
    "name": "claude-fallback",
    "routing": { "routers": ["rt_9Hk3Ws7PzQ", "rt_2Lw6Yd8NcV"] }
  }'

Members change through /v1/routers/{id}/members: POST adds a member at an optional zero-based position, PATCH on /members/{key} moves it to a new position, and DELETE removes it. The key is the member's router id.

Move a member to the frontbash
curl -X PATCH https://api.multi-router.ai/v1/routers/rt_7Fb2Kq9WnC/members/rt_2Lw6Yd8NcV \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H 'If-Match: "2"' \
  -H "Content-Type: application/json" \
  -d '{ "position": 0 }'

Errors

Creating and changing a fallback router returns the following errors, each with status 400:

ErrorCause
missing_fieldThe members list is missing or empty.
too_many_membersThe router has more than 50 members.
invalid_memberA member is not a static router in the organization.
duplicate_memberA member is listed twice.
invalid_weightA member of a fallback router has a weight.
member_existsA member add names a router that is already a member.
field_not_supported_for_kindThe body sets a field that does not apply, such as prompt.

Routing

Weighted Router

Split traffic across static routers by weight, for A/B tests and gradual rollouts.

A weighted router splits traffic across its member static routers. On each request it picks a member at random in proportion to the members' weights, so members weighted 90 and 10 receive about 90% and 10% of requests. Changing the weights changes the split at once, without changing clients, which suits A/B tests between models or providers and gradual rollouts of a new model. When the picked member fails, the request moves to another member, as in a fallback router.

Members and weights

A member is a static router in the same organization with a weight, a whole number of 1 or more. The weights do not need to add up to any total: each member's share is its weight divided by the sum of all weights. A weighted router has between 1 and 50 members, each listed once. A member added in the dashboard starts with weight 1, and the router's Members panel shows each member's share as a percentage of the total.

How a member is selected

Multi-Router draws a new random order of the members for every request, weighted by their weights, and tries the members in that order. The first member is picked with probability equal to its share, and when it fails, the request moves to the next member in the same order. Requests are independent, so a caller is not kept on one member across requests. The failures that move a request to the next member are the same as for a fallback router; see Fallback Router.

Headers, settings and Traffic

A weighted router holds its name, description, tags and members, and each request takes its model, provider key, region and settings from the member that serves it. Responses carry x-mr-policy: weighted (or header when selected with x-mr-router), x-mr-composer with the weighted router's id, and x-mr-attempts. In Traffic, the Router column shows the member that served each request, and the Type column shows weighted. When a weighted router is selected with x-mr-router, the request's model is ignored and each member uses its own model.

Creating a weighted router

A weighted router is created on the Routers page:

  1. Select New router, choose Weighted Router under Routers, and select Continue.
  2. Under Configure, enter a Name, an optional Description and Tags, add members with Add Static router, and set each member's weight.
  3. Select Review, check the members and weights, and select Create router.
Create a weighted router that sends 10% of traffic to a new modelbash
curl -X POST https://api.multi-router.ai/v1/routers \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "weighted",
    "name": "sonnet-rollout",
    "routing": {
      "routers": [
        { "router_id": "rt_9Hk3Ws7PzQ", "weight": 90 },
        { "router_id": "rt_5Qa1Mf3KzE", "weight": 10 }
      ]
    }
  }'

To move more traffic to the new model, change each member's weight with PATCH /v1/routers/{id}/members/{key}, where the key is the member's router id, or change the weights in the router's Members panel.

Send 25% to the new modelbash
curl -X PATCH https://api.multi-router.ai/v1/routers/rt_3Vc8Np2HsY/members/rt_5Qa1Mf3KzE \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H 'If-Match: "1"' \
  -H "Content-Type: application/json" \
  -d '{ "weight": 25 }'

Errors

Creating and changing a weighted router returns the following errors, each with status 400:

ErrorCause
invalid_weightA member has no weight, or a weight of 0.
invalid_jsonA weight is not a whole number.
missing_fieldThe members list is missing or empty.
too_many_membersThe router has more than 50 members.
invalid_memberA member is not a static router in the organization.
duplicate_memberA member is listed twice.
member_existsA member add names a router that is already a member.
field_not_supported_for_kindThe body sets a field that does not apply, such as prompt.

Routing

Claude Proxy

The router that serves Claude Code and Claude Desktop: its routes, regions, credentials, models, settings and errors.

A Claude Proxy is a router that serves Claude Code, Claude Desktop and other clients of Anthropic's APIs. It receives requests in the native Anthropic, AWS Bedrock or Google Vertex format, forwards the request body to the provider its route names, and returns the provider's response unchanged, so every feature of those APIs is available. A Claude Proxy has a route, which sets the provider and variant, a region, a credential source and the models it serves, and it records usage for each person in Traffic. How Claude Code and Claude Desktop are configured to use a Claude Proxy is described in Agents & Editors.

Endpoints

The endpoints of a Claude Proxy are the API paths that accept requests in each provider's native format. A request selects the proxy with the X-MR-Router header, which names the proxy's id or an alias that points to it and is required on the Bedrock and Vertex endpoints. Each endpoint serves the routes of its own provider:

EndpointFormatServes
POST /v1/messagesAnthropic MessagesThe Anthropic API route.
POST /v1/messages/count_tokensAnthropic token countingThe Anthropic API route.
POST /bedrock/model/{model}/invokeAWS BedrockThe Bedrock routes.
POST /bedrock/model/{model}/invoke-with-response-streamAWS Bedrock streamingThe Bedrock routes.
POST /vertex/projects/{p}/locations/{l}/publishers/anthropic/models/{m}:rawPredictGoogle VertexThe Vertex AI route, including token counting with the model count-tokens.
…:streamRawPredictGoogle Vertex streamingThe Vertex AI route.
GET /v1/modelsAnthropic model listThe models the proxy serves, answered from the catalog.

On the Vertex endpoint, Multi-Router uses the project of the provider key and the region of the route, whatever project and location the client's URL names. The Bedrock endpoint accepts any model name the proxy recognizes in the URL, including a URL-encoded inference profile ARN.

Routes

A route is the provider and variant a Claude Proxy sends requests to. The dashboard lists the following routes:

RouteRoute idProvider and variantDescription
Anthropic APIanthropicAnthropic, standardForwards to api.anthropic.com in the global or us region.
Bedrock (Global)bedrock-globalAWS Bedrock, globalCross-region inference profiles. Inference can run in any AWS region, with no residency guarantee.
Bedrock (Geo)bedrock-geoAWS Bedrock, geoGeography inference profiles. Inference stays inside one geography, such as the US, EU, Japan or Australia.
Bedrock (In-region)bedrock-in-regionAWS Bedrock, in_regionBare foundation-model ids. Inference stays in the single region called, and fewer models are offered.
Vertex AIvertexGoogle Vertex, standardVertex AI in the project of the provider key and the region of the route.
CustomcustomSet for each modelRoutes each model through a model group, whose members name their own provider, variant and region. See Custom route.

Every route except Custom requires a region, chosen from the regions the route serves. A model with no offering in the chosen region is Not Available there, and Multi-Router does not fall back to another region. The dashboard shows how many of the route's models each region serves. See Regions & Variants.

Credentials

The credential source decides whose provider account pays for requests:

Credential sourceDashboard labelRoutesBehavior
organizationOrganization KeyEvery routeMulti-Router removes the client's credential and sends the provider key set on the proxy. Usage is billed to the organization's Anthropic, AWS or Google Cloud account.
developerDeveloper CredentialsAnthropic APIThe client's own Anthropic credential is forwarded unchanged, and usage is billed to that person's account.

The Multi-Router API key travels in the X-MR-Api-Token: Bearer <key> header. With Organization Key, the client can send it in Authorization or x-api-key instead, because Multi-Router replaces that credential with the provider key; with Developer Credentials, a Multi-Router API key in those headers returns 400.

An organization key must be an enabled key of the route's provider. A Bedrock API key must have been created for the proxy's region, while AWS access keys and Vertex keys work in any region. When a proxy's key is later disabled or deleted, its requests return 503 until the proxy uses another key. See Provider Key.

Models

The models a Claude Proxy serves are the current Claude models listed in the catalog's Claude Proxy section, such as Claude Opus 5.5, Claude Sonnet 5 and Claude Haiku 4.5. A proxy can turn off individual models, and at least one model must stay served. Each model has one of three states on a proxy:

StateAPI valueMeaning
ServedservedThe route offers the model in the proxy's region, and the proxy serves it.
DisableddisabledThe proxy has turned the model off. The dashboard shows a Disabled badge.
Not availablenot_availableThe route does not offer the model in the proxy's region. The dashboard shows Not available in the region.

Clients name models in their own format, and Multi-Router maps each name to the catalog model and then to the model id of the proxy's route and region. For example, every one of these names resolves to Claude Sonnet 4.6:

Name the client sendsModel id sent to the provider
claude-sonnet-4-6claude-sonnet-4-6 on the Anthropic API and Vertex AI routes
us.anthropic.claude-sonnet-4-6global.anthropic.claude-sonnet-4-6 on Bedrock (Global)
anthropic/claude-sonnet-4.6jp.anthropic.claude-sonnet-4-6 on Bedrock (Geo) in ap-northeast-1
anthropic.claude-sonnet-4-6anthropic.claude-sonnet-4-6 on Bedrock (In-region)

Because the route and region decide where inference runs, a client's Bedrock inference profile prefix or Vertex location does not change it. GET /v1/models with the proxy's X-MR-Router header lists the served models with the model ids of the proxy's route.

Custom route

The Custom route serves the members of a model group, each on the provider, variant and region the member names, so one Claude Proxy can serve one model on Anthropic and another on AWS Bedrock in the EU. Every member must be a Claude model the Claude Proxy serves, appear once, and name a variant and a region. The Custom route uses organization credentials with one provider key for each member, set in routing.model_keys, and has no region of its own. A request for a model outside the group is refused. Each endpoint serves only its own provider, so a client reaches the group members on the provider of the endpoint it calls: a Claude Code client set up for Bedrock reaches the members on AWS Bedrock. The model group is set with routing.model_group when the proxy is created, or with PATCH /v1/routers/{id}. See Model Group.

Request handling

Multi-Router forwards the client's request body unchanged, with two exceptions: it replaces the model name with the model id of the route when they differ, and on the Anthropic API route in the us region it adds "inference_geo": "us" when the request omits it. It forwards the client's headers, and removes cookies, forwarding headers and every x-mr- header; with Organization Key it also replaces the client's credential headers and adds an anthropic-version header when the client sends none. The query string is forwarded as sent, except one that carries a Multi-Router API key or a reserved provider parameter, which returns 400. Requests are sent to the provider once, without retries, and the provider's status and error body are returned to the client.

LimitValue
Request body32 MB
Time to the provider's response headers300 seconds

Settings

A Claude Proxy's settings are its routing configuration (route, region, credential source, provider key and disabled models), its request logging, and its name, description and tags. Request logging records each inference request in Traffic according to the proxy's Request Logging settings; see Router Settings and Observability. Budgets apply to a Claude Proxy and its models. Prompt bindings, parameter defaults and model source apply to static routers and have no effect on a Claude Proxy.

Creating a Claude Proxy

To create a Claude Proxy in the dashboard:

  1. On the Routers page, select New router, and under Proxies choose Claude Proxy.
  2. Under Details, enter a Name, and optionally a Description and Tags.
  3. Under Routing, choose a Route and a Region, turn off any models the proxy should not serve, and choose Developer Credentials or Organization Key with a provider key.
  4. Under Review & create, check the Route, Region (or Model group on the Custom route), Models and Credentials, and create the proxy.

The Routing card shows the models the proxy serves with their model ids and prices, the client endpoint for the route, and the Claude Code setup. To create a Claude Proxy with the API, send POST /v1/routers with kind claude_proxy and its routing: route, region, credential_source, provider_key_id, model_group, model_keys and disabled_models. Static router fields, such as prompt and parameter_defaults, are refused:

Back to Routers

Eng proxy

Route: bedrock-geo

enabledclaude proxy
engAdd tag

Claude Code for the engineering team, served from Bedrock in the US.

rt_5Tc7Hn1GsR·created Aug 18, 2026·updated Oct 1, 2026
Disable
OverviewTrafficActivity

Aliases

Add
claude-code

Routing

How the model names Claude Code sends are resolved and where they are served.

Bedrock (Geo) · us-west-2Edit
Route
Bedrock (Geo)bedrock · geo12 of 12 models · 19 regions
Geography inference profiles. Inference stays inside one geography (US, EU, APAC, Japan or Australia).
Region
us-west-2All 12 modelsone of 19 regions on Bedrock (Geo)
Models
11 of 12 models routable · 1 disabledBedrock (Geo) · us-west-2
ModelVersionVendorVariantRegionWire id chat · count tokensPrice / 1M in · out
Claude Fable 5
anthropic/claude-fable-5
5bedrockgeous-west-2
us.anthropic.claude-fable-5
anthropic.claude-fable-5
$11.00 / $55.00
Claude Fable 5.1
anthropic/claude-fable-5.1
5.1bedrockgeous-west-2
us.anthropic.claude-fable-5-1
anthropic.claude-fable-5-1
$11.00 / $55.00
Claude Haiku 4.5
anthropic/claude-haiku-4.5
4.5bedrockgeous-west-2
us.anthropic.claude-haiku-4-5-20251001-v1:0
anthropic.claude-haiku-4-5-20251001-v1:0
$1.10 / $5.50
Claude Opus 4.5
anthropic/claude-opus-4.5
4.5bedrockgeous-west-2
us.anthropic.claude-opus-4-5-20251101-v1:0
anthropic.claude-opus-4-5-20251101-v1:0
$5.50 / $27.50
Claude Opus 4.6
anthropic/claude-opus-4.6
4.6bedrockgeous-west-2
us.anthropic.claude-opus-4-6-v1
anthropic.claude-opus-4-6-v1
$5.50 / $27.50
Claude Opus 4.7
anthropic/claude-opus-4.7
4.7bedrockgeous-west-2
us.anthropic.claude-opus-4-7
anthropic.claude-opus-4-7
$5.50 / $27.50
Claude Opus 4.8
anthropic/claude-opus-4.8
4.8bedrockgeous-west-2
us.anthropic.claude-opus-4-8
anthropic.claude-opus-4-8
$5.50 / $27.50
Claude Opus 5Disabled
anthropic/claude-opus-5
5bedrockgeous-west-2
us.anthropic.claude-opus-5
anthropic.claude-opus-5
$5.50 / $27.50
Claude Opus 5.5
anthropic/claude-opus-5.5
5.5bedrockgeous-west-2
us.anthropic.claude-opus-5-5
anthropic.claude-opus-5-5
$4.40 / $22.00
Claude Sonnet 4.5
anthropic/claude-sonnet-4.5
4.5bedrockgeous-west-2
us.anthropic.claude-sonnet-4-5-20250929-v1:0
anthropic.claude-sonnet-4-5-20250929-v1:0
$3.30 / $16.50
Claude Sonnet 4.6
anthropic/claude-sonnet-4.6
4.6bedrockgeous-west-2
us.anthropic.claude-sonnet-4-6
anthropic.claude-sonnet-4-6
$3.30 / $16.50
Claude Sonnet 5
anthropic/claude-sonnet-5
5bedrockgeous-west-2
us.anthropic.claude-sonnet-5
anthropic.claude-sonnet-5
$2.20 / $11.00
Client endpoint
Anthropic dialecthttps://api.multi-router.airefuses every model on this route
Bedrock dialecthttps://api.multi-router.ai/bedrockserves every model
Vertex dialecthttps://api.multi-router.ai/vertexrefuses every model on this route
What Claude Code is pointed at. A request may arrive on any door; after the model resolves, its vendor is checked against the door.
Forwards to
bedrock · us-west-2 · 11 models
POSThttps://bedrock-runtime.us-west-2.amazonaws.com/model/{chat id}/invokeinvoke-with-response-stream when streaming
model list
GEThttps://api.multi-router.ai/v1/modelsanswered from the catalog, not forwarded · 11 models, each under its chat wire id
Credentials
Organization Key
Bedrock productionAPI keybedrockus-west-2
Carries us-west-2, the region selected above.
Claude Code setup

Request Logging

What this router records about its requests. Capped by the administrator's Storage Policy.

Edit
Request metadataFull Metadata
Request payloadsNone
A Claude Proxy's Routing card on Bedrock (Geo) in us-west-2: the models it serves with their model ids and prices, one disabled, and its organization key. Select to enlarge.
Create a Claude Proxy on Bedrock (Geo)bash
curl -X POST https://api.multi-router.ai/v1/routers \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "claude_proxy",
    "name": "Eng proxy",
    "routing": {
      "route": "bedrock-geo",
      "region": "us-west-2",
      "credential_source": "organization",
      "provider_key_id": "llpk_4Vd8Qm2RtL",
      "disabled_models": ["anthropic/claude-opus-5"]
    }
  }'

A Claude Proxy created without routing uses the Anthropic API route in the global region with developer credentials. Its routing is read and changed with the following endpoints:

MethodPathDescriptionScope
GET/v1/routers/{id}The proxy, with its routing: route, region, credential source, provider key, model group, model keys and disabled models.routers:read
GET/v1/routers/{id}/routesEvery model the proxy serves, with its provider, variant, region and provider key.routers:read
PATCH/v1/routers/{id}Changes the routing fields sent. A route switch sends the fields the new route needs in the same request.routers:write

Policy-managed Claude Proxies

A policy-managed Claude Proxy is one Multi-Router creates when a person assigned to a tools policy sets up Claude Code or Claude Desktop in the Portal, one for each tool, with routing taken from the policy. These proxies carry the reserved person: and policy: tags and are managed through the policy: changing, enabling, disabling or deleting one directly returns 409 policy_managed. See Self-Service Provisioning.

Traffic and pricing

Traffic records one row for each inference request through a Claude Proxy, with the name the client sent, the model and model id that served it, the provider, variant and region, and the cost. Token counting requests are not recorded. Cost is computed from the catalog price of the route and region; on the Anthropic API route it follows the region Anthropic reports the request ran in.

Errors

Creating or changing a Claude Proxy returns the following errors, each with status 400:

ErrorCause
route_not_foundThe route is not in the catalog, or the routing settings are missing.
region_not_on_routeThe route requires a region, the route does not serve the region, or the Custom route names a region.
credential_not_allowedDeveloper credentials are used on a route other than Anthropic API, or the Custom route names a single key or developer credentials.
key_not_foundOrganization credentials name no key, a key not in the organization, or a disabled key, or a Custom member has no key.
key_vendor_mismatchThe key belongs to a different provider than the route or member.
key_region_mismatchA Bedrock API key was created for a region other than the proxy's region, or a Bedrock or Vertex key has invalid settings.
group_not_foundThe Custom route names no model group or a model group not in the organization, or a model group is set on another route.
group_member_not_servedThe model group has no members, a member is not a served Claude model, appears twice, lacks a variant or region, or is not served at its provider, variant and region, or member keys are set on another route or name a model the proxy does not serve.
all_models_disabledEvery model the route serves in the region, or every group member, is disabled.
disabled_not_in_modelsThe disabled list names a model the proxy does not serve, or names one twice.
catalog_section_missingThe catalog has no Claude Proxy section.

Requests through a Claude Proxy return the following errors, in the format of the endpoint:

StatusCause
404The model is unknown, retired or deprecated, disabled on the proxy, not available on the route in its region, outside the Custom route's model group, or served by another endpoint's provider.
403The Claude Proxy is disabled.
503The proxy's provider key is missing or disabled, its route or the catalog's Claude Proxy section is missing, its model group is missing, a Custom member has no key, or a Vertex access token is not ready.
502The provider could not be reached or timed out.
429The API key's rate limit or a budget refused the request.
413The request body is over 32 MB.
405A method other than POST was used on the Bedrock or Vertex endpoint.
400The X-MR-Router header is missing on the Bedrock or Vertex endpoint, the Multi-Router API key was sent in a forwarded header, the query string carries a Multi-Router API key or reserved parameter, or an inference_geo conflicts with the proxy's region.

Routing

Pi Proxy

The router that serves the Pi.Dev coding agent, forwarding its requests to Anthropic or OpenAI with each person's own credentials.

A Pi Proxy is a router that serves the Pi.Dev coding agent. It forwards Pi.Dev's requests to Anthropic or OpenAI, including OpenAI Codex with a ChatGPT subscription, with each person's own provider credentials, and records usage for each person in Traffic. A Pi Proxy has no routing configuration: each request goes to the provider the request is written for. How Pi.Dev is configured to use a Pi Proxy is described in Agents & Editors.

Endpoints

The endpoints of a Pi Proxy are the API paths Pi.Dev sends requests to. A request selects the proxy with the X-MR-Router header:

EndpointForwards to
POST /v1/messagesapi.anthropic.com
POST /v1/chat/completionsapi.openai.com
POST /v1/responsesapi.openai.com
POST /backend-api/codex/responseschatgpt.com, for OpenAI Codex with a ChatGPT subscription, streaming only

/v1/responses and /backend-api/codex/responses are served only through a Pi Proxy. A Pi Proxy serves neither token counting nor GET /v1/models.

Credentials

A Pi Proxy forwards each person's own Anthropic or OpenAI credential unchanged and adds no provider key, so usage is billed to that person's provider account. The Multi-Router API key travels in the X-MR-Api-Token: Bearer <key> header, and a Multi-Router API key in a header the proxy forwards to the provider returns 400.

Models

A Pi Proxy forwards requests for models in the catalog for the endpoint's provider: Anthropic models on /v1/messages and OpenAI models on the OpenAI endpoints. A request for a model the catalog does not list for that provider returns 400. A router or alias named in model on /v1/messages or /v1/chat/completions sends the request through that router, and on /v1/responses returns 400. Codex requests and requests with no model are forwarded without a check.

Creating a Pi Proxy

A Pi Proxy is created in the dashboard by selecting New router on the Routers page, choosing Pi Proxy under Proxies, and entering a Name; the dashboard then shows the models.json settings that point Pi.Dev at the proxy. With the API, send POST /v1/routers:

Create a Pi Proxybash
curl -X POST https://api.multi-router.ai/v1/routers \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "pi_proxy", "name": "Eng Pi Proxy" }'

A Pi Proxy accepts no routing settings: its routing is left out, or sent empty or with null values only. PATCH /v1/routers/{id} changes its name, description, enabled state, rate limits and request logging, and its tags change through the tags endpoints. See Managing Routers.

Limits

Limits are the size and time bounds a Pi Proxy applies to each request:

LimitValue
Request body on /v1/messages32 MB
Request body on the OpenAI and Codex endpoints4 MiB
Time to the provider's response headers120 seconds

Traffic

Traffic records one row for each request through a Pi Proxy, with the provider, the model id that served it and the person who sent it, according to the proxy's Request Logging settings.

Errors

Requests through a Pi Proxy return the following errors, in the format of the endpoint:

StatusCause
404A request to /v1/responses or /backend-api/codex/responses names no Pi Proxy, or the API key cannot use it.
403The Pi Proxy is disabled.
400The model is not in the catalog for the provider, /v1/responses names a router, or the Multi-Router API key is in a forwarded header.
413The request body is over the endpoint's limit.
429The API key's rate limit or a budget refused the request.
502The provider could not be reached or timed out.

Routing

Alias

Stable names that point to a router and can be repointed to move every caller at once.

An alias is a second name for a router, such as production, that points to the router's id. Requests, API keys limited to specific routers, and Traffic can use the alias in place of the router's id, and repointing the alias to another router moves every caller to that router at once, with no change to applications or API keys. An alias is not a router type: it has no model, provider or settings of its own, and a request through an alias is served by the router it points to.

Alias names

An alias name is 1 to 120 characters of letters, digits, hyphens, underscores and periods, unique in the organization across router ids and aliases. The prefix rt_ is reserved for router ids. An alias can point to any router in the same organization except another alias, and a router can have several aliases.

Using an alias

A request names an alias the same way as a router id, in the model field as multi-router/production or production, or in the x-mr-router header. An API key limited to an alias follows the alias when it is repointed. In Traffic, the Alias column shows the alias the request used and the Router column shows the router that served it. A request through an alias returns router_disabled when either the alias or its router is disabled.

Managing aliases in the dashboard

Aliases are managed in the Aliases panel on each router's page, which lists the identifiers that resolve to the router. Add creates an alias: as the name is typed, the panel shows Available, or why the name cannot be used. Move to another router repoints an alias, and the panel notes that the mapping flips atomically and that requests in progress are unaffected; when API keys are limited to the alias, it also notes that those keys follow the alias to its new router. Remove alias deletes it.

Managing aliases with the API

Through the API, an alias is a label on a router, listed in the router's aliases field and managed with two endpoints that take the router's rt_ id:

MethodPathDescriptionScope
POST/v1/routers/{id}/aliasesAttach the alias sent as alias. Returns 201 for a new alias, or 200 when the alias is already on this router or moves here from another router.routers:write
DELETE/v1/routers/{id}/aliasesDetach the alias named in the alias query parameter.routers:delete
Create an aliasbash
curl -X POST https://api.multi-router.ai/v1/routers/rt_9Hk3Ws7PzQ/aliases \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "alias": "production" }'
Repoint the aliasbash
curl -X POST https://api.multi-router.ai/v1/routers/rt_2Lw6Yd8NcV/aliases \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "alias": "production" }'

Attaching an alias to the router it should point to repoints it as a single atomic update, and the response names the previous router in moved_from. A router cannot be deleted while it has aliases; repoint or detach them first.

Detach the aliasbash
curl -X DELETE "https://api.multi-router.ai/v1/routers/rt_2Lw6Yd8NcV/aliases?alias=production" \
  -H "Authorization: Bearer $MR_API_KEY"

Errors

Creating, repointing and deleting aliases return the following errors:

ErrorStatusCause
invalid_alias400The name is invalid, or is a router id.
missing_field400The body has no alias.
unknown_field400The body has a field other than alias.
router_not_found404The router id matches no router in the organization.
policy_managed409The router was created by a tools policy.
alias_changed409The alias changed at the same time. Retry.
has_dependent_aliases409A router is deleted while it has aliases.

Routing

Router Settings

Parameter defaults, prompt bindings, request logging, tags and descriptions, and which router types use each.

Router settings are the behavior a router applies to every request beyond choosing a model: default request parameters, a bound prompt, what Traffic records, and the labels used to find and group routers. Settings apply to requests after they change, with no change to callers. Which settings a router has depends on its type:

SettingStatic routerFallback, weightedClaude ProxyPi Proxy
Parameter defaultsYesFrom the serving memberNoNo
Prompt bindingYesFrom the serving memberNoNo
Request loggingYesFrom the serving memberYesYes
TagsYesYesYesYes
DescriptionYesYesYesYes

Parameter defaults

Parameter defaults are request parameters a static router fills in when a request leaves them out, on both Chat Completions and Messages. The caller's value always wins, and a parameter sent as null or an empty stop list counts as left out. Messages requires max_tokens in every request, so the Max tokens default applies to Chat Completions. Parameter defaults are set in the Settings panel of the router's page under Parameter defaults, with Stop sequences entered as a comma-separated list, and saved with Save settings.

FieldDashboard labelValues
temperatureTemperature0 to 2
max_tokensMax tokens1 or more
top_pTop P0 to 1
seedSeedA whole number
presence_penaltyPresence penalty-2 to 2
frequency_penaltyFrequency penalty-2 to 2
reasoning_effortReasoning effortnone, minimal, low, medium, high, xhigh
verbosityVerbositylow, medium, high
response_formatResponse formattext, json_object
tool_choiceTool choicenone, auto, required
parallel_tool_callsParallel tool callstrue or false
stop_sequencesStop sequencesUp to 10, each up to 200 characters

Prompt binding

A prompt binding attaches a saved prompt to a static router, so every request through the router carries it. The prompt is an inline prompt or a file in a connected GitHub repository, and the binding either tracks its latest revision or is pinned to a revision number or commit. A text prompt combines with the caller's system message according to Prompt composition; the caller's system message is the first message, when its role is system. A chat prompt's messages are inserted before the conversation as written and replace the caller's system message, and Prompt composition does not apply to it.

Prompt compositionValueBehavior
MergemergeThe router's prompt comes first, followed by the caller's system message; on Messages the two stay separate blocks. This is the dashboard's default.
OverrideoverrideOnly the router's prompt applies, and the caller's system message is dropped.
FallbackfallbackThe caller's system message applies when present; otherwise the router's prompt applies.

A prompt with {{variables}} takes their values from the request's prompt_variables object on Chat Completions and Messages, and a missing variable returns missing_prompt_variables. See Prompt Variables. In the dashboard, the Prompt panel shows the Source, Prompt, Revision and Prompt composition, with Bind or Edit to choose the prompt, and the Bind a prompt panel offers Track latest or Pin to a specific revision. See Prompt Binding.

Prompt binding pinned to revision 3json
{
  "prompt": {
    "binding": {
      "source": "inline",
      "prompt_id": "ip_6Rt2Kw9QmD",
      "version_mode": "pinned",
      "pinned_value": "3"
    },
    "system_message_mode": "merge"
  }
}

Request logging

Request logging sets what Traffic records about each request a router serves: Request metadata (None, Failures only, Anonymous Metadata or Full Metadata) and Request payloads (None, Failures only, Sample or All Payloads), with a sample percentage for Sample. A router's logging is capped by the administrator's Storage Policy, options above the cap show as Administrator managed, and the sample percentage cannot exceed the administrator's limit. Request payloads record no more than Request metadata allows: None turns payloads off, and Failures only limits payloads to failures. A router that has never been changed follows the Storage Policy. See Observability for what each level records and how to view it.

Tags

Tags are labels that group routers. A router carries up to 20 tags of up to 80 characters each; tags are stored in lowercase without duplicates, and contain no # or control characters. Budgets can be scoped to routers by tag, and the Routers page lists tags in its views rail and filters by them. The prefixes person: and policy: are reserved for routers created by tools policies. Tags are set in the dashboard, and an alias has no tags of its own.

Description

A description is free text about what a router is for and who owns it, up to 500 characters, shown on the router's page and edited with the pencil next to it.

Routing

Managing Routers

Find, inspect, enable, disable and delete routers in the dashboard and through the API.

Routers are managed on the Routers page of the dashboard and through the /v1/routers API. Organization admins create, change, enable, disable and delete routers, organization members can view them, and API keys need the router scopes described below.

The Routers page

The Routers page lists every router in the selected organization. A strip at the top shows Routers and Enabled, counted over the routers that match the current filters, with Aliases, Requests 24h, Errors 24h and p95 24h. The table lists each router's name and id with optional columns:

ColumnShows
Statusenabled, disabled, unconfigured for a router with no model, or needs re-save for a Claude Proxy whose routing has not been saved.
ModelThe router's model, or its members or route.
PromptThe bound prompt.
AliasesThe router's aliases.
24h reqRequests in the last 24 hours.
UpdatedWhen the router last changed. Sorting by it toggles newest and oldest first.
Engineering · org_7Tq2Wd9KxP

All routers

Save viewNew router
Status:
AllActiveDisabled
Filters
Search name, id, model, tag, alias…
Export
Routers
8
Enabled
7
Aliases
3
Requests 24h
70K
Errors 24h
312
p95 24h
1840 ms
Routers
1–8 of 825 / page
Columns
Updated
NameStatusModelPromptAliases24h reqUpdated
Eng proxy
rt_5Tc7Hn1GsR
enabledbedrock-geo route
—
claude-code
41K12m ago
Support bot
rt_9Hk3Ws7PzQ
enabledanthropic / anthropic/claude-sonnet-4.5
support-system
ip_5Gk2Nw8QvL@latest
production
18K2h ago
claude-fallback
rt_6Fp1Zr4MxJ
enabled2 members
—
support-ha
2.1K5h ago
Support bot · Bedrock EU
rt_2Lw6Yd8NcV
enabledbedrock / anthropic/claude-sonnet-4.5
support-system
ip_5Gk2Nw8QvL@latest
—1.2K1d ago
Support bot A/B
rt_3Qa8Jk2VnE
enabled2 members
—
—9403d ago
pi-agents
rt_8Mv4Bq6LdY
enabledAnthropic / OpenAI passthrough
—
—6.3K6d ago
Draft classifier
rt_1Xe9Pw3KcU
unconfiguredpick a model
—
——9d ago
Legacy summarizer
rt_4Nd2Rs8TfB
disabledopenai / openai/gpt-5-mini
summarize.md
pr_7Jw3Hc5XmD@3f9c2e1
——5/18/2026
The Routers page: the views rail, filters, 24-hour stats, and each router's type, status, model, prompt, aliases and requests. Select to enlarge.

The Columns picker shows or hides columns. The filter bar filters by status (All, Active or Disabled), by Provider, Model, Tag, Has prompt, Has aliases and Updated, and by a search across name, id, provider, model, tag and alias. Export downloads the visible rows as CSV. The views rail offers pinned views (All routers, With aliases, Recently changed and Disabled), views by router type, saved views, and tags.

The router page

Each router has a page with a header and an Overview tab. The header shows the router's name, status, type, tags, description, id with Copy router ID, and creation and change dates, with Disable or Enable and Delete. The Overview tab shows the Aliases panel, the type's main panel (Model for a static router, Members for a fallback or weighted router, Routing for a Claude Proxy, Forwarding for a Pi Proxy), and the Prompt, Request Logging and Settings panels that apply to the type. See Router Settings.

Enabling and disabling

Disabling a router stops it from serving: requests to it, and through its aliases, return router_disabled, while its configuration is kept. Disabling acts as a kill switch for a model or an application, and enabling the router restores it. A disabled member of a fallback or weighted router is skipped. Routers are disabled and enabled from the router's page, from the Routers table, or with PATCH /v1/routers/{id} and {"enabled": false} or {"enabled": true}.

Deleting a router

Deleting a router removes it permanently. A router must be disabled before it is deleted, must have no aliases pointing to it, and must not be a member of a fallback or weighted router. A router created by a tools policy is enabled, disabled, changed and deleted through the policy, and aliases cannot point to it.

Router API

The router endpoints act on the routers of the API key's organization:

MethodPathDescriptionScope
POST/v1/routersCreate a router. Accepts an Idempotency-Key header and dry_run.routers:write
GET/v1/routersList routers, newest change first, paged with limit (1 to 200, default 50) and cursor, and filtered with kind and tag.routers:read
GET/v1/routers/{id}Get a router by id, with its tags and aliases.routers:read
PATCH/v1/routers/{id}Change a router, including enabling and disabling it with enabled.routers:write
DELETE/v1/routers/{id}Delete a disabled router with no aliases.routers:delete

Scopes are cumulative: routers:delete includes write, and write includes read. An API key with no scopes has full control, and a key without the required scope receives 403 insufficient_scope. Limiting a key to specific routers controls which routers it sends requests through; a key with routers:read lists every router in the organization. The list response holds the routers in data with next_cursor and has_more, and kind accepts static, fallback, weighted, claude_proxy or pi_proxy. Members, aliases and tags have their own endpoints; see Routers in the API Reference. With a kind filter, a page can hold fewer routers than the limit while more remain, so continue until next_cursor is null.

Disable, then delete, a routerbash
curl -X PATCH https://api.multi-router.ai/v1/routers/rt_9Hk3Ws7PzQ \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": false }'

curl -X DELETE https://api.multi-router.ai/v1/routers/rt_9Hk3Ws7PzQ \
  -H "Authorization: Bearer $MR_API_KEY"
Delete response 200json
{ "organization_id": "org_7Tq2Wd9KxP", "id": "rt_9Hk3Ws7PzQ", "object": "router", "deleted": true }

Errors

Managing routers returns the following errors:

ErrorStatusCause
router_not_found404No router with that id exists in the organization.
router_enabled409A router is deleted while it is enabled. Disable it first.
has_dependent_aliases409A router is deleted while it has aliases.
policy_managed409The router was created by a tools policy and is changed through the policy.
version_conflict412The If-Match ETag is stale. The response carries the current router.
insufficient_scope403The API key lacks the router scope the endpoint requires.

Prompts

Overview

Saved, versioned prompts that a router applies to every request it serves.

A prompt in Multi-Router is a saved set of instructions, such as a system prompt or a set of example messages, that is stored once and attached to a router. Every request through that router carries the prompt, so applications send only their own messages and the instructions are changed in one place, with no change to application code or API keys. Each prompt keeps a history of revisions, and a router either follows the latest revision or is pinned to one, so a prompt change is released to a router deliberately and can be rolled back by pinning an earlier revision.

Where prompts live

A prompt's source is the place its content is written and versioned. Multi-Router has two sources, and both produce prompts that bind to routers in the same way:

SourceId prefixWritten inRevisionsChanged by
Inline promptip_The dashboard or the APINumbered 1, 2, 3 and onwardSaving a new revision in the dashboard or the API
GitHub promptpf_A .md or .json file in a connected GitHub repositoryGit commitsPushing a commit to the tracked branch

Inline prompts suit teams that edit prompts in the dashboard. GitHub prompts suit teams that review prompt changes in pull requests and want the repository to stay the source of truth; a GitHub prompt is read-only in Multi-Router and updates when a commit lands on the tracked branch. See Inline Prompt and GitHub Prompt.

Text and chat prompts

A prompt's type decides how its content joins a request. A text prompt is a single block of instructions that becomes the system message. A chat prompt is an ordered list of system, user and assistant messages, such as worked examples, that is placed in front of the caller's conversation. The type of an inline prompt is chosen when it is created; the type of a GitHub prompt follows its file extension, with .md files as text and .json files as chat. See Prompt Type.

Variables

A variable is a named placeholder written as {{customer_name}} that each request fills in with its own value through the prompt_variables field. Variables let one prompt serve many requests that differ only in a few values, such as a customer's name, a product or a language. A request that leaves out a variable the prompt uses is rejected. See Prompt Variables.

Binding a prompt to a router

A prompt binding attaches one prompt to a static router. The binding names the prompt, chooses whether the router follows the latest revision or a pinned revision, and sets Prompt composition, which decides how a text prompt combines with a system message the caller sends. Fallback and weighted routers apply the binding of whichever member serves the request. Claude Proxies and Pi Proxies forward a coding agent's own prompts and carry no binding. See Prompt Binding.

How a request uses a prompt

A request through a router with a bound prompt is handled in this order:

  1. Multi-Router resolves the router from the request, and reads its prompt binding.
  2. It loads the bound revision: the latest revision when the binding tracks latest, or the pinned revision or commit.
  3. It fills each variable with the matching value from prompt_variables, and rejects the request when a value is missing.
  4. It combines the prompt with the caller's messages: a text prompt according to Prompt composition, and a chat prompt placed before the caller's messages.
  5. It sends the combined request to the router's model and provider, and records the prompt and revision in Traffic.

The caller sends only its own messages, and the provider receives the complete request. A change to a prompt that a router tracks at latest applies from the next request, and the router itself stays unchanged.

Example

A support team saves an inline text prompt named support-triage with the content below, and binds it to the static router rt_9Hk3Ws7PzQ tracking latest with Prompt composition set to Merge:

support-triage, revision 3text
You are a support agent for {{product}}. Classify the customer's message as billing,
technical or account, then answer in {{language}}. Keep answers under 120 words.

The application sends only the customer's message and the two variable values:

Requestbash
curl https://api.multi-router.ai/v1/chat/completions \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "multi-router/rt_9Hk3Ws7PzQ",
    "messages": [
      { "role": "user", "content": "I was charged twice for my March invoice." }
    ],
    "prompt_variables": { "product": "Acme Cloud", "language": "English" }
  }'

The provider receives the filled-in prompt as the system message, followed by the customer's message:

Messages sent to the providerjson
[
  { "role": "system", "content": "You are a support agent for Acme Cloud. Classify the customer's message as billing,\ntechnical or account, then answer in English. Keep answers under 120 words." },
  { "role": "user", "content": "I was charged twice for my March invoice." }
]

When the team saves revision 4, the router uses it from the next request. If revision 4 performs worse, pinning the binding to revision 3 restores the earlier prompt for every caller at once.

Prompts in Traffic

Traffic records the prompt each request used, so any response can be traced to the exact prompt content that produced it. The request detail shows a Prompt binding section with the Prompt ID and the Revision; for a GitHub prompt it also shows the Repository and File path, and the revision is the commit that was served, with a link to the file at that commit on GitHub. A binding that tracks latest is recorded with the concrete revision number or commit in use at the time of the request. See Observability.

PageCovers
Inline PromptCreating, editing and versioning prompts in the dashboard and the API.
GitHub PromptConnecting GitHub, tracking files, syncing and file status.
Prompt TypeText and chat prompts, their content and the rules for chat messages.
Prompt VariablesPlaceholder syntax, supplying values and missing-variable errors.
Prompt BindingAttaching a prompt to a router, revisions and Prompt composition.
Managing PromptsThe Prompts page, access, limits and the prompt API.
Router SettingsThe other settings a router applies to each request.

Prompts

Inline Prompt

Prompts written and versioned in Multi-Router, through the dashboard or the API.

An inline prompt is a prompt whose content is stored in Multi-Router. It is created and edited on the Prompts page or with the API, and every saved change becomes a new numbered revision, so the full history of the prompt stays available and a router can be pinned to any revision. Inline prompts have ids that start with ip_, such as ip_6Rt2Kw9QmD.

Fields

An inline prompt has the following fields:

FieldDescription
idThe prompt id, starting with ip_. Assigned by Multi-Router and fixed for the life of the prompt.
nameA display name of up to 80 characters, such as support-triage. Names can be changed at any time.
typetext or chat. Chosen when the prompt is created and fixed afterwards.
bodyThe content of the current revision: a string for a text prompt, or an array of messages for a chat prompt.
variablesThe variable names found in the body, in the order they first appear.
versionThe number of the latest revision.
etagThe prompt's ETag, sent in If-Match to change it safely.
tagsTag names, changed through the tags endpoints.
created_atWhen the prompt was created.
updated_atWhen the prompt was last renamed or given a new revision.

Revisions

A revision is a saved version of a prompt's content. Revisions are numbered from 1 in the order they are saved, and a saved revision is never changed, so revision 3 always has the same content. Each revision records:

FieldDescription
versionThe revision number.
messageAn optional note of up to 200 characters describing the change, such as Fix typo in greeting, entered as the title when saving in the dashboard. The first revision saved in the dashboard is titled Initial.
authorThe person who saved the revision in the dashboard, or the API key that saved it through the API.
created_atWhen the revision was saved.

A change to the content saves a new revision, and a change to the name alone renames the prompt with no new revision. To return to earlier content, open the revision in History, copy its content, and save it as a new revision, or pin the routers that use the prompt to the earlier revision. Through the API, POST /v1/prompts/{id}/versions/{version}/restore saves an earlier revision as a new revision. See Prompt Binding.

Creating in the dashboard

An inline prompt is created on the Prompts page:

  1. Open Prompts in the top navigation, select New Prompt, and choose New inline prompt.
  2. Choose Text or Chat, and enter a name of up to 80 characters. The page warns when another prompt in the organization already has the name.
  3. Write the content. In a text prompt, wrap variables in double curly braces, such as {{product}}. In a chat prompt, add messages with the System, User and Assistant buttons.
  4. Select Save · revision 1.

Editing in the dashboard

Back to Prompts

support-triage

textrev 3
Last edited 3 days ago
EditRenameDelete
supportAdd tag
ContentHistory3
You are a support agent for {{product}}.

Classify the customer's message as billing, technical or account, then answer in {{language}}.
Keep answers under 120 words and link to the help center when an article covers the question.
Variables
Auto-detected from body. Provide values via prompt_variables at request time.
{{product}}
{{language}}
An inline text prompt at revision 3, with its Content and History tabs and the variables found in its content. Select to enlarge.

The prompt's page shows its name, a type pill such as text or chat · 4 msgs, the current revision such as rev 3, and when it was last edited. It has two tabs and a side panel:

AreaContents
ContentThe content of the current revision. Edit opens the editor.
HistoryEvery revision with its number, title, author and date. Selecting a revision shows its content.
VariablesThe variables found in the content, or No variables detected.

Edit opens the editor with an optional revision title, and Save changes saves the content as the next revision. Rename changes the name with no new revision. The chat editor shows one card per message: messages are added with the System, User and Assistant buttons, reordered by dragging, and removed after a confirmation. Edit raw JSON switches to the message array as JSON, and Back to structured editor returns to the cards. A Structural issue banner describes any message order that breaks the chat rules, and the prompt saves once the issue is fixed. See Prompt Type.

Managing with the API

Inline prompts are created with POST /v1/prompts. An optional Idempotency-Key header makes a retried request create the prompt once. A text body is a JSON string, and a chat body is a JSON array of messages:

Create a text promptbash
curl -X POST https://api.multi-router.ai/v1/prompts \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "text",
    "name": "support-triage",
    "body": "You are a support agent for {{product}}. Answer in {{language}}."
  }'
Response 201json
{
  "organization_id": "org_7Tq2Wd9KxP",
  "data": {
    "id": "ip_6Rt2Kw9QmD",
    "object": "prompt",
    "etag": "\"1\"",
    "source": "inline",
    "name": "support-triage",
    "type": "text",
    "body": "You are a support agent for {{product}}. Answer in {{language}}.",
    "variables": ["product", "language"],
    "version": 1,
    "tags": [],
    "created_at": "2026-09-14T17:02:11Z",
    "updated_at": "2026-09-14T17:02:11Z"
  }
}

PATCH /v1/prompts/{id} changes the name or body sent. A changed body saves a new revision, a changed name alone renames the prompt, and the type is fixed. An If-Match header with the prompt's ETag refuses the change with 412 if another change landed first.

Save revision 2bash
curl -X PATCH https://api.multi-router.ai/v1/prompts/ip_6Rt2Kw9QmD \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H 'If-Match: "1"' \
  -H "Content-Type: application/json" \
  -d '{
    "body": "You are a support agent for {{product}}. Classify the message, then answer in {{language}}."
  }'

The revision history is read and restored with these endpoints:

EndpointReturns
GET /v1/prompts/{id}/versionsThe revisions, newest first, each with version, author, message and created_at.
GET /v1/prompts/{id}/versions/{version}One revision, including its body.
POST /v1/prompts/{id}/versions/{version}/restoreMakes an earlier revision current by saving it as a new revision. Takes no body.

Deleting

Deleting an inline prompt removes the prompt and all of its revisions. A prompt is deleted only when no router is bound to it; while routers use it, the delete is refused with prompt_in_use and the routers are listed, so they can be bound to another prompt or unbound first. In the dashboard, Delete asks for confirmation before removing the prompt. With the API, the prompt is deleted with DELETE /v1/prompts/{id}:

Delete response 200json
{ "organization_id": "org_7Tq2Wd9KxP", "id": "ip_6Rt2Kw9QmD", "object": "prompt", "deleted": true }

Errors

Creating, editing and deleting inline prompts return the following errors:

ErrorStatusCause
idempotency_key_reused409The Idempotency-Key was used before with a different request.
idempotency_in_progress409A request with the same Idempotency-Key is still being processed.
missing_field400The type, name or body is missing.
invalid_field400A field has the wrong shape, the name or content is over its limit, or a chat body breaks the chat rules.
invalid_json400The request body is not valid JSON.
unknown_field400An edit sends a field prompts do not have, such as description.
field_read_only400An edit sends a read-only field, such as id, type or version. current_version and variables return unknown_field.
use_sub_collection400An edit sends tags. Tags change through /v1/prompts/{id}/tags. The valid field of the error lists that path template; {id} is not replaced with the object id.
version_conflict412The If-Match ETag is stale. The response carries the current prompt.
revision_conflict409Another change saved a revision at the same moment. Read the prompt and retry.
prompt_not_found404No prompt with that id exists in the organization.
revision_not_found404The prompt has no revision with that number.
prompt_in_use409A delete names a prompt that routers are bound to. The message lists the routers.
source_read_only409An edit or delete names a GitHub prompt, which changes only through its repository.

Prompts

GitHub Prompt

Prompts kept as files in a GitHub repository and synced to Multi-Router on every push.

A GitHub prompt is a .md or .json file in a GitHub repository that Multi-Router tracks. The repository stays the source of truth: prompt changes go through the team's usual review in pull requests, and when a commit that changes a tracked file lands on the tracked branch, Multi-Router picks up the new content. GitHub prompts are read-only in Multi-Router, their revisions are commits, and a router can be pinned to any commit of the file.

A repository is connected to Multi-Router through a GitHub connection and a prompt repo:

ObjectIdDescription
GitHub connectionThe GitHub loginA GitHub organization or personal account where the Multi-Router GitHub App is installed and claimed by the Multi-Router organization.
Prompt repopr_One repository and branch tracked as a prompt source, such as example-co/prompts on main.
GitHub promptpf_One tracked file in a prompt repo, such as prompts/support-triage.md.

Connecting a GitHub organization

A GitHub connection gives Multi-Router read access to the repositories the Multi-Router GitHub App is installed on. Connecting and disconnecting a GitHub organization requires an organization admin, and is done in the dashboard:

  1. On the Prompts page, select New Prompt and choose Connect new GitHub org.
  2. On GitHub, choose the organization or personal account, choose which repositories the App can read, and approve the install.
  3. GitHub returns to the Prompts page, which claims the installation for the organization and opens the form to connect a repository.

Manage GitHub orgs on the same menu lists the connected organizations, with Connect, Disconnect, Authorize GitHub and Re-authorize. The panel shows which GitHub user it is listing installations for, and an installation is claimed by an admin who has used Authorize GitHub and can see the installation on GitHub. A GitHub installation can be claimed by more than one Multi-Router organization. Disconnecting a GitHub organization stops its prompt repos from syncing: routers that track latest keep the content last synced, and routers pinned to an earlier commit return an error until the organization is connected again.

Connecting a repository

A prompt repo tracks one branch of one repository. In the dashboard:

  1. Select New Prompt and choose Connect repo from the connected GitHub organization.
  2. Search for and choose the repository, then choose the branch.
  3. Choose the .md and .json files to track, and connect.

Multi-Router fetches each chosen file at once, and each file appears on the Prompts page as a GitHub prompt. A repository and branch are connected once per organization; to track another branch of the same repository, connect it as a second prompt repo. Through the API, a prompt repo is connected with POST /v1/prompt-repos. Connect the repository's GitHub organization in the dashboard first; until it is connected, each file of the prompt repo shows the status error with the message github account not connected.

Connect a prompt repobash
curl -X POST https://api.multi-router.ai/v1/prompt-repos \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Support prompts",
    "repository": "example-co/prompts",
    "branch": "main",
    "file_paths": ["prompts/support-triage.md", "prompts/refund-examples.json"]
  }'
Response 201json
{
  "organization_id": "org_7Tq2Wd9KxP",
  "data": {
    "id": "pr_Xb5Nq2Lw8T",
    "object": "prompt_repo",
    "etag": "\"1\"",
    "name": "Support prompts",
    "repository": "example-co/prompts",
    "branch": "main",
    "sync": { "status": "synced", "last_synced_at": "2026-09-14T17:20:04Z" },
    "tags": [],
    "created_at": "2026-09-14T17:20:03Z",
    "updated_at": "2026-09-14T17:20:04Z"
  }
}

Tracked files

A tracked file is a path in the prompt repo that Multi-Router keeps in sync. A file path is relative to the repository root, up to 512 characters, ends in .md or .json in any letter case, and starts with a file or folder name, with every segment other than . or ... A .md file is a text prompt whose content is the file's text, and a .json file is a chat prompt whose content is a JSON array of messages. See Prompt Type.

On the prompt's page, Add files opens a list of the branch's files with the tracked files checked and locked, and Stop tracking removes the file from Multi-Router while leaving it in the repository. Through the API, POST /v1/prompt-repos/{id}/files with a path tracks one file, and DELETE /v1/prompt-repos/{id}/files?path= stops tracking it. GET /v1/prompt-repos/{id}/files lists the tracked files with their prompt ids and sync status. A file that routers are bound to stays tracked until those routers are unbound, and the request is refused with prompt_in_use.

Syncing

Syncing is how a commit on GitHub reaches Multi-Router. When a push lands on a tracked branch, GitHub notifies Multi-Router, which fetches each tracked file the push added, changed or removed, in every organization and prompt repo that tracks that branch. Files the push did not touch keep their current content. A router that tracks latest uses the new content from its next request, usually within seconds of the push.

A file is fetched when it is first tracked, when it is added again, and on each push that changes it. Each tracked file has a status:

StatusMeaning
pendingThe file is tracked and its first fetch has not finished.
syncedThe file was fetched and its content is current with the branch.
errorThe last fetch failed. The error message says why.

When a fetch fails for a temporary reason, such as a GitHub outage or rate limit, the file keeps serving the content from its last successful fetch until the next push syncs it. When the file has been removed from the branch, or its content is not a valid prompt, routers bound to it return an error until the file is fixed and pushed again. The error message is one of:

Error messageCause
removed upstreamA push deleted the file from the tracked branch.
invalid chat body: …A .json file is not a valid chat prompt. The message describes the problem.
github account not connectedThe repository's GitHub organization is no longer connected.
github: unauthorizedThe GitHub App no longer has access to the repository.
github: not foundThe repository, branch or file was not found.
github: rate limitedGitHub limited the number of requests.
github: server errorGitHub returned a server error.
github: timed outGitHub did not respond in time.
github: response too largeThe file is larger than the prompt size limit.
github: network errorGitHub could not be reached.
github: api errorGitHub returned an unexpected error.
github: malformed responseGitHub returned a response Multi-Router could not read.
github: credentials misconfiguredThe GitHub integration for this Multi-Router installation is set up incorrectly.

Revisions

The revisions of a GitHub prompt are the commits on the tracked branch that changed the file, newest first, each with its commit SHA, commit message and date. The prompt's History tab lists them, and a router can be pinned to any of them by its SHA, written in full or shortened to at least 7 characters. The API reads them with GET /v1/prompts/{id}/versions and GET /v1/prompts/{id}/versions/{sha}, which read the history from GitHub when called, and return up to the 100 most recent revisions. Reading a single commit returns its version and body. The commits of the whole repository, or of one tracked file, are listed with GET /v1/prompt-repos/{id}/commits. When GitHub cannot be read, the revision endpoints return 502 with github_unavailable, when the GitHub organization is not connected, or github_error, when the fetch fails.

The GitHub prompt page

Back to Prompts

example-co/prompts

syncedmain
3 files tracked · last sync 3 days ago
Disconnect
supportproductionAdd tag
prompts/support-triage.md
synced3f9c2a7
Add filesStop tracking
ContentHistory
You are a support agent for {{product}}.

Classify the customer's message as billing, technical or account, then answer in {{language}}.
Keep answers under 120 words and link to the help center when an article covers the question.

Read-only — push to GitHub to update.

Variables
Auto-detected from body. Provide values via prompt_variables at request time.
{{product}}
{{language}}
A tracked GitHub file: its repository and branch, the file switcher, sync status and commit, and its read-only content. Select to enlarge.

A GitHub prompt's page shows the file's content with a note that it is read-only and changes by pushing to GitHub, and a History tab with its commits. A file switcher moves between the files of the same prompt repo. The page offers Add files, Stop tracking and Disconnect, which disconnects the whole prompt repo.

Disconnecting a repository

Disconnecting a prompt repo stops tracking all of its files, and their GitHub prompts are removed from Multi-Router. The repository on GitHub is unchanged. A prompt repo with files that routers are bound to stays connected until those routers are unbound, and the request is refused with prompt_in_use. Through the API, a prompt repo is disconnected with DELETE /v1/prompt-repos/{id}, which returns the number of files removed:

Delete response 200json
{ "organization_id": "org_7Tq2Wd9KxP", "id": "pr_Xb5Nq2Lw8T", "object": "prompt_repo", "deleted": true, "files_deleted": 2 }

GitHub connections in the API

GitHub connections are read and removed through the API, and created in the dashboard because installing the GitHub App takes place on GitHub.

EndpointDescription
GET /v1/github/connectionsLists the connected GitHub organizations, each with its login, installation_id and connected_at.
GET /v1/github/connections/{login}Reads one connection.
DELETE /v1/github/connections/{login}Disconnects a GitHub organization. Its prompt repos stay and stop syncing. Accepts dry_run.

Errors

Connecting repositories and tracking files return the following errors:

ErrorStatusCause
prompt_repo_exists409The organization already tracks this repository and branch.
invalid_field400A file path, repository name or branch is not valid.
prompt_repo_not_found404No prompt repo with that id exists in the organization.
file_not_found404The prompt repo does not track that file.
github_not_connected424The repository's GitHub organization is not connected.
github_repo_not_found404The connected organization cannot find the repository or branch.
connection_not_found404No connected GitHub organization has that login.
prompt_in_use409Routers are bound to a file being untracked or disconnected. The message lists the routers.
source_read_only409An edit or delete names a GitHub prompt through the prompt endpoints.
rate_limited429The organization made more than 60 requests in a minute that check GitHub.
invalid_body400A required field is missing, or the body has the wrong shape.
invalid_json400The request body is not valid JSON.
version_conflict412The If-Match ETag is stale. The response carries the current prompt repo.
use_sub_collection400A PATCH sends files or tags, which change through their own endpoints. The valid field of the error lists the endpoint path template, such as /v1/prompt-repos/{id}/files; {id} is not replaced with the object id.
github_not_configured503The GitHub integration is not set up for this Multi-Router installation.

Prompts

Prompt Type

Text and chat prompts: what each contains and how it joins a request.

Every prompt is either a text prompt or a chat prompt. The type decides what the prompt's content looks like and how the prompt combines with the caller's messages. A text prompt supplies the system message. A chat prompt supplies a short conversation, such as instructions followed by worked examples, that comes before the caller's messages.

Text promptChat prompt
ContentOne block of textAn array of system, user and assistant messages
Inline prompttype set to texttype set to chat
GitHub promptA .md fileA .json file
Joins the request asThe system messageMessages placed before the caller's messages
Prompt compositionAppliesNot applicable

Text prompts

A text prompt is a single block of instructions, written as plain text or Markdown, that becomes the system message of each request. Through the API its body is a JSON string; in a GitHub repository it is the full text of a .md file. When the caller also sends a system message, Prompt composition on the router decides whether the two are merged, the router's prompt replaces the caller's, or the caller's is used in place of the router's. See Prompt Binding.

prompts/support-triage.mdmarkdown
You are a support agent for {{product}}.

Classify the customer's message as billing, technical or account, then answer in {{language}}.
Keep answers under 120 words and link to the help center when an article covers the question.

Chat prompts

A chat prompt is an ordered array of messages, each with a role and content. It is used to give the model instructions together with example exchanges that show the expected answers, a technique known as few-shot prompting. Each request places the chat prompt's messages in front of the caller's messages. The caller's own system message, when it sends one, is replaced by the chat prompt.

prompts/refund-examples.jsonjson
[
  { "role": "system", "content": "Decide whether a refund request qualifies under the {{plan}} plan policy. Reply with APPROVE or DECLINE and one sentence." },
  { "role": "user", "content": "I cancelled 3 days after renewal and never used the service." },
  { "role": "assistant", "content": "APPROVE. Cancellations within 14 days of renewal with no usage qualify." },
  { "role": "user", "content": "I used the service all month and want a refund for the last week." },
  { "role": "assistant", "content": "DECLINE. Partial months of active use do not qualify." }
]

A request through a router bound to this prompt sends one new customer message:

Messages sent to the providerjson
[
  { "role": "system", "content": "Decide whether a refund request qualifies under the Business plan policy. Reply with APPROVE or DECLINE and one sentence." },
  { "role": "user", "content": "I cancelled 3 days after renewal and never used the service." },
  { "role": "assistant", "content": "APPROVE. Cancellations within 14 days of renewal with no usage qualify." },
  { "role": "user", "content": "I used the service all month and want a refund for the last week." },
  { "role": "assistant", "content": "DECLINE. Partial months of active use do not qualify." },
  { "role": "user", "content": "I was billed for a seat I removed last week." }
]

Chat rules

A chat prompt must form a valid start of a conversation, so that any caller message can follow it. The dashboard, the API and GitHub syncing all check these rules, and a prompt that breaks one is refused when it is saved or synced:

RuleDetail
RolesEach message has the role system, user or assistant, in any letter case.
System messageAt most one system message, and only as the first message.
First conversational messageThe first message after the system message has the role user.
AlternationUser and assistant messages alternate after that.
ContentEvery message has content with at least one character other than spaces.
LengthAt least 1 and at most 64 messages.
SizeAt most 128 KiB for an inline prompt, and 256 KiB for a GitHub file.

A chat prompt can consist of a single system message. Message content is either a string or an array of content parts, in the same shape as Chat Completions messages.

Choosing a type

A text prompt fits instructions that every request needs, and works with callers that send their own system message through Prompt composition. A chat prompt fits behavior that is easier to show than to describe, such as a classification format or a tone, and it sets the system message for every caller. The type of an inline prompt is fixed when it is created; to change type, create a new prompt and bind routers to it.

Prompts

Prompt Variables

Placeholders in a prompt that each request fills in with its own values.

A variable is a named placeholder in a prompt, written in double curly braces as {{customer_name}}, that each request fills in with its own value. Variables let one prompt serve every request that differs only in a few details, such as the customer, product, plan or language, while the prompt itself stays in one place. Multi-Router lists the variables of every prompt on its page and in the API's variables field.

Syntax

A variable is a name between {{ and }}. A name starts with a letter or underscore, continues with letters, digits and underscores, and is case sensitive. Spaces inside the braces are allowed, so {{ product }} and {{product}} are the same variable. A variable used several times in a prompt takes the same value everywhere.

Written in the promptResult
{{product}}The variable product.
{{ product }}The variable product.
{{customer_name}}The variable customer_name.
{{_region2}}The variable _region2.
{{2nd_item}}Kept as written. A name cannot start with a digit.
{{order-id}}Kept as written. A name cannot contain a hyphen.
{{ product }Kept as written. The braces are not balanced.

Text that is not a valid variable stays in the prompt exactly as written, which lets a prompt include literal double braces, such as a template example, by writing them in a form that is not a valid name.

Supplying values

A request supplies variable values in the top-level prompt_variables object, which maps each variable name to a string. The field is accepted on Chat Completions and on Messages:

Chat Completionsbash
curl https://api.multi-router.ai/v1/chat/completions \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "multi-router/rt_9Hk3Ws7PzQ",
    "messages": [{ "role": "user", "content": "How do I reset my password?" }],
    "prompt_variables": { "product": "Acme Cloud", "language": "Spanish" }
  }'
Messagesbash
curl https://api.multi-router.ai/v1/messages \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "multi-router/rt_9Hk3Ws7PzQ",
    "max_tokens": 1024,
    "messages": [{ "role": "user", "content": "How do I reset my password?" }],
    "prompt_variables": { "product": "Acme Cloud", "language": "Spanish" }
  }'

Every value is a string; a number or boolean makes the request invalid. The values follow these rules:

CaseBehavior
A value for every variableEach placeholder is replaced with its value.
A variable with no valueThe request is rejected with missing_prompt_variables.
An empty stringAccepted. The placeholder is replaced with nothing.
A name the prompt does not useIgnored.
A value containing double bracesInserted as written. Values are not scanned for further variables.
No prompt bound to the routerprompt_variables is ignored.

Variables in chat prompts

In a chat prompt, variables are filled in throughout each message: in its content, including every text part, and in the message's name and tool call fields when the prompt includes them. One prompt_variables object supplies the values for every message in the prompt.

Missing variables

A request that omits a variable the bound prompt uses is rejected before it reaches the provider, with status 400 and the error missing_prompt_variables. The message lists every missing variable. For a chat prompt, each name is shown with the message it appears in, written as message[index:role].field:name:

Response 400json
{
  "error": {
    "type": "invalid_request_error",
    "code": "missing_prompt_variables",
    "message": "router's bound prompt requires these variables that the request omits: language"
  }
}

On Messages, the error has the Anthropic error shape, with the type invalid_request_error and the same message.

A router that tracks the latest revision uses the variables of whatever revision is current, so adding a variable to a prompt requires every caller to send it. To roll out a new variable safely, update the callers to send it first, since extra values are ignored, and then save the revision that uses it.

Prompts

Prompt Binding

Attaching a prompt to a router, choosing its revision, and composing it with the caller's messages.

A prompt binding attaches one prompt to a static router, so every request through the router carries that prompt. The binding names the prompt, chooses whether the router follows the prompt's latest revision or a pinned revision, and works together with Prompt composition, the router setting that decides how a text prompt combines with a system message the caller sends. A router has at most one binding, and one prompt can be bound to many routers.

Router types

Prompt bindings belong to static routers. Each router type uses them as follows:

Router typePrompt binding
Static routerHas its own binding.
Fallback routerUses the binding of the member that serves the request.
Weighted routerUses the binding of the member that serves the request.
Claude ProxyHas no binding. The coding agent's own prompts are forwarded as sent.
Pi ProxyHas no binding. The coding agent's own prompts are forwarded as sent.

A request through an alias uses the binding of the router the alias points to. Because fallback and weighted routers use their members' bindings, members can carry different prompts, for example a prompt tuned for each model. When a fallback member's prompt cannot be loaded, the fallback router moves on to the next member.

Latest or pinned

The revision mode decides which version of the prompt the router uses. Tracking latest suits prompts under active development; pinning suits production routers, where a prompt change is released by moving the pin.

ModeValueDashboard labelRouter uses
LatestlatestTrack latestThe newest revision of an inline prompt, or the newest synced commit of a GitHub prompt. A new revision applies from the next request.
PinnedpinnedPin to a specific revisionOne revision number of an inline prompt, or one commit SHA of a GitHub prompt, until the pin is changed.

A common release flow is to keep a staging router on latest and the production router pinned: a new revision is tested through the staging router, and the production router's pin is then moved to it. Pinning back to the previous revision rolls the change back.

Prompt composition

Prompt composition is the router setting that decides how a bound text prompt combines with a system message the caller sends. When the caller sends no system message, the router's prompt is used in every mode.

ModeValueDashboard descriptionBehavior
MergemergeBoth router and caller system apply (default)The router's prompt comes first, followed by the caller's system message.
OverrideoverrideOnly the router's prompt applies; caller's system is droppedThe router's prompt replaces the caller's system message.
FallbackfallbackCaller's system wins when present; router's is the defaultThe caller's system message is used, and the router's prompt applies only when the caller sends none.

Merge suits a router that sets shared rules, such as tone or safety instructions, while callers add their own context. Override suits a router where the prompt must be exactly what is reviewed, regardless of callers. Fallback suits a router that supplies a sensible default for callers that have no prompt of their own.

On each endpoint

Prompt composition applies the same way on both endpoints, with one difference in how the result is shaped:

EndpointCaller system messageMerge result
POST /v1/chat/completionsThe first message, when its role is system.One system message: the router's prompt, a blank line, then the caller's system message.
POST /v1/messagesThe system field.The router's prompt as the first system block, followed by the caller's blocks unchanged.

On Messages, the caller's system blocks keep their own settings, including prompt caching marks, so a caller's cached system prompt stays cached when a router prompt is added in front of it.

Chat prompts

A bound chat prompt is placed in front of the caller's messages, and the caller's system message is replaced by the chat prompt. Prompt composition applies only to text prompts, and the Prompt panel shows Not applicable in its place when a chat prompt is bound. See Prompt Type.

Binding in the dashboard

Back to Routers

Support bot

enabledstatic
productionsupportAdd tag

Answers customer questions in the help center chat widget.

rt_9Hk3Ws7PzQ·created Mar 4, 2026·updated Sep 30, 2026
Disable
OverviewTrafficActivity

Aliases

Add
production

Model

Edit
Provideranthropic
Modelanthropic/claude-sonnet-4.5
Variantstandard
Provider keyAnthropic productionllpk_8Rn3Kx5WbT
Model source
Use request modelUse the caller's model with this router's provider, credential & preset

Prompt

Edit
SourceInline
Promptsupport-triage(ip_6Rt2Kw9QmD)
Revision@3(pinned)
Prompt composition
MergeBoth router and caller system apply (default)

Request Logging

What this router records about its requests. Capped by the administrator's Storage Policy.

Edit
Request metadataFull Metadata
Request payloadsFailures only

Settings

Per-router configuration. Persisted in the router's versioned config blob.

A static router's Prompt panel with an inline prompt pinned to revision 3 and Prompt composition set to Merge. Select to enlarge.

The Prompt panel on a static router's page shows the binding's Source, Prompt, Revision and Prompt composition, or No prompt bound when the router has none, in which case the router forwards the caller's system message unchanged. To bind a prompt:

  1. On the router's page, select Bind in the Prompt panel, or Edit to change an existing binding.
  2. In 1. Source, choose Inline or a connected GitHub prompt repo.
  3. In 2. Prompt, choose the prompt or file.
  4. In 3. Revision, choose Track latest or Pin to a specific revision, and select Bind. Pinned revisions are listed by number with their title, author and date, or by short commit SHA.
  5. For a text prompt, choose Merge, Override or Fallback in the Prompt composition dropdown of the Prompt panel. The choice saves on its own.
Back to Routers

Support bot

enabledstatic
productionsupportAdd tag

Answers customer questions in the help center chat widget.

rt_9Hk3Ws7PzQ·created Mar 4, 2026·updated Sep 30, 2026
Disable
OverviewTrafficActivity

Bind a prompt

Pick where the prompt lives, then drill in.

Unbind Cancel
1. SourceInlinesupport-triage
Revision
BindBack
The Bind a prompt editor at the revision step, pinning support-triage to revision 3. Select to enlarge.

Unbind removes the binding. When creating a static router, the Bound prompt field on the New Router page binds an inline prompt at its latest revision; the binding can be changed on the router's page afterwards.

Binding with the API

A binding is set with the prompt.binding field when a static router is created with POST /v1/routers or changed with PATCH /v1/routers/{id}, and Prompt composition is prompt.system_message_mode. A change that leaves out prompt.binding keeps the existing binding, and "prompt": { "binding": null } removes it. The routers bound to a prompt are listed with GET /v1/prompts/{id}/routers.

FieldValue
sourceinline or github
Where the prompt lives.
prompt_idAn ip_ id
The inline prompt. Used when source is inline.
prompt_repo_idA pr_ id
The prompt repo. Used when source is github.
file_pathA tracked file path
The file in the prompt repo, such as prompts/support-triage.md. Used when source is github.
version_modelatest or pinned
Whether the router follows the newest revision or a pinned one.
pinned_valueA revision number or commit SHA
The pinned revision. A positive whole number for an inline prompt, or a commit SHA of 7 to 40 hexadecimal characters for a GitHub prompt. Used when version_mode is pinned.
Bind an inline prompt pinned to revision 3bash
curl -X PATCH https://api.multi-router.ai/v1/routers/rt_9Hk3Ws7PzQ \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": {
      "binding": {
        "source": "inline",
        "prompt_id": "ip_6Rt2Kw9QmD",
        "version_mode": "pinned",
        "pinned_value": "3"
      },
      "system_message_mode": "merge"
    }
  }'
GitHub prompt binding tracking latestjson
{
  "prompt": {
    "binding": {
      "source": "github",
      "prompt_repo_id": "pr_Xb5Nq2Lw8T",
      "file_path": "prompts/support-triage.md",
      "version_mode": "latest"
    }
  }
}

Multi-Router checks the binding when it is saved: the prompt or tracked file must exist in the organization, and a pinned revision or commit must exist for that prompt.

Errors

Saving a binding returns the following errors:

ErrorStatusCause
prompt_not_found400The prompt, prompt repo or tracked file does not exist in the organization.
invalid_pinned_value400The pinned value is not a positive whole number for an inline prompt, or not a 7 to 40 character commit SHA for a GitHub prompt.
pinned_value_not_found400The prompt has no such revision, or the file does not exist at that commit.
github_not_connected400The prompt repo's GitHub organization is no longer connected.
field_not_supported_for_kind400A binding is set on a router type that has none, such as a Claude Proxy.
unknown_field400The binding uses rev_mode; the API takes version_mode.
prompt_validation_rate_limited429The organization made more than 60 requests in a minute that check GitHub. See Limits on Managing Prompts.
prompt_upstream502GitHub could not be reached to check a commit.
github_not_configured503The GitHub integration is not set up for this Multi-Router installation.

Requests through a router with a binding return the following errors. Apart from missing_prompt_variables, they have the error type upstream_error and are identified by their message:

StatusError or messageCause
400missing_prompt_variablesThe request leaves out a variable the prompt uses. See Prompt Variables.
502this router's bound prompt is no longer availableThe router's prompt was deleted, or its GitHub file was removed from the branch or failed to sync.
502this router's bound chat prompt is malformedThe chat prompt breaks the chat rules.
429github rate limited; retry after NsGitHub limited requests while Multi-Router read a pinned commit from GitHub.
503github integration not configured on this serverA GitHub prompt is bound and the GitHub integration is not set up for this Multi-Router installation.

Prompts

Managing Prompts

The Prompts page, who can change prompts, limits, and the prompt API.

Prompts are managed on the Prompts page in the dashboard and through the prompt endpoints of the API. The Prompts page lists every inline and GitHub prompt in the organization, shows which routers use each one, and flags GitHub files that failed to sync.

The Prompts page

Engineering · org_7Tq2Wd9KxP

All prompts· Inline & GitHub Managed

Save viewNew Prompt
Source:
AllInlineGitHub
Filters
Search name, path…
Prompts
7
Inline
4
GitHub
3
Bound routers
4
Unused
3
Errored
1
Prompts
1–7 of 725 / page
Columns
Updated
NameStatusTagsBound toUpdated
escalation-policy.md
example-co/prompts·main·4e8a1c2
synced
supportAdd tag
Escalations
rt_3Pn8Tc6WdF
25 min ago
refund-examples.json
example-co/prompts·main·9b3d7f0
synced
supportAdd tag
unbound25 min ago
support-triage
ip_6Rt2Kw9QmD
rev 3
supportAdd tag
Support bot
rt_9Hk3Ws7PzQ
3 days ago
refund-classifier
ip_8Jd4Fv1XnB
rev 2
supportbillingAdd tag
Refunds
rt_8Mc5Jx3VbN
5 days ago
sql-helper
ip_3Ys7Gp5MwH
rev 4
analyticsAdd tag
Analytics
rt_7Vb4Qm2HsK
8 days ago
legacy-faq.md
example-co/prompts·main·1c5e9a4
error
supportAdd tag
unbound12 days ago
onboarding-email
ip_5Hc9Lr2TzA
rev 1
marketingAdd tag
unbound21 days ago
The Prompts page: views, stat tiles, and inline and GitHub prompts with their status, tags and the routers they are bound to. Select to enlarge.

The Prompts page opens from Prompts in the top navigation. Tiles across the top count the organization's prompts under Prompts, Inline, GitHub, Bound routers, Unused and Errored, where Bound routers counts the prompts bound to at least one router. The table lists one prompt per row with these columns, which the Columns menu shows or hides:

ColumnShows
NameThe prompt name, or the file name of a GitHub prompt with its repository and path on hover.
Statussynced, pending or error for a GitHub prompt.
TagsThe labels on the prompt. A GitHub prompt shows its prompt repo's tags.
Bound toThe routers the prompt is bound to.
UpdatedWhen the prompt last changed.

The side rail offers built-in views, and any filtered list can be saved under My views with Save view:

ViewLists
All promptsEvery prompt in the organization.
InlineInline prompts.
GitHub ManagedGitHub prompts.
Recently changedPrompts changed in the last 7 days.
Sync errorsGitHub prompts whose last fetch failed.
UnusedPrompts bound to no router.

The list can be filtered further:

FilterValues
SourceAll, Inline or GitHub.
StatusAny, Synced, Pending or Error.
Bound toAny, A router or Unbound.
GitHub repositoryOne connected prompt repo.
TagsOne or more tags.

The New Prompt menu creates an inline prompt, connects a repository from a connected GitHub organization, connects a new GitHub organization, and opens Manage GitHub orgs. See Inline Prompt and GitHub Prompt.

Access

Every member of an organization can create, edit and delete inline prompts, connect and disconnect prompt repos, and track files. Connecting and disconnecting a GitHub organization requires an organization admin, because it grants Multi-Router access to that organization's repositories. Binding a prompt to a router is part of changing the router. API keys reach prompts through the scopes below.

ScopeAllows
prompts:readListing and reading prompts and their revisions.
prompts:writeCreating and editing inline prompts.
prompts:deleteDeleting inline prompts.
prompt_repos:readListing and reading prompt repos and their files.
prompt_repos:writeConnecting prompt repos and tracking files.
prompt_repos:deleteDisconnecting prompt repos and untracking files.
github:readListing GitHub connections.
github:deleteDisconnecting GitHub organizations.

Limits

Prompts and prompt repos have the following limits:

ItemLimit
Prompt name80 characters
Revision title200 characters
Inline prompt content, text or chat128 KiB
Chat prompt messages64
GitHub prompt file256 KiB
Revisions returned by the APIThe 100 most recent
Requests that check GitHub, per organization60 per minute
Prompt repo name80 characters
Prompt repos per organization200
Tracked files per prompt repo1,000
File paths per request100
File path512 characters
Branch name250 characters

Prompt API

The prompt endpoints cover inline prompts, GitHub prompts, prompt repos and GitHub connections:

EndpointDescription
GET /v1/promptsLists prompts.
POST /v1/promptsCreates an inline prompt.
GET /v1/prompts/{id}Reads one prompt, including its body and variables.
PATCH /v1/prompts/{id}Changes an inline prompt, saving a new revision when the body changes.
DELETE /v1/prompts/{id}Deletes an inline prompt and its revisions.
GET /v1/prompts/{id}/versionsLists revisions or commits, newest first.
GET /v1/prompts/{id}/versions/{version}Reads one revision or commit, including its body.
POST /v1/prompts/{id}/versions/{version}/restoreMakes an earlier inline revision current.
GET /v1/prompts/{id}/routersLists the routers bound to the prompt.
POST /v1/prompt-reposConnects a repository and branch, and tracks files.
GET /v1/prompt-reposLists prompt repos.
GET /v1/prompt-repos/{id}Reads one prompt repo.
PATCH /v1/prompt-repos/{id}Renames a prompt repo.
DELETE /v1/prompt-repos/{id}Disconnects a prompt repo.
GET /v1/prompt-repos/{id}/filesLists tracked files.
POST /v1/prompt-repos/{id}/filesTracks one more file.
DELETE /v1/prompt-repos/{id}/filesStops tracking the file named in the path query parameter.
GET /v1/prompt-repos/{id}/commitsLists the branch's commits, optionally for one file.
GET /v1/github/connectionsLists connected GitHub organizations.
GET /v1/github/connections/{login}Reads one GitHub connection.
DELETE /v1/github/connections/{login}Disconnects a GitHub organization.

Prompts, prompt repos and model groups also have tags endpoints. See Prompts in the API Reference for scopes, conditional writes and errors. GET /v1/prompts returns a page of prompts in data, with next_cursor and has_more for paging. It accepts these query parameters:

ParameterDescription
sourceinline or github.
typetext or chat.
prompt_repo_idOnly the GitHub prompts of one prompt repo.
limitPrompts per page, from 1 to 200. The default is 50.
cursorThe next_cursor of the previous page.

A GitHub prompt read with GET /v1/prompts/{id} carries its repository details:

GET /v1/prompts/pf_cHJfWGI1TnEyTHc4VC9wcm9tcHRzL3N1cHBvcnQtdHJpYWdlLm1kjson
{
  "organization_id": "org_7Tq2Wd9KxP",
  "data": {
    "id": "pf_cHJfWGI1TnEyTHc4VC9wcm9tcHRzL3N1cHBvcnQtdHJpYWdlLm1k",
    "object": "prompt",
    "source": "github",
    "name": "prompts/support-triage.md",
    "type": "text",
    "body": "You are a support agent for {{product}}.\n\nClassify the customer's message as billing, technical or account, then answer in {{language}}.",
    "variables": ["product", "language"],
    "version": "3f9c2a7e1b4d8065c2e9a17f0b3d5c8e6a4f2b19",
    "github": {
      "prompt_repo_id": "pr_Xb5Nq2Lw8T",
      "repo": "example-co/prompts",
      "branch": "main",
      "file_path": "prompts/support-triage.md",
      "current_sha": "3f9c2a7e1b4d8065c2e9a17f0b3d5c8e6a4f2b19",
      "status": "synced"
    }
  }
}

Prompts in Traffic

Traffic records the prompt and revision each request used. See Prompts in Traffic on the overview and Observability.

Agents & Editors

Overview

How Claude Code, Claude Desktop and Pi.Dev connect to Multi-Router, and what an organization controls once they do.

Agents and editors are the AI tools people use directly, such as a coding agent in a terminal or a desktop chat app. Each tool connects to Multi-Router through a router built for it: a Claude Proxy for Claude Code and Claude Desktop, and a Pi Proxy for Pi.Dev. The tool keeps working the way its users expect, in its own request format, while the organization decides which models it can use, which credentials pay for it, how much it can spend, and what Traffic records about it.

Supported tools

A supported tool is one Multi-Router serves in its own request format through a dedicated router:

ToolKindRouterFormats
Claude CodeCoding agent in the terminalClaude ProxyAnthropic, Amazon Bedrock and Google Vertex
Claude DesktopDesktop chat appClaude ProxyAnthropic
Pi.DevCoding agent in the terminalPi ProxyAnthropic, OpenAI and OpenAI Codex

How a tool connects

A tool connects to Multi-Router by sending its normal requests to the Multi-Router address, https://api.multi-router.ai, with two extra headers. The X-MR-Router header names the Claude Proxy or Pi Proxy that serves the tool, and the Multi-Router API key identifies the person or machine making the request. Each tool's page gives the exact settings. A request is handled in this order:

  1. The tool sends a request in its own format, such as an Anthropic Messages request from Claude Code.
  2. Multi-Router checks the API key and selects the router named in X-MR-Router.
  3. The router checks the request against the organization's rules, such as the allowed models, budgets and rate limits.
  4. The router forwards the request to the provider with the credential its settings call for, and streams the response back unchanged.
  5. Traffic records the request with its router, API key, model, tokens and cost.

Credentials

The credential is the provider login that pays for each request. A Claude Proxy uses one of two credential sources, chosen in its settings:

Credential sourceWho paysUsed by
OrganizationThe organization's provider key, such as an Anthropic, Bedrock or Vertex key added in Provider Keys. The organization's key is the only provider credential involved.Claude Code on any route, and Claude Desktop on the Anthropic route
DeveloperEach developer's own Anthropic login or API key, such as a Claude subscription, passed through to Anthropic as sent.Claude Code on the Anthropic route

A Pi Proxy always uses the developer's own provider login: an Anthropic API key or Claude subscription, an OpenAI API key, or a ChatGPT subscription.

With either source, the developer also holds a Multi-Router API key, which identifies them in Traffic and applies the organization's limits. See Provider Key and Claude Proxy.

What the organization controls

Once a tool connects through a router, the organization manages it from Multi-Router:

ControlApplies toEffect
Route and regionClaude ProxyWhich provider and region serve the tool, such as Anthropic, or Bedrock in the US.
Allowed modelsClaude ProxyThe models the router serves. A model group on the Custom route limits Claude Code and Claude Desktop to an approved set.
Disabled modelsClaude ProxyIndividual models switched off on the router are refused.
DisableClaude Proxy and Pi ProxyDisabling the router stops the tool for everyone using it, from the next request.
BudgetsClaude Proxy and Pi ProxySpending caps for the organization, chosen routers or router tags, with optional caps per model on Claude Proxies. When each person has their own router, as Self-Service Provisioning sets up, a router budget caps one person. A request past its cap is refused until the budget resets or is raised.
Rate limitsClaude Proxy and Pi ProxyRequests per API key.
Request loggingClaude Proxy and Pi ProxyWhether Traffic records request metadata and payloads.

API keys

Each person or machine using a tool needs a Multi-Router API key with the scopes the tool calls for. A key can also be limited to the tool's router, so it reaches nothing else in the organization.

ScopeNeeded for
inferenceEvery request a tool sends.
models:readClaude Desktop's model picker, and model discovery in Claude Code.

In organizations that use Self-Service Provisioning, people set up Claude Code and Claude Desktop themselves from the Portal, which creates their router and API key from a tools policy and shows the settings to paste. See Self-Service Provisioning.

Usage in Traffic

Traffic shows each request a tool sends, with the router in the Router column and the API key in the request detail, so usage and cost are visible per person and per tool. Claude Code requests also carry their session id, which groups the requests of one Claude Code session. See Observability.

PageCovers
Claude CodeSettings for each route and credential source, models and sessions.
Claude DesktopThe gateway settings, the model picker and rolling the settings out.
Pi.DevProvider settings for Anthropic, OpenAI and OpenAI Codex.
TroubleshootingErrors each tool can return, with their causes and fixes.
Claude ProxyRoutes, credentials, regions and models of a Claude Proxy.
Pi ProxyEndpoints and model checks of a Pi Proxy.

Agents & Editors

Claude Code

Connecting Claude Code to a Claude Proxy with the Anthropic, Amazon Bedrock or Google Vertex format.

Claude Code is Anthropic's coding agent for the terminal. It connects to Multi-Router through a Claude Proxy, using a few settings that point it at Multi-Router and name the proxy. Claude Code then works as usual: it sends its requests in the Anthropic, Amazon Bedrock or Google Vertex format, and the Claude Proxy serves them from the route and region the organization chose, with the organization's limits applied.

Installing Claude Code

Claude Code is installed with Anthropic's installer. On macOS, Linux and WSL, run the command below; Windows and other options are covered in Anthropic's Claude Code documentation.

Install Claude Codebash
curl -fsSL https://claude.ai/install.sh | bash

Choosing a setup

The setup follows from two settings of the Claude Proxy: its route, which is the provider that serves the requests, and its credential source, which decides whose login pays. Each combination uses one of four sets of settings:

SetupRouteCredential sourceClaude Code sends
Anthropic, developer loginAnthropicDeveloperAnthropic format, with the developer's own Claude login
Anthropic, organization keyAnthropicOrganizationAnthropic format
BedrockAny Amazon Bedrock routeOrganizationAmazon Bedrock format
VertexAny Google Vertex routeOrganizationGoogle Vertex format

The Claude Proxy's page in the dashboard shows the settings for its own setup under Claude Code setup, filled in with the proxy's id, with a Copy button. Replace the API key placeholder with the developer's Multi-Router API key.

Back to Routers

Eng Claude Code

Route: anthropic

enabledclaude proxy
engAdd tag

Claude Code for the engineering team, billed to the organization Anthropic account.

rt_5Tc7Hn1GsR·created Aug 18, 2026·updated Oct 1, 2026
Disable
OverviewTrafficActivity

Aliases

Add
claude-code

Routing

How the model names Claude Code sends are resolved and where they are served.

Anthropic API · globalEdit
Route
Anthropic APIanthropic · standard12 of 12 models · 2 regions
Forwards to api.anthropic.com. Every servable model, one region.
Region
globalAll 12 modelsone of 2 regions on Anthropic API
Models
12 of 12 models routableAnthropic API · global
ModelVersionVendorVariantRegionWire id chat · count tokensPrice / 1M in · out
Claude Fable 5
anthropic/claude-fable-5
5anthropicstandardglobal
claude-fable-5
claude-fable-5
$10.00 / $50.00
Claude Fable 5.1
anthropic/claude-fable-5.1
5.1anthropicstandardglobal
claude-fable-5-1
claude-fable-5-1
$10.00 / $50.00
Claude Haiku 4.5
anthropic/claude-haiku-4.5
4.5anthropicstandardglobal
claude-haiku-4-5
claude-haiku-4-5
$1.00 / $5.00
Claude Opus 4.5
anthropic/claude-opus-4.5
4.5anthropicstandardglobal
claude-opus-4-5
claude-opus-4-5
$5.00 / $25.00
Claude Opus 4.6
anthropic/claude-opus-4.6
4.6anthropicstandardglobal
claude-opus-4-6
claude-opus-4-6
$5.00 / $25.00
Claude Opus 4.7
anthropic/claude-opus-4.7
4.7anthropicstandardglobal
claude-opus-4-7
claude-opus-4-7
$5.00 / $25.00
Claude Opus 4.8
anthropic/claude-opus-4.8
4.8anthropicstandardglobal
claude-opus-4-8
claude-opus-4-8
$5.00 / $25.00
Claude Opus 5
anthropic/claude-opus-5
5anthropicstandardglobal
claude-opus-5
claude-opus-5
$5.00 / $25.00
Claude Opus 5.5
anthropic/claude-opus-5.5
5.5anthropicstandardglobal
claude-opus-5-5
claude-opus-5-5
$4.00 / $20.00
Claude Sonnet 4.5
anthropic/claude-sonnet-4.5
4.5anthropicstandardglobal
claude-sonnet-4-5
claude-sonnet-4-5
$3.00 / $15.00
Claude Sonnet 4.6
anthropic/claude-sonnet-4.6
4.6anthropicstandardglobal
claude-sonnet-4-6
claude-sonnet-4-6
$3.00 / $15.00
Claude Sonnet 5
anthropic/claude-sonnet-5
5anthropicstandardglobal
claude-sonnet-5
claude-sonnet-5
$2.00 / $10.00
Client endpoint
Anthropic dialecthttps://api.multi-router.aiserves every model
Bedrock dialecthttps://api.multi-router.ai/bedrockrefuses every model on this route
Vertex dialecthttps://api.multi-router.ai/vertexrefuses every model on this route
What Claude Code is pointed at. A request may arrive on any door; after the model resolves, its vendor is checked against the door.
Forwards to
anthropic · global · 12 models
POSThttps://api.anthropic.com/v1/messages
POSThttps://api.anthropic.com/v1/messages/count_tokens
model list
GEThttps://api.multi-router.ai/v1/modelsanswered from the catalog, not forwarded · 12 models, each under its chat wire id
Credentials
Organization Key
Anthropic productionAPI keyanthropicall regions
This key carries no region, so there is nothing to reconcile.
Claude Code setup
Paste into the employee’s shell.Copy
export ANTHROPIC_BASE_URL='https://api.multi-router.ai'
export ANTHROPIC_AUTH_TOKEN='<employee MR token>'
export ANTHROPIC_CUSTOM_HEADERS='X-MR-Router: rt_5Tc7Hn1GsR'
export CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK=1

No ANTHROPIC_DEFAULT_*_MODEL lines. Whatever model name Claude Code sends is resolved through the catalog's inbound names and routed on the table above.

Request Logging

What this router records about its requests. Capped by the administrator's Storage Policy.

Edit
Request metadataFull Metadata
Request payloadsNone
A Claude Proxy on the Anthropic route with the organization key, with Claude Code setup expanded to show the settings to paste. Select to enlarge.

Where the settings go

Claude Code reads its Multi-Router settings as environment variables, set either in the shell or under "env" in a settings file. The user settings file is the most common choice, because it applies to every Claude Code session for that user:

PlaceLocationApplies to
User settings file~/.claude/settings.json on macOS, Linux and WSLEvery session for that user.
User settings file on Windows%USERPROFILE%\.claude\settings.jsonEvery session for that user.
Project settings file.claude/settings.json in a repository, shared with the teamSessions started in that project.
Local project settings file.claude/settings.local.json in a repository, kept out of version controlSessions started in that project, for one person.
Shell environmentexport lines in ~/.zshrc or ~/.bashrcSessions started from that shell.
Managed settings on macOS/Library/Application Support/ClaudeCode/managed-settings.json, deployed by ITEvery user on the machine, and takes precedence over user settings.
Managed settings on Linux/etc/claude-code/managed-settings.json, deployed by ITEvery user on the machine, and takes precedence over user settings.
Managed settings on WindowsC:\Program Files\ClaudeCode\managed-settings.json, deployed by ITEvery user on the machine, and takes precedence over user settings.

When the same setting appears in several places, managed settings take precedence, then local project settings, then project settings, then user settings. A settings file usually holds other settings too, such as permissions or apiKeyHelper; add the Multi-Router values to its "env" block and keep the rest of the file as it is. The settings for the Anthropic route with the organization key look like this:

~/.claude/settings.jsonjson
{
  "env": {
    "ANTHROPIC_BASE_URL": "https://api.multi-router.ai",
    "ANTHROPIC_AUTH_TOKEN": "<Multi-Router API key>",
    "ANTHROPIC_CUSTOM_HEADERS": "X-MR-Router: rt_5Tc7Hn1GsR",
    "CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK": "1"
  }
}

ANTHROPIC_CUSTOM_HEADERS holds one header per line. In a settings file, lines are separated with \n; in a shell, the value is written across two lines inside quotes.

Anthropic route with the developer's login

With the Developer credential source, each developer signs in to Claude Code with their own Claude account or Anthropic API key, as they would without Multi-Router. Multi-Router passes that login to Anthropic as sent, without storing it, and usage is billed to the developer's own Claude plan or Anthropic account. The Multi-Router API key travels in its own header, X-MR-Api-Token, next to the router id:

Anthropic route, developer loginbash
export ANTHROPIC_BASE_URL='https://api.multi-router.ai'
export ANTHROPIC_CUSTOM_HEADERS='X-MR-Router: rt_5Tc7Hn1GsR
X-MR-Api-Token: Bearer <Multi-Router API key>'

Leave ANTHROPIC_AUTH_TOKEN unset in this setup, so that Claude Code sends the developer's own login. The Developer credential source is available on the Anthropic route.

Anthropic route with the organization key

With the Organization credential source, the Claude Proxy sends each request to Anthropic with the organization's Anthropic key, and usage is billed to the organization's Anthropic account. The Multi-Router API key is the developer's only credential:

Anthropic route, organization keybash
export ANTHROPIC_BASE_URL='https://api.multi-router.ai'
export ANTHROPIC_AUTH_TOKEN='<Multi-Router API key>'
export ANTHROPIC_CUSTOM_HEADERS='X-MR-Router: rt_5Tc7Hn1GsR'
export CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK=1

CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK lets Claude Code use fast mode when it signs in with a Multi-Router API key. Managed settings that require a Claude login, forceLoginMethod and forceLoginOrgUUID, suit the Developer setup only; leave them out of managed settings for the organization key, Bedrock and Vertex setups, which sign in with the Multi-Router API key through ANTHROPIC_AUTH_TOKEN or apiKeyHelper.

Amazon Bedrock routes

On a Bedrock route, Claude Code sends requests in the Amazon Bedrock format to the Claude Proxy's Bedrock endpoint, and the Claude Proxy signs them with the organization's Bedrock key. The Multi-Router API key is the developer's only credential:

Bedrock routebash
export CLAUDE_CODE_USE_BEDROCK=1
export CLAUDE_CODE_SKIP_BEDROCK_AUTH=1
export ANTHROPIC_BEDROCK_BASE_URL='https://api.multi-router.ai/bedrock'
export ANTHROPIC_AUTH_TOKEN='<Multi-Router API key>'
export ANTHROPIC_CUSTOM_HEADERS='X-MR-Router: rt_5Tc7Hn1GsR'
export CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK=1

Claude Code can name a model by its Bedrock model id, inference profile id or ARN. The Claude Proxy resolves the name and builds the Bedrock request for the route's own region, so a region inside an ARN is replaced by the route's region. See AWS Bedrock.

Google Vertex routes

On a Vertex route, Claude Code sends requests in the Google Vertex format to the Claude Proxy's Vertex endpoint, and the Claude Proxy authenticates with the organization's Vertex key. The Multi-Router API key is the developer's only credential:

Vertex routebash
export CLAUDE_CODE_USE_VERTEX=1
export CLAUDE_CODE_SKIP_VERTEX_AUTH=1
export ANTHROPIC_VERTEX_BASE_URL='https://api.multi-router.ai/vertex'
export ANTHROPIC_VERTEX_PROJECT_ID='example-co-ai'
export CLOUD_ML_REGION='us-east5'
export ANTHROPIC_AUTH_TOKEN='<Multi-Router API key>'
export ANTHROPIC_CUSTOM_HEADERS='X-MR-Router: rt_5Tc7Hn1GsR'
export CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK=1

Claude Code needs ANTHROPIC_VERTEX_PROJECT_ID and CLOUD_ML_REGION to build its request paths. The Claude Proxy replaces both with the Vertex key's project and the route's region, so the values shown in the Claude Code setup on the proxy's page are the ones to use. See Google Vertex.

Keys from a script with apiKeyHelper

apiKeyHelper is a Claude Code setting that names a script whose output is the API key. Claude Code runs the script when it starts and again when the key needs refreshing, so the Multi-Router API key can come from a secrets manager or vault and be rotated there, with no key written in the settings file. The helper replaces ANTHROPIC_AUTH_TOKEN in the organization key, Bedrock and Vertex setups, and the other settings stay the same:

~/.claude/settings.jsonjson
{
  "apiKeyHelper": "~/.claude/mr-api-key.sh",
  "env": {
    "ANTHROPIC_BASE_URL": "https://api.multi-router.ai",
    "ANTHROPIC_CUSTOM_HEADERS": "X-MR-Router: rt_5Tc7Hn1GsR",
    "CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK": "1"
  }
}

The script prints the Multi-Router API key and nothing else. For example, reading it from HashiCorp Vault:

~/.claude/mr-api-key.shbash
#!/bin/sh
vault kv get -field=api_key secret/multi-router/claude-code

The helper works as follows with Multi-Router:

BehaviorDetail
HeaderClaude Code sends the helper's key in the x-api-key header, which a Claude Proxy accepts on the Anthropic, Bedrock and Vertex routes.
RefreshClaude Code runs the helper again after 5 minutes, or after a request is refused with 401. CLAUDE_CODE_API_KEY_HELPER_TTL_MS sets a different refresh interval in milliseconds.
RotationA new key returned by the helper is used from the next refresh, so rotating the key in the vault and then disabling the old key in Multi-Router changes keys with no restart.
SetupsThe organization key, Bedrock and Vertex setups. The Developer setup carries the Multi-Router key in X-MR-Api-Token and keeps the developer's own Claude login, so it uses no helper.
Other key settingsSet only one of apiKeyHelper, ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY.
Model discoveryModel discovery is reliable with ANTHROPIC_AUTH_TOKEN. Claude Code has a known issue reading the model list with helper keys, so with a helper, models are chosen with /model.

In managed settings, apiKeyHelper lets IT deploy one settings file to every machine, with each person's key coming from their own vault entry.

Models

Claude Code chooses its models as it does with Anthropic directly, including when a developer switches models with /model. The Claude Proxy accepts any spelling of a model's name, such as the Anthropic model id, the Multi-Router catalog slug anthropic/claude-opus-5, a Bedrock inference profile id or an ARN, and serves it on the route's provider. Each response carries the x-mr-upstream-wire-id header with the model id sent to the provider.

A developer chooses the model in three ways:

WhenHowExample
At launchThe --model optionclaude --model claude-opus-5
During a session/model with a model name, keeping the conversation/model claude-sonnet-4-5
Current model/model with no name/model

Claude Code uses three model tiers, Opus, Sonnet and Haiku, and picks a tier for each task, such as Haiku for quick background work. Each tier can be pinned to a specific model, for example to keep a team on one version while a newer one is evaluated. Pinning is optional; without it, Claude Code uses its own default for each tier:

SettingTier
ANTHROPIC_DEFAULT_OPUS_MODELOpus
ANTHROPIC_DEFAULT_SONNET_MODELSonnet
ANTHROPIC_DEFAULT_HAIKU_MODELHaiku, used for quick background tasks
Pinning the Sonnet and Haiku tiersjson
{
  "env": {
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4-5",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5"
  }
}

A pinned model must be one the Claude Proxy serves.

A Claude Proxy serves the models of its route that are switched on, and answers a request for any other model with a message naming the model and the reason, such as a model switched off on the router or outside the route's model group. See Troubleshooting.

Model discovery

Model discovery lets Claude Code's model picker list exactly the models the Claude Proxy serves. It is turned on with one more setting, and works on the Anthropic route with an API key that has the models:read scope:

Model discoverybash
export CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1

Claude Code then reads the list from GET /v1/models on the proxy, which returns one entry for each model the route serves and the router has switched on. After the route, its model group or its switched-off models change, run /model to refresh the list. Setting CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC also turns discovery off.

Token counting

Claude Code counts tokens before some requests to manage its context window. A Claude Proxy answers token counts on the Anthropic route and Vertex routes whenever it would serve the matching request, at no cost and outside Traffic and budgets. On Bedrock routes, Claude Code estimates token counts itself.

Sessions in Traffic

Claude Code sends a session id with each request, and Traffic records it, so the requests of one Claude Code session can be viewed together. A tool or script can set its own session id with the x-mr-session-id header, which takes precedence. See Observability.

Features that need a direct Anthropic connection

A few Claude Code features work only when Claude Code connects to Anthropic directly. Remote Control is available when no base URL points to a gateway, and voice dictation is available when ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN and apiKeyHelper are unset. Every other feature, including subagents, tools, plan mode, prompt caching and fast mode, works through a Claude Proxy.

Switching an existing installation

An installation that was used with Anthropic directly can carry settings that take precedence over the Multi-Router ones. Before the first session through Multi-Router:

  1. Remove the model field from every settings file, so that model selection follows the Claude Proxy and /model.
  2. For the organization key, Bedrock and Vertex setups, run /logout in Claude Code, so that a stored Claude login gives way to the Multi-Router API key.
  3. Restart Claude Code, which reads its settings when it starts.

The Developer setup keeps the developer's Claude login, so it needs only the first and last steps.

Checking the setup

A working setup is confirmed from inside Claude Code:

  1. Run /status. It shows the base URL, which is https://api.multi-router.ai, or the Bedrock or Vertex address on those routes, and how Claude Code is signed in.
  2. Run /model with no name. It shows the model the session uses, which matches the model in the settings or the one chosen with /model.
  3. Ask Claude Code to say hello. The request then appears in Traffic under the Claude Proxy.

When Claude Code shows its login screen instead, the settings did not reach the Claude Code process; check that they are in a settings file or in the shell Claude Code was started from. When /model shows a different model than expected, look for a model field in a project or local settings file, or choose the model again with /model.

Agents & Editors

Claude Desktop

Connecting Claude Desktop to a Claude Proxy through its third-party inference gateway settings.

Claude Desktop is Anthropic's desktop app for chat and local work. It connects to Multi-Router through its third-party inference settings, which send its requests to a gateway instead of to Anthropic. The gateway is a Claude Proxy on the Anthropic route with the Organization credential source: requests are served with the organization's Anthropic key, and the app's model picker lists the models the organization allows.

Chat and Code

Claude Desktop has two kinds of work, each connected to Multi-Router in its own place. The Code tab runs Claude Code inside the app and reads Claude Code's settings, so it connects through the Claude Code setup. The rest of the app, including chat, connects through the gateway settings on this page.

Part of the appConnected throughSetup
Chat and local workThe third-party inference gateway settingsThis page, from Setting up
Code tabClaude Code's settings, such as ~/.claude/settings.jsonClaude Code. Start a new Code session after changing them.

Requirements

Claude Desktop connects through a Claude Proxy and an API key set up as follows:

ItemRequirement
Claude ProxyThe Anthropic route, or a Custom route whose model group contains Anthropic models, with the Organization credential source. Claude Desktop sends requests in the Anthropic format only.
API keyScopes inference and models:read. The model picker reads its list with models:read.
Claude DesktopInstalled from claude.ai/download and signed in with a work account.

Credentials

Claude Desktop sends one credential with each request, the Gateway API key, so the Multi-Router API key is the only credential it holds. The Claude Proxy therefore uses the Organization credential source: Multi-Router identifies the person from their API key and sends the request to Anthropic with the organization's Anthropic key, and usage is billed to the organization's Anthropic account. A tools policy for Claude Desktop accepts the Organization credential source only, for the same reason.

Setting up

The gateway is set in Claude Desktop's developer settings:

  1. In Claude Desktop, choose Help, then Troubleshooting, then Enable Developer Mode.
  2. Choose Developer, then Configure Third-Party Inference, and open Connection.
  3. Fill in the fields below, and save.
  4. Quit and reopen Claude Desktop, which reads these settings when it starts.
FieldValue
Inference providerGateway
Gateway base URLhttps://api.multi-router.ai
Credential kindStatic API key
Gateway API keyThe Multi-Router API key
Gateway auth schemeBearer. The key then travels in the Authorization header on every request, including the model list.
Custom inference headersX-MR-Router with the Claude Proxy's id, such as rt_5Tc7Hn1GsR

To check the setup, open a new chat and say hello. The reply appears in Traffic under the Claude Proxy.

Models

Claude Desktop fills its model picker from the Claude Proxy, which lists each model its route serves and the router has switched on. A model group of Anthropic models on the Custom route therefore controls exactly which models people can pick. Leave the app's own model list setting, inferenceModels, unset so that the picker follows the proxy. The picker refreshes when Claude Desktop starts.

The list comes from the Claude Proxy and behaves as follows:

CasePicker
Each model the route servesOne entry, with the model's display name, such as Claude Opus 5.
A model switched off on the Claude ProxyRemoved from the picker the next time Claude Desktop starts.
A model added to the model groupAdded to the picker the next time Claude Desktop starts.
An API key without models:readEmpty, because the list request is refused with insufficient_scope. Add the scope to the key.

Example

A company gives its support team Claude Desktop with two approved models. An admin creates a model group named Support models containing Claude Opus 5 and Claude Sonnet 4.5, then a Claude Proxy named Support desktop on the Custom route with that group and the Organization credential source. Its id is rt_6Wp2Jd8LmS. Each person on the team gets an API key with the inference and models:read scopes, limited to that router, and enters these values in Claude Desktop:

Configure Third-Party Inference, Connectiontext
Inference provider:       Gateway
Gateway base URL:         https://api.multi-router.ai
Credential kind:          Static API key
Gateway API key:          <the person's Multi-Router API key>
Gateway auth scheme:      Bearer
Custom inference headers: X-MR-Router = rt_6Wp2Jd8LmS

After a restart, the model picker lists Claude Opus 5 and Claude Sonnet 4.5. When the admin later adds Claude Haiku 4.5 to Support models, it appears in everyone's picker the next time they open Claude Desktop, with no change on their machines. A budget on the Support desktop router caps the team's monthly spend, and Traffic shows each person's usage by their API key.

Rolling out to many people

IT teams roll the same settings out to many machines from the Configure Third-Party Inference screen: Export saves the settings as a .mobileconfig profile for macOS or a .reg file for Windows, which device management tools then install. Each person needs their own Multi-Router API key, so that Traffic and budgets apply to them individually.

In organizations that use Self-Service Provisioning, people set up Claude Desktop themselves from the Portal. Choosing Claude Desktop creates their own Claude Proxy from the organization's tools policy and an API key with the inference and models:read scopes. The Portal then shows the install and Developer Mode steps, each Connection field value with its own copy button, the key masked with Copy and Reveal key, and the steps to check the setup. See Self-Service Provisioning.

Rolling out the Code tab

The Code tab follows Claude Code's managed settings, which device management tools such as Jamf, Kandji and Intune install on each machine. Managed settings take precedence over each person's own settings, so every Code tab session goes through the Claude Proxy. The settings file and its contents are the same as for Claude Code:

PlatformManaged settings file
macOS/Library/Application Support/ClaudeCode/managed-settings.json
WindowsC:\Program Files\ClaudeCode\managed-settings.json

With one file for every machine, apiKeyHelper gives each person their own Multi-Router API key from a vault. See Claude Code.

Changing settings and rotating keys

Claude Desktop reads its gateway settings only when it starts, so every change on a machine takes effect after the app is quit and reopened. Changes made in Multi-Router, such as the models a Claude Proxy serves, its route or its budgets, apply to requests at once and reach the model picker at the next start. To rotate a person's API key:

  1. Create a new API key with the same scopes and router limit.
  2. Enter it in the Gateway API key field, or redeploy the exported profile with the new key, and restart Claude Desktop.
  3. Disable the old key in Multi-Router once the new one is in use.

Usage in Traffic

Each Claude Desktop request appears in Traffic under the Claude Proxy in the Router column, with the person's API key in the request detail, the model, tokens and cost. Filtering Traffic by the Claude Proxy shows the team's usage, and the API key separates one person from another. When each person has their own Claude Proxy, as Self-Service Provisioning sets up, the Router column alone identifies the person. See Observability.

Troubleshooting

Claude Desktop shows Multi-Router's errors in the chat. The common ones are:

Symptom or messageCauseFix
400 your MR API token was sent in authorizationThe Claude Proxy uses the Developer credential source.Switch the Claude Proxy to the Organization credential source.
404 model is served on bedrock, not available on the Anthropic endpointThe Claude Proxy's route, or a model in its group, is served by Bedrock or Vertex.Use the Anthropic route, or a model group of Anthropic models.
The model picker is emptyThe API key lacks models:read, or the app was not restarted after setup.Add the scope to the key, then quit and reopen Claude Desktop.
401 invalid or missing API tokenThe Gateway API key is wrong, disabled or expired.Enter a current key and restart.
403 this Claude Proxy is disabledAn admin disabled the Claude Proxy.Enable the Claude Proxy.
429 budget is exhaustedA budget on the Claude Proxy reached its cap.Wait for the budget to reset, or raise it.

See Troubleshooting for every message.

Supported sessions

Through a gateway, Claude Desktop runs local sessions: chats and local work on the person's own machine. SSH sessions, cloud environments and Remote Control use a direct connection to Anthropic.

Agents & Editors

Pi.Dev

Connecting the Pi.Dev coding agent to a Pi Proxy for Anthropic, OpenAI and OpenAI Codex.

Pi.Dev is a coding agent for the terminal that works with several model providers. It connects to Multi-Router through a Pi Proxy, which forwards its requests to Anthropic, OpenAI or OpenAI Codex unchanged. Developers keep their own provider logins, such as an Anthropic API key, a Claude subscription, an OpenAI API key or a ChatGPT subscription, and the organization sees every request in Traffic and can turn access off at any time.

Providers

A Pi.Dev provider is a model provider Pi.Dev can call. A Pi Proxy serves three of them:

Pi.Dev providerBase URLForwards toDeveloper login
anthropichttps://api.multi-router.aiAnthropicAnthropic API key or Claude subscription
openaihttps://api.multi-router.ai/v1OpenAIOpenAI API key
openai-codexhttps://api.multi-router.ai/backend-apiOpenAI CodexChatGPT subscription

Setting up

Routers›New router

New router

Cancel
Type
Configure
Review & create

Pi Proxy

Best for managing your team’s pi coding agent. You see usage for each user and can enable or disable access at any time.
Name
Human label shown in the routers list.
Eng Pi Proxy

A Pi Proxy forwards pi coding-agent traffic to Anthropic or OpenAI unchanged. Users keep their own provider credentials. After you create it, each user adds this to ~/.pi/agent/models.json:

{ "providers": {
  "anthropic": { "baseUrl": "https://api.multi-router.ai",
    "headers": { "X-MR-Router": "<the rt_… id>",
                 "X-MR-Api-Token": "Bearer <user MR token>" } },
  "openai": { "baseUrl": "https://api.multi-router.ai/v1",
    "headers": { "X-MR-Router": "<the rt_… id>",
                 "X-MR-Api-Token": "Bearer <user MR token>" } },
  "openai-codex": { "baseUrl": "https://api.multi-router.ai/backend-api",
    "headers": { "X-MR-Router": "<the rt_… id>",
                 "X-MR-Api-Token": "Bearer <user MR token>" } }
} }

Per-user usage then shows up in your traffic analytics. Disable the proxy or a user token to remove access.

Back
Review
Creating a Pi Proxy: the New Router page shows the models.json settings each developer adds. Select to enlarge.

Pi.Dev reads provider settings from ~/.pi/agent/models.json. Each provider points at Multi-Router and adds two headers: X-MR-Router with the Pi Proxy's id, and X-MR-Api-Token with the developer's Multi-Router API key. The developer's provider login stays where Pi.Dev keeps it.

~/.pi/agent/models.jsonjson
{
  "providers": {
    "anthropic": {
      "baseUrl": "https://api.multi-router.ai",
      "headers": {
        "X-MR-Router": "rt_3Kf8Vb2NwQ",
        "X-MR-Api-Token": "Bearer <Multi-Router API key>"
      }
    },
    "openai": {
      "baseUrl": "https://api.multi-router.ai/v1",
      "headers": {
        "X-MR-Router": "rt_3Kf8Vb2NwQ",
        "X-MR-Api-Token": "Bearer <Multi-Router API key>"
      }
    },
    "openai-codex": {
      "baseUrl": "https://api.multi-router.ai/backend-api",
      "headers": {
        "X-MR-Router": "rt_3Kf8Vb2NwQ",
        "X-MR-Api-Token": "Bearer <Multi-Router API key>"
      }
    }
  }
}

Pi.Dev streams responses through a Pi Proxy over server-sent events. Set the transport in ~/.pi/agent/settings.json:

~/.pi/agent/settings.jsonjson
{ "transport": "sse" }

Include only the providers the developer uses. To check the setup, start Pi.Dev, send a prompt, and find the request in Traffic under the Pi Proxy.

Credentials

A Pi Proxy always uses the developer's own provider login, and forwards it to the provider unchanged. The Multi-Router API key belongs only in X-MR-Api-Token, because the Authorization and x-api-key headers carry the provider login. A request that carries the Multi-Router key in one of those headers is refused with a message saying to move it to X-MR-Api-Token.

Models

Pi.Dev names models as the provider does, such as claude-sonnet-4-5 or gpt-5.1. The Pi Proxy checks each request's model before forwarding it: the model must be in the Multi-Router catalog and belong to the provider the request is sent to, so an OpenAI model sent to the Anthropic provider is refused with a message saying which provider to use. OpenAI Codex requests are forwarded without a model check. See Pi Proxy.

Usage in Traffic

Traffic records each request with the Pi Proxy, the developer's API key, the model, tokens and cost. Disabling the Pi Proxy stops access from the next request, and disabling the developer's API key stops access within about a minute.

Agents & Editors

Troubleshooting

Errors Claude Code, Claude Desktop and Pi.Dev can return through Multi-Router, with their causes and fixes.

Errors that Multi-Router returns to a tool arrive in the tool's own format, so the tool shows them as it would show a provider error, with Multi-Router's message. Errors from the provider itself, such as a provider rate limit, pass through unchanged. The tables below list Multi-Router's messages by area.

Setup and sign-in

These errors mean the tool's settings are incomplete or in the wrong place:

Symptom or messageCauseFix
Claude Code shows its login screenThe settings did not reach the Claude Code process.Put the settings in ~/.claude/settings.json, or start Claude Code from the shell where they are set.
401 invalid or missing API tokenThe Multi-Router API key is missing, mistyped, disabled or expired.Check the key in the tool's settings, and its status in API Keys.
400 your MR API token was sent in authorization, or in x-api-keyOn a Developer setup or a Pi Proxy, the Multi-Router key is in the header that carries the provider login. The message names that header.Move the key to X-MR-Api-Token. For Claude Desktop, switch the Claude Proxy to the Organization credential source instead.
400 X-MR-Router is required on the Bedrock ingressA Bedrock request has no X-MR-Router header.Add X-MR-Router to ANTHROPIC_CUSTOM_HEADERS.
400 X-MR-Router is required on the Google Vertex ingressA Vertex request has no X-MR-Router header.Add X-MR-Router to ANTHROPIC_CUSTOM_HEADERS.
403 insufficient_scopeThe API key lacks a scope the request needs.Add inference, and models:read for Claude Desktop or model discovery.
Model errors on the Anthropic route that name no Claude ProxyThe request has no X-MR-Router header, so it is routed by its model name as an ordinary request instead of through the Claude Proxy.Add X-MR-Router to ANTHROPIC_CUSTOM_HEADERS.

Models

A Claude Proxy refuses a model it does not serve with 404 and one of these messages:

MessageCause
model 'X' is not a known modelThe name matches no model in the catalog.
model 'slug' is disabled on this routerThe model is switched off on the Claude Proxy.
model 'slug' is not available on <route> in <region>The route's provider does not offer the model in that region.
model 'slug' is not in group <group>The model is outside the Custom route's model group.
model 'slug' is not available in group <group>The model is in the group, but its provider or region is not offered for it.
model 'slug' is served on <provider>, not available on the <format> endpointThe tool uses a format that does not match the route, such as the Bedrock format on an Anthropic route.
model 'slug' is <lifecycle>The model is retired or otherwise out of service, and the message gives its status.

An API key limited to specific routers receives a single 404 for any of these, so the key reveals nothing about other routers: the requested model or router was not found on the Anthropic route, and Claude Proxy not found on Bedrock and Vertex routes. When Claude Code's model picker lists models the proxy no longer serves, run /model; if the list stays the same, delete ~/.claude/cache/gateway-models.json and restart Claude Code.

Access and limits

These errors come from the organization's controls. On the Anthropic route and a Pi Proxy, budget errors have the error type budget_exhausted.

StatusMessageCause
403this Claude Proxy is disabledThe Claude Proxy was disabled.
403this Pi Proxy is disabledThe Pi Proxy was disabled.
429budget <id> is exhausted for this period; it resets at <time>A budget with a period reached its cap. The Retry-After header gives the seconds until it resets.
429budget <id> is exhaustedA lifetime budget reached its cap.
429per-token rate limit exceededThe API key sent more requests than its rate limit allows, on the Anthropic route or a Pi Proxy.
429rate limit exceededThe API key sent more requests than its rate limit allows, on a Bedrock or Vertex route.

Claude Proxy configuration

These errors mean the Claude Proxy needs attention from an admin:

StatusMessageFix
403this Claude Proxy has no routing configuration; save it with a route before it can serveChoose a route on the Claude Proxy and save it.
503this Claude Proxy's Organization key is unavailableEnable the provider key, or choose another on the Claude Proxy.
503this Claude Proxy has no provider key for its routeAdd a provider key for the route's provider.
503this Claude Proxy is on the Custom route without a model group; re-save itChoose a model group and save the Claude Proxy.
503this Claude Proxy's route `X` is not in the current catalog; re-save itChoose a current route and save the Claude Proxy.
502Amazon Bedrock request failedBedrock could not be reached. Retry, and check the Bedrock key.
503this Claude Proxy could not authenticate to Amazon BedrockCheck the Bedrock key's credentials and region.
503this Claude Proxy's Bedrock key configuration is unavailableCheck the Bedrock key, or choose another on the Claude Proxy.
503region `X` differs from the region its Bedrock API key was minted forUse a Bedrock key created for the route's region, or choose that key's region on the route.
502Google Vertex request failedVertex could not be reached. Retry, and check the Vertex key.
503this Claude Proxy's Google OAuth token is not ready; retry shortlyRetry after a few seconds.

Pi.Dev

A Pi Proxy refuses requests whose model does not fit the provider with 400 and one of these messages:

MessageFix
`X` is not an OpenAI modelChoose an OpenAI model for the openai provider, or move the model to its own provider.
`X` is not an Anthropic modelChoose an Anthropic model for the anthropic provider.
`X` is not known to the catalogUse a model id from the Multi-Router catalog.
`X` names a router, not a modelOn the openai provider, send a model id in model. A Pi Proxy forwards to the provider directly.

API Access

Overview

How applications, scripts and tools authenticate to Multi-Router, and how an API key decides what a caller can reach.

An API key is the credential an application, script or tool uses to call Multi-Router. It identifies the caller and decides what the caller can reach: which endpoints, which routers, and which resources it can read or change. A key belongs to one organization and reaches only that organization's routers, prompts and other resources. Applications use one API key for every request, while the routing, models, prompts and provider credentials stay on the routers the key calls.

API keys and routers

An API key and a router do different jobs. The key says who is calling and what they may do; the router says how a request is served: which model and provider, which prompt, and what Traffic records. Changing a router's model or prompt changes the behavior for every key that calls it, and issuing a new key gives a new caller access with no change to any router. See Routing.

The API address

The Multi-Router API is served at https://api.multi-router.ai, with its endpoints under /v1, such as POST /v1/chat/completions and GET /v1/routers. The same address also serves the Claude Proxy's Bedrock and Vertex endpoints and the Pi Proxy's OpenAI Codex endpoint, which accept the same keys. Each request carries its key in the Authorization header:

A request with an API keybash
curl https://api.multi-router.ai/v1/models \
  -H "Authorization: Bearer $MR_API_KEY"

Claude Code, Claude Desktop and Pi.Dev use the same key through a Claude Proxy or Pi Proxy, and some of them send it in a different header. See Authentication.

What a key can reach

Three settings on each key decide what it can reach:

SettingDecidesPage
ScopesWhich endpoints the key can call, such as inference only, or reading and changing routers.Scope
RoutersWhich routers the key can send inference requests to: any router, or a chosen list.Router Access
ExpiryThe date after which the key stops working.API Key

Every key also has a rate limit on inference requests. See Rate Limit.

Kinds of credentials

Multi-Router accepts three kinds of credentials for API calls, each created in its own place:

CredentialCreated byUsed for
API keyA person on the API Keys page of the dashboard.Applications, scripts and tools, with the scopes and routers chosen for the key.
Managed keySelf-Service Provisioning, when a person sets up a tool from the Portal.That person's tool, on their own router.
Machine identityA workload's own cloud identity, such as an AWS role or a Kubernetes service account, matched to a machine binding.Workloads that call Multi-Router with their cloud identity in place of a stored key, with inference access.

See Self-Service Provisioning and Machine Identity.

Security

API keys are stored encrypted and checked on every request. Provider keys stay inside Multi-Router, out of callers' reach. A disabled or expired key is refused, and a change to a key, such as disabling it or changing its scopes, takes effect within about a minute. Scoped keys reach only the endpoints their scopes name, which keeps endpoints added in the future closed to them, and keys limited to specific routers receive a "not found" response for every router outside their list, which keeps the rest of the organization private.

The other pages in API Access cover each part of a key in detail:

PageCovers
API KeyCreating, copying, editing, disabling and deleting keys.
ScopeFull control, scoped keys, and every scope and the endpoints it covers.
Router AccessLimiting a key to specific routers and aliases.
AuthenticationThe headers that carry a key, and authentication errors.
Rate LimitRequest limits per key and the 429 response.

API Access

API Key

Creating, copying, editing, disabling and deleting API keys on the API Keys page.

API keys are created and managed on the API Keys page of the dashboard, opened from API Keys in the profile menu. Each key belongs to the organization selected when it is created, and the page lists the keys of the selected organization. A key's value starts with sk-mr-, and each key also has an id starting with tok_, such as tok_4Gm8Rq2VxN, which Traffic records in place of the value.

Fields

API Keys

Bearer tokens used to call /v1/*. Scoped to your account.

New API key
  • support-bot
    Support widget on the help center
    Inferencert_9Hk3Ws7PzQ, production·Modelsread
    sk-mr-xYzQ••••••••••••••••••••••••••••CopyCreated 9/28/2026
    AccessDisableDelete
  • deploy-pipeline
    CI job that manages routers and prompts
    Routerswrite·Promptswrite·Model groupsread
    sk-mr-4bTn••••••••••••••••••••••••••••CopyCreated 9/12/2026
    AccessDisableDelete
  • priya-claude-codemanaged
    Provisioned from the onboarding portal
    Inferencert_5Tc7Hn1GsR
    sk-mr-Lp8w••••••••••••••••••••••••••••CopyCreated 8/30/2026
    AccessDisableDelete
  • analytics-readonlydisabled
    Inferenceany router·Modelsread·Routersread·Promptsread·Prompt reposread·GitHubread·Provider keysread·Model groupsread·Budgetsread
    sk-mr-9cRd••••••••••••••••••••••••••••CopyCreated 7/2/2026
    AccessEnableDelete
  • legacy-admin
    Full controlevery capability, including ones added to the API later
    sk-mr-Qe2v••••••••••••••••••••••••••••CopyCreated 3/4/2026
    AccessDisableDelete
The API Keys page: scoped, router-limited, managed, disabled and full-control keys, each with its access summary. Select to enlarge.

An API key has the following fields:

FieldDashboard labelDescription
NameKey nameUp to 80 characters, such as production or support-bot.
DescriptionDescriptionOptional, up to 200 characters, such as who uses the key and where.
ScopesAccessFull control, or a chosen set of scopes. See Scope.
RoutersAccess, InferenceAny router, or a list of routers and aliases. See Router Access.
ExpirySet when the key is createdThe date after which the key stops working.
Statusdisabled badgeEnabled, or disabled with the disabled badge.
CreatedCreatedThe date the key was created.
IdToken in TrafficThe key's id, such as tok_4Gm8Rq2VxN, recorded on each request.

Creating a key

A key is created on the API Keys page:

  1. Open API Keys from the profile menu, and select New API key.
  2. Enter a Key name, and optionally a Description.
  3. Choose the key's access. A new key starts with the Inference only preset, which allows inference and listing models on any router. See Scope.
  4. Choose an expiry date for the key.
  5. Select Create.

The key appears in the list with its first characters shown, such as sk-mr-xYzQ followed by dots. Copy puts the full value on the clipboard.

Copying a key

A key's full value can be copied from the list at any time with Copy, including long after it was created. This lets a team add the same key to another system later, such as a second deployment, with the same key.

Expiry

An expiry date limits how long a key works. After the date, every request with the key is refused with 401, as for a disabled key, and the key stays in the list so its history in Traffic keeps its name. To keep a caller working, create a new key before the old one expires and move the caller to it.

Changing access

Access on a key's row opens the Access editor, which shows the key's description, scopes and routers. Edit makes them changeable, and Save applies the change. The key's value stays the same, so callers keep working with the new access within about a minute. See Scope and Router Access.

Disabling and deleting

Disable stops a key from working, and Enable restores it with the same value. Delete removes a key permanently and is available once the key is disabled, so a key in use is always switched off before it is removed. Deleting asks for confirmation. Requests already recorded in Traffic keep the key's id.

A change to a key, such as disabling it or changing its access, takes effect within about a minute.

Managed keys

A managed key is created by Self-Service Provisioning when a person sets up a tool such as Claude Code from the Portal. It allows inference on that person's own Claude Proxy, and for Claude Desktop also listing models. It shows the managed badge in the list, and its access is set when it is created and shown read-only in the Access editor. The person sees and copies the key in the Portal, where Issue a new key replaces it. See Self-Service Provisioning.

Keys in Traffic

Traffic records the id of the key that made each request, and the request detail shows it as Token. The id identifies the key while keeping its value private, and it is recorded whenever the router logs request metadata, including with Anonymous Metadata. Giving each application or person its own key makes their usage separable in Traffic. See Observability.

Practices

The following practices keep keys easy to manage and limit the effect of a leaked key:

PracticeReason
One key per application, environment or personUsage is separable in Traffic, and one key can be disabled without affecting others.
The narrowest scopes that workMost applications need only the Inference only preset.
A router list for single-purpose applicationsA leaked key reaches only the routers it was made for.
An expiry dateA forgotten key stops working on its own.
A description of where the key is usedThe right key is easy to find when it needs to be disabled.

API Access

Scope

Full control and scoped keys, every scope, and the endpoints each one covers.

A scope is a permission on an API key to use a group of endpoints, such as sending inference requests or changing routers. A key either has full control, with no restriction, or a set of scopes that names exactly what it can do. Scopes are chosen in the Access editor when a key is created, and changed later with Access on the key's row.

Full control and scoped keys

The Access editor sets a key to one of two modes:

ModeDashboard labelKey can
Full controlFull controlUse every endpoint the organization can, including endpoints added to the API later.
ScopedScopedUse only the endpoints its scopes name. Endpoints added later stay closed until a scope for them is granted.

A scoped key with an empty set shows as No access in the list. Scoped keys suit applications: a key that holds only inference keeps routers and prompts safe from change if it leaks.

Data plane scopes

Data plane scopes cover sending requests to models and listing them:

ScopeDashboard rowEndpoint
inferenceInferencePOST /v1/chat/completions
inferenceInferencePOST /v1/messages
inferenceInferencePOST /v1/messages/count_tokens
inferenceInferencePOST /v1/responses
inferenceInferenceThe Claude Proxy Bedrock endpoints under /bedrock
inferenceInferenceThe Claude Proxy Vertex endpoints under /vertex
inferenceInferenceThe Pi Proxy OpenAI Codex endpoint, POST /backend-api/codex/responses
models:readModel catalogGET /v1/models

The Inference row offers None, Any router, or Specific routers, which limits the key to a list of routers. See Router Access.

Control plane scopes

API Keys

Bearer tokens used to call /v1/*. Scoped to your account.

New API key
  • support-bot
    Support widget on the help center
    Inferencert_9Hk3Ws7PzQ, production·Modelsread
    sk-mr-xYzQ••••••••••••••••••••••••••••CopyCreated 9/28/2026
    AccessDisableDelete
  • deploy-pipeline
    CI job that manages routers and prompts
    Routerswrite·Promptswrite·Model groupsread
    sk-mr-4bTn••••••••••••••••••••••••••••CopyCreated 9/12/2026
    AccessDisableDelete
    Access
    ScopedFull control
    PresetsInference onlyRead everythingNothing
    Data plane
    Inference
    chat/completions · messages · responses · codex
    inference
    NoneAny routerSpecific routers
    Model catalog
    list the models available to this organization
    models
    NoneRead
    Control planeone level per resource group
    Routers
    routers, aliases and their configuration
    routers
    NoneReadWriteDelete
    Prompts
    prompt bodies and their revisions
    prompts
    NoneReadWriteDelete
    Prompt repos
    connected repos and tracked prompt files
    prompt_repos
    NoneReadWriteDelete
    GitHub
    connected GitHub accounts
    list and disconnect only — connecting an account is a dashboard step
    github
    NoneReadDelete
    Provider keys
    upstream provider credentials
    read-only over the API — keys are managed from the dashboard
    provider_keys
    NoneRead
    Model groups
    organization-owned model sets and their membership
    model_groups
    NoneReadWriteDelete
    Budgets
    spending limits and their status
    budgets
    NoneReadWriteDelete

    Every row works the same way: pick one level, and each level includes the ones before it. Read lists and inspects. Write adds create and edit. Delete adds removal — the only irreversible one, and on routers it also orphans that router's traffic history. Groups show only the levels the API supports.

    CancelSave
  • priya-claude-codemanaged
    Provisioned from the onboarding portal
    Inferencert_5Tc7Hn1GsR
    sk-mr-Lp8w••••••••••••••••••••••••••••CopyCreated 8/30/2026
    AccessDisableDelete
  • analytics-readonlydisabled
    Inferenceany router·Modelsread·Routersread·Promptsread·Prompt reposread·GitHubread·Provider keysread·Model groupsread·Budgetsread
    sk-mr-9cRd••••••••••••••••••••••••••••CopyCreated 7/2/2026
    AccessEnableDelete
  • legacy-admin
    Full controlevery capability, including ones added to the API later
    sk-mr-Qe2v••••••••••••••••••••••••••••CopyCreated 3/4/2026
    AccessDisableDelete
The Access editor for a deployment key: Inference off, Write on Routers and Prompts, and Read on Model groups. Select to enlarge.

Control plane scopes cover managing resources through the API. Each resource group has levels that build on each other: read allows reading, write adds creating and changing, and delete adds deleting. A key holds one level per group. Reading uses GET, writing uses POST and PATCH, and deleting uses DELETE.

ScopeDashboard rowLevelAllows
routers:readRoutersReadReading on /v1/routers
routers:writeRoutersWriteReading, creating and changing on /v1/routers
routers:deleteRoutersDeleteReading, creating, changing and deleting on /v1/routers
prompts:readPromptsReadReading on /v1/prompts
prompts:writePromptsWriteReading, creating and changing on /v1/prompts
prompts:deletePromptsDeleteReading, creating, changing and deleting on /v1/prompts
prompt_repos:readPrompt reposReadReading on /v1/prompt-repos
prompt_repos:writePrompt reposWriteReading, creating and changing on /v1/prompt-repos
prompt_repos:deletePrompt reposDeleteReading, creating, changing and deleting on /v1/prompt-repos
github:readGitHubReadReading on /v1/github/connections
github:deleteGitHubDeleteReading and deleting on /v1/github/connections
provider_keys:readProvider keysReadReading on /v1/provider-keys
provider_keys:writeProvider keysWriteReading, creating and changing on /v1/provider-keys
provider_keys:deleteProvider keysDeleteReading, creating, changing and deleting on /v1/provider-keys
model_groups:readModel groupsReadReading on /v1/model-groups
model_groups:writeModel groupsWriteReading, creating and changing on /v1/model-groups
model_groups:deleteModel groupsDeleteReading, creating, changing and deleting on /v1/model-groups
budgets:readBudgetsReadReading on /v1/budgets
budgets:writeBudgetsWriteReading, creating and changing on /v1/budgets
budgets:deleteBudgetsDeleteReading, creating, changing and deleting on /v1/budgets
traffic:readTrafficReadReading on /v1/traffic
tags:readTagsReadReading on /v1/tags
api_tokens:readAPI tokensReadReading on /v1/api-tokens
agents:readAgentsReadReading on /v1/agents
agents:deleteAgentsDeleteReading and deleting on /v1/agents
organizations:readOrganizationsReadReading on /v1/organizations
organizations:writeOrganizationsWriteReading, creating and changing on /v1/organizations
organizations:deleteOrganizationsDeleteReading, creating, changing and deleting on /v1/organizations
invites:readInvitesReadReading on /v1/people/invites
invites:writeInvitesWriteReading, creating and changing on /v1/people/invites
invites:deleteInvitesDeleteReading, creating, changing and deleting on /v1/people/invites
sso:readSSOReadReading on /v1/sso
sso:writeSSOWriteReading, creating and changing on /v1/sso
sso:deleteSSODeleteReading, creating, changing and deleting on /v1/sso
machine_identity:readMachine identityReadReading on /v1/machine-identity
machine_identity:writeMachine identityWriteReading, creating and changing on /v1/machine-identity
machine_identity:deleteMachine identityDeleteReading, creating, changing and deleting on /v1/machine-identity

GitHub connections and agents are read and removed through the API, and set up in the dashboard and the Portal, so those groups have no write level. Traffic, tags and API tokens are read-only through the API. Three GET endpoints need budgets:write: a budget's overrides, its budget requests and one budget request. The catalog endpoints under /v1/catalog need models:read.

Presets

Presets set every row of the Access editor at once:

PresetScopes
Inference onlyinference on any router, and models:read. New keys start with this preset.
Read everythinginference, models:read, and Read on every control plane row.
NothingAn empty set of scopes.

When every group is set to its highest level, the editor notes that the key still holds a fixed list, and offers Switch to Full control for a key that should also reach endpoints added later.

Examples

Common keys and the access each needs:

Key forAccess
A production applicationInference only, with a router list naming the routers it calls.
A script that audits router and prompt configurationRead on Routers, Prompts and Model groups, with Inference set to None.
A deployment pipeline that manages routers and promptsRouters, Prompts and Model groups at Write or Delete, with Inference set to None.
Claude Desktopinference and models:read, limited to its Claude Proxy.

Scope names

Each scope has a name, such as routers:write, which error messages use to say which scope a request needs. A key holds one level per group, and each level includes the ones below it, so a key with routers:write also reads routers. Workloads that sign in with Machine Identity hold inference. See Machine Identity.

Errors

A scoped key that calls an endpoint outside its scopes receives one of these responses:

StatusResponseCause
403insufficient_scope, with the message this API key is not permitted to use this endpoint; it requires the <scope> scopeThe endpoint needs a scope the key lacks. The message names the lowest scope that allows the request.
403insufficient_scope, with the message this API key is not permitted to use this endpointThe endpoint belongs to no scope the key can hold, such as a newer endpoint.
403API token lacks the required inference scopeA Bedrock or Vertex request through a Claude Proxy, from a key without inference.
404not foundThe method is not one the group offers, such as a write to provider keys.

API Access

Router Access

Limiting an API key's inference requests to specific routers and aliases.

Router access limits which routers an API key can send inference requests to. A key set to Any router reaches every router in the organization; a key set to Specific routers reaches only the routers and aliases in its list. A limited key suits an application with one job, such as a support bot, because a leaked key then reaches only the routers that application uses, with the models, prompts and budgets those routers carry.

Setting the list

API Keys

Bearer tokens used to call /v1/*. Scoped to your account.

New API key
  • support-bot
    Support widget on the help center
    Inferencert_9Hk3Ws7PzQ, production·Modelsread
    sk-mr-xYzQ••••••••••••••••••••••••••••CopyCreated 9/28/2026
    AccessDisableDelete
    Access
    ScopedFull control
    PresetsInference onlyRead everythingNothing
    Data plane
    Inference
    chat/completions · messages · responses · codex
    inference
    NoneAny routerSpecific routers
    rt_9Hk3Ws7PzQ
    production
    Add router
    Direct model access is off for this key. A request naming a provider model, or relying on a bare model name resolving by pattern, is refused — otherwise the key could bypass these routers entirely.
    Model catalog
    list the models available to this organization
    models
    NoneRead
    Control planeone level per resource group
    Routers
    routers, aliases and their configuration
    routers
    NoneReadWriteDelete
    Prompts
    prompt bodies and their revisions
    prompts
    NoneReadWriteDelete
    Prompt repos
    connected repos and tracked prompt files
    prompt_repos
    NoneReadWriteDelete
    GitHub
    connected GitHub accounts
    list and disconnect only — connecting an account is a dashboard step
    github
    NoneReadDelete
    Provider keys
    upstream provider credentials
    read-only over the API — keys are managed from the dashboard
    provider_keys
    NoneRead
    Model groups
    organization-owned model sets and their membership
    model_groups
    NoneReadWriteDelete
    Budgets
    spending limits and their status
    budgets
    NoneReadWriteDelete

    Every row works the same way: pick one level, and each level includes the ones before it. Read lists and inspects. Write adds create and edit. Delete adds removal — the only irreversible one, and on routers it also orphans that router's traffic history. Groups show only the levels the API supports.

    CancelSave
  • deploy-pipeline
    CI job that manages routers and prompts
    Routerswrite·Promptswrite·Model groupsread
    sk-mr-4bTn••••••••••••••••••••••••••••CopyCreated 9/12/2026
    AccessDisableDelete
  • priya-claude-codemanaged
    Provisioned from the onboarding portal
    Inferencert_5Tc7Hn1GsR
    sk-mr-Lp8w••••••••••••••••••••••••••••CopyCreated 8/30/2026
    AccessDisableDelete
  • analytics-readonlydisabled
    Inferenceany router·Modelsread·Routersread·Promptsread·Prompt reposread·GitHubread·Provider keysread·Model groupsread·Budgetsread
    sk-mr-9cRd••••••••••••••••••••••••••••CopyCreated 7/2/2026
    AccessEnableDelete
  • legacy-admin
    Full controlevery capability, including ones added to the API later
    sk-mr-Qe2v••••••••••••••••••••••••••••CopyCreated 3/4/2026
    AccessDisableDelete
A key limited to two entries, a router and an alias, with direct model access off. Select to enlarge.

In the Access editor, the Inference row has three choices: None, Any router and Specific routers. Specific routers shows the key's list with Add router, which opens a search of routers by name or id, and the list can hold routers and aliases. A list holds at least one entry and at most 100. A router list works together with the inference scope, so a key with full control reaches every router; to limit a key's routers, set it to Scoped first.

How requests are matched

A request is allowed when the name it uses, or the router that name leads to, is in the key's list. This makes aliases work in both directions:

List containsRequest namesResult
The alias productionproductionAllowed. When the alias is repointed, the key follows it to the new router.
The router rt_9Hk3Ws7PzQAn alias that points to rt_9Hk3Ws7PzQAllowed, because the alias leads to a router in the list.
The router rt_9Hk3Ws7PzQAnother router, rt_2Lw6Yd8NcVRefused.
Any entryA model name with no router, such as anthropic/claude-sonnet-4.5 or claude-sonnet-4-5Refused. A limited key reaches models through its routers, and the Access editor notes that direct model access is off for the key.

For a fallback or weighted router, the list names the router itself, and the key then reaches every member through it. When an alias is repointed, the Aliases panel notes that keys limited to the alias follow it to its new router. See Alias.

Refusals

A limited key that names a router outside its list receives the same response as for a router that does not exist, which keeps the organization's other routers private. On Chat Completions the response is:

Response 404json
{
  "error": {
    "type": "invalid_request_error",
    "code": "model_not_found",
    "message": "the requested model or router was not found"
  }
}

The other endpoints use their own format with the same meaning:

EndpointStatusResponse
POST /v1/messages404Anthropic format, type not_found_error, message the requested model or router was not found
Claude Proxy Bedrock and Vertex endpoints404Claude Proxy not found

On Chat Completions and Messages, a disabled or missing router also returns this 404 to a limited key. A Claude Proxy or Pi Proxy in the key's list that has been disabled returns 403 with this Claude Proxy is disabled or this Pi Proxy is disabled.

What router access covers

Router access applies to inference requests. Managing routers through the API follows the key's control plane scopes, so a key with routers:read lists every router in the organization whatever its router list. See Scope.

API Access

Authentication

The headers that carry an API key, and the responses when authentication fails.

Authentication is how a request proves which API key it comes from. Most requests carry the key in the Authorization header as a bearer token. Requests through a Claude Proxy or Pi Proxy may carry the developer's own provider login in that header, so Multi-Router also reads the key from its own header, X-MR-Api-Token, and from x-api-key on Claude endpoints.

Headers

Multi-Router reads the API key from these headers:

HeaderFormatAccepted on
AuthorizationBearer sk-mr-…Every endpoint.
X-MR-Api-TokenBearer sk-mr-…Every endpoint. Used with a Claude Proxy or Pi Proxy when Authorization carries a provider login.
x-api-keysk-mr-…The endpoints in the next table.

The x-api-key header, as Anthropic clients send it, is accepted on these endpoints:

EndpointCondition
POST /v1/messagesAlways.
POST /v1/messages/count_tokensAlways.
Claude Proxy Bedrock endpoints under /bedrockAlways.
Claude Proxy Vertex endpoints under /vertexAlways.
GET /v1/modelsWith an X-MR-Router header naming a Claude Proxy. Without it, the key goes in Authorization.

Multi-Router reads the first of these headers present on the request, in the order X-MR-Api-Token, Authorization, x-api-key, and authenticates with that header alone. A request therefore carries the key in one header, and an Authorization header holding anything else, such as a provider login, is paired with X-MR-Api-Token. The value is Bearer or bearer, a single space, and the key, exactly as issued.

Examples

The same key works in either header, depending on the endpoint:

Authorization headerbash
curl https://api.multi-router.ai/v1/chat/completions \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "model": "multi-router/rt_9Hk3Ws7PzQ", "messages": [{ "role": "user", "content": "Hello" }] }'
x-api-key header on Messagesbash
curl https://api.multi-router.ai/v1/messages \
  -H "x-api-key: $MR_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{ "model": "multi-router/rt_9Hk3Ws7PzQ", "max_tokens": 256, "messages": [{ "role": "user", "content": "Hello" }] }'

The OpenAI and Anthropic SDKs send their API key setting in these headers, so an SDK works with Multi-Router by setting its base URL to https://api.multi-router.ai/v1 for OpenAI, or https://api.multi-router.ai for Anthropic, and its API key to the Multi-Router key. The Anthropic SDK lists models with x-api-key, so model listing through it takes an X-MR-Router header naming a Claude Proxy.

Keys on proxy requests

On a Claude Proxy with the Developer credential source and on a Pi Proxy, the Authorization and x-api-key headers carry the developer's provider login, and the Multi-Router key travels in X-MR-Api-Token. A Multi-Router key found in a header bound for the provider is refused with 400, which keeps the key inside Multi-Router. On Messages, token counting and Responses the message names the header; on the Bedrock and Vertex endpoints it is MR API token in forwarded header. See Agents & Editors.

Errors

A request whose key is missing, unknown, disabled or expired receives 401. The response is the same in every case, which keeps the organization's keys private:

EndpointStatusResponse
/v1 endpoints401{"error":"unauthorized"}
Pi Proxy OpenAI Codex endpoint401{"error":"unauthorized"}
Claude Proxy Bedrock and Vertex endpoints401invalid or missing API token, in the Bedrock or Vertex error format

A valid key that lacks a scope receives 403 insufficient_scope. See Scope.

API Access

Rate Limit

The number of requests each API key can send per minute, and the response when a key exceeds it.

A rate limit caps how many requests an API key can send in a period of time. Multi-Router applies a rate limit to every key, which protects the organization's provider accounts and budgets from a runaway script or a leaked key. The limit counts requests over a rolling 60-second window, so a key that reaches it can send again as soon as earlier requests fall outside the window.

Limits

Each API key has the following limits:

EndpointCounts toward
POST /v1/chat/completionsInference, 300 requests per minute
POST /v1/messagesInference, 300 requests per minute
POST /v1/responsesInference, 300 requests per minute
POST /backend-api/codex/responsesInference, 300 requests per minute
Claude Proxy Bedrock endpoints, including token countingInference, 300 requests per minute
Claude Proxy Vertex endpoints, other than token countingInference, 300 requests per minute
POST /v1/messages/count_tokensToken counting, 300 requests per minute
Claude Proxy Vertex token countingToken counting, 300 requests per minute

Token counting has its own limit, which keeps inference capacity free for requests to models. Each key has one inference count and one token counting count, shared by every endpoint in the row.

A workload that signs in with Machine Identity has the same limits for each machine binding and subject. See Machine Identity.

The 429 response

A request over the limit is refused before it reaches a provider:

Response 429json
{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded",
    "message": "per-token rate limit exceeded"
  }
}

On Messages the response uses the Anthropic error format with the same type and message, and on a Claude Proxy's Bedrock and Vertex endpoints the message is rate limit exceeded. The OpenAI and Anthropic SDKs retry 429 responses automatically with exponential backoff.

Other limits

Rate limits are one of several controls on usage, each with its own purpose and response:

ControlLimitsResponse
Rate limitRequests per key per minute.429 rate_limit_exceeded
BudgetSpending over a period, for the organization, chosen routers or router tags.429 budget_exhausted, with Retry-After
Provider rate limit on a static, fallback or weighted routerThe provider's own limits on the organization's provider key.Multi-Router retries, a fallback router moves to its next member, and otherwise the caller receives 429 upstream_rate_limit.
Provider rate limit on a Claude Proxy or Pi ProxyThe provider's own limits on the key or login in use.The provider's 429, passed through as sent.

See Budgets & Spend Control.

Observability

Overview

What Multi-Router records about each request, and where to see usage, latency, errors and cost.

Observability in Multi-Router is the record of every request that passes through a router: who sent it, which router and model served it, how long it took, how many tokens it used, what it cost, and whether it succeeded. The record is kept for each request individually, so a single response can be traced to the exact router, model, prompt revision and API key behind it, and the same records add up to usage, latency, error and cost figures for the organization.

What is recorded

Each request record holds the following fields:

GroupField
RequestRequest id
RequestTime
RequestOrganization
RequestAPI key id
RequestCaller's customer id
RequestCaller's session id
RequestCaller's correlation id
RequestPeer IP
RoutingAlias used
RoutingRouter
RoutingModel requested
RoutingModel served
RoutingProvider, variant and region
RoutingProvider's model id
PromptBound prompt and the revision or commit used
OutcomeStatus code
OutcomeError kind and error message
OutcomeAttempts
TokensInput and output tokens
TokensCached and cache-write tokens
TokensRequest and response sizes
CostTotal cost and its parts
LatencyTotal time and time to first token
LatencyTime at the provider and time spent in Multi-Router
Machine identityIssuer, subject and machine binding of a Machine Identity workload

Routers can also store the full request and response, called payloads, for some or all requests. See Request Logging.

Where to see it

Request records are shown on several dashboard pages, each for a different purpose:

PlaceShows
TrafficEvery request, with filters, views, stats, charts and CSV export.
Request DetailOne request in full, opened from Traffic, with its payloads when they were stored.
Routers pageRequests, errors and p95 latency over the last 24 hours for the organization, and requests over the last 24 hours for each router.
Budget usageSpend over time, by router and by model, for each budget. See Budgets & Spend Control.

What decides what is recorded

Each router's Request Logging settings decide how much it records: full request metadata, metadata without fields that identify end users, failed requests only, or nothing, and whether payloads are stored. An administrator's Storage Policy sets the most any router can record. See Request Logging.

Cost

Multi-Router prices each request when it completes, from the price of the model offering that served it: a catalog offering, or a custom offering with its own prices. The cost is stored with the request, so totals in Traffic and budgets reflect the prices at the time of each request. See Cost.

Tracing a request

Every response carries an x-mr-request-id header with the request's id, which finds the request in Traffic. Callers can also send their own session and correlation ids, which Traffic records and searches, to tie requests to their own logs. See Request Tracing.

The other pages in Observability cover each part in detail:

PageCovers
TrafficViews, filters, columns, stats, charts and export.
Request DetailEvery field of a request, errors, and payloads.
Request LoggingWhat each router records, and the Storage Policy.
CostHow each request is priced.
Request TracingRequest, session and correlation ids, and response headers.

Observability

Traffic

The Traffic page: views, time ranges, filters, columns, stats, charts and CSV export.

Traffic is the dashboard page that lists every request the organization's routers recorded, newest first. It opens from Traffic in the top navigation, showing the last 24 hours. Views, time ranges and filters narrow the list, a strip of stats and charts summarizes the requests in view, and any request opens in place to show its full detail.

Views

Engineering · org_7Tq2Wd9KxP

Overview · Traffic

CustomizeSave view
Time:
1h24h7d30d
CustomFilters
Search any id (request/correlation/session)…
Export
Requests
128,403
Success rate
99.4%
5xx errors
142
Tokens
84.2M
Latency · p95
1,240ms
Latency · p99
3,180ms
Cost
$1,284.50
Customers
312
Requests · Count / time128,403
Outcome · 5xx / time142
Latency · p95 / time1,240ms
Cost · Total / time$1,284.50
Requests
Page 125 / page
Columns
DateTimeRequest IDRouterAliasModelVendorIn/OutCostLatencyStatus
2026-09-3014:32:11req_Lw4Tn8Vb2ksupport-bot—anthropic/claude-sonnet-4.5anthropic2,310/512$0.011.94s200
2026-09-3014:32:09req_9Pc1RmYe4srt_9Hk3Ws7PzQ—anthropic/claude-haiku-4.5bedrock860/210$0.0021640ms200
2026-09-3014:32:07req_8Fq2LmXo1crt_9Hk3Ws7PzQ—anthropic/claude-haiku-4.5bedrock1,240/380$0.0035808ms200
2026-09-3014:32:04req_3Zr6KdWq8nclaude-code-eng—anthropic/claude-sonnet-4.5vertex5,120/1,034$0.033.21s200
2026-09-3014:32:02req_7Hb5NsTq3wdraft-classifier—openai/gpt-5-miniopenai640/42$0.0002410ms200
2026-09-3014:32:00req_2Kd8VxRe6mclaude-code-eng—anthropic/claude-opus-5.5anthropic12,480/2,206$0.128.42s200
2026-09-3014:31:58req_6Ym2HcPa5tsupport-bot—anthropic/claude-sonnet-4.5anthropic0/0$0212ms429
2026-09-3014:31:55req_5Wt9GpLc1zdraft-classifier—openai/gpt-5.4openai0/0$012.7s502
2026-09-3014:31:52req_4Qn3BvYk7rrt_9Hk3Ws7PzQ—anthropic/claude-haiku-4.5bedrock910/264$0.0024690ms200
2026-09-3014:31:49req_8Jv1MzDs2psupport-bot—anthropic/claude-sonnet-4.5anthropic3,045/688$0.022.48s200
The Traffic page with the Overview view: the views rail, time range and filters, stats, charts and the Requests table. Select to enlarge.

A view is a saved combination of filters, time range, stats and charts. The views rail on the left lists the built-in views and the person's own saved views:

ViewStats and charts for
OverviewRequest volume, success, latency and cost across all requests.
LatencyTotal latency, time to first token and the time spent at the provider.
ErrorsError rates and error kinds.
CostTotal cost and its parts.
By modelRequests, latency and cost broken down by model.
CustomersUsage broken down by the caller's customer id.

A built-in view sets the stats and charts and applies to every request in the time range and filters. Save view stores the current filters, time range, stats and charts as a saved view, which can be renamed or deleted. Changing a saved view marks it Modified, and saving updates it. A saved view keeps the length of its time range, such as the last 7 days, and applies it from the moment it is opened.

Time range

The time range sets which requests the page shows. The Time control offers 1h, 24h, 7d and 30d, and Custom offers these ranges:

RangeKind
Last 15 minutesRelative
Last 1 hourRelative
Last 6 hoursRelative
Last 24 hoursRelative
Last 7 daysRelative
Last 30 daysRelative
TodayCalendar
YesterdayCalendar
This weekCalendar
Last weekCalendar
This monthCalendar
Last monthCalendar

The page shows up to 30 days of requests. It loads when the range, filters or page size change, and a new choice of range brings in the latest requests.

Filters

Filters narrow the requests to those that match every filter set. The Filters menu offers:

FilterMatches
ModelThe model that served the request.
RouterThe router the request entered through, or the member router that served it, by id.
ProviderThe provider that served the request.
StatusAny, 2xx, 4xx or 5xx.
StreamingAny, Yes or No.
Error kindThe error kind exactly as recorded, such as rate_limit_error or timeout. See Request Detail.
CustomerThe caller's customer id, such as tenant_acme.
SessionThe session id, exactly as sent in x-mr-session-id.
CorrelationThe correlation id, exactly as sent in x-mr-correlation-id.
Machine issuerThe identity provider of a Machine Identity workload.
Machine subjectThe workload identity, such as a service account.
Machine bindingThe machine binding the workload matched.
Router typeAny, llm, or fallback.

The search box finds a request by any of its ids: the request id, a correlation id or a session id, matched exactly.

Requests table

The Requests table lists one request per row, 50 per page by default, with 25 and 100 also available, and the page buttons move to the first, previous, next and last page. Selecting a request id opens the request in place. See Request Detail. The Columns button chooses which columns appear, and the choice is remembered in the browser:

ColumnShown by defaultShows
DateYesThe date of the request.
TimeYesThe time of the request.
Request IDYes, alwaysThe request id, which opens the request.
RouterYesThe router that served the request.
AliasYesThe alias the request used, when it used one.
ModelYesThe model that served the request.
VendorYesThe provider that served the request.
In/OutYesInput and output tokens.
CostYesThe cost of the request.
LatencyYesTotal latency.
StatusYesThe status code.
TypeNoThe router type.
VariantNoThe offering variant, such as standard or global.
RegionNoThe region that served the request.
Wire idNoThe provider's own model id.
CustomerNoThe caller's customer id.
Identity typeNoThe kind of Machine Identity workload, when one made the request.
Machine issuerNoThe identity provider of the workload.
Machine subjectNoThe workload identity.
Machine bindingNoThe machine binding the workload matched.
AttemptsNoHow many attempts the request took.

Stats and charts

The stat strip and chart strip above the table summarize the requests in view. Customize chooses up to 8 stats and 4 charts, in any order, and a chart can be broken down by a dimension, such as latency by model. Latency metrics offer p50, p90, p95, p99, maximum and average; throughput offers average and p95; attempts offer average, maximum and p95. The metrics are:

MetricGroup
RequestsRequests
Success %Outcome
Error rateOutcome
4xxOutcome
5xxOutcome
Rate-limitedOutcome
RetryableOutcome
Recovered %Outcome
Substituted %Outcome
Upstream error rateOutcome
Upstream 5xxOutcome
Total latencyLatency
Time to first tokenLatency
Upstream latencyLatency
Time spent in Multi-RouterLatency
Total tokensTokens
Input tokensTokens
Output tokensTokens
Cache read tokensTokens
Cache create tokensTokens
Cache hit %Tokens
Tokens per requestTokens
Throughput, output tokens per secondThroughput
Total costCost
Cost per requestCost
Cost per 1,000 tokensCost
Input, output, cache read and cache write costCost
AttemptsAttempts
Multi-attempt %Attempts
Distinct models, providers, routers, customers, sessions and error kindsDistinct
Busiest model, provider, router, customer and error kindBusiest

A chart can break down by one of these dimensions:

Dimension
Model
Provider
Router
Customer
Error kind
Session
Machine issuer
Machine subject
Machine binding
Router type

Export

Export downloads the requests that match the current time range and filters as a CSV file named for the range, newest first, with up to 100,000 rows. A larger export ends with the line # truncated at 100000 rows, and a narrower time range or more filters fetch the rest. The file has these columns:

CSV columnstext
timestamp, request_id, alias_name, router_id, leaf_router_id, model_served, provider,
status_code, prompt_tokens, completion_tokens, cost_usd_micros, latency_total_ms, attempts,
customer_user, router_type, classifier_type, classifier_version, calibration_version,
difficulty_score, raw_complexity, task_type, classify_latency_ms, short_input_floored,
model_group, ladder_size, rung_index, first_choice_model, token_estimate, has_code,
turn_count, gateway_platform, variant, region, model_wire_id, identity_kind, issuer_id,
subject, binding_id

cost_usd_micros is the cost in millionths of a US dollar, so 1500 is $0.0015. router_id is the router the request entered through, and leaf_router_id is the member router that served it. The columns from classifier_type to turn_count are kept for older records and are empty for current requests.

Observability

Request Detail

Every field of a single request, its errors, and its stored payloads.

Request detail is the full record of one request, opened by selecting its request id in Traffic. The header shows the request id, status, model and provider, and a row of tiles gives the Latency, Cost, Tokens, time to first token, Attempts and Router overhead. Sections below give every recorded field, and when the router stored payloads, Full Request Payload opens the request and response themselves.

Request

Engineering · org_7Tq2Wd9KxP

Overview · Traffic

CustomizeSave view
Time:
1h24h7d30d
CustomFilters
Search any id (request/correlation/session)…
Export
Requests
128,403
Success rate
99.4%
5xx errors
142
Tokens
84.2M
Latency · p95
1,240ms
Latency · p99
3,180ms
Cost
$1,284.50
Customers
312
Requests · Count / time128,403
Outcome · 5xx / time142
Latency · p95 / time1,240ms
Cost · Total / time$1,284.50
Requests
Page 125 / page
Columns
DateTimeRequest IDRouterAliasModelVendorIn/OutCostLatencyStatus
2026-09-3014:32:11req_Lw4Tn8Vb2ksupport-bot—anthropic/claude-sonnet-4.5anthropic2,310/512$0.011.94s200
2026-09-3014:32:09req_9Pc1RmYe4srt_9Hk3Ws7PzQ—anthropic/claude-haiku-4.5bedrock860/210$0.0021640ms200
2026-09-3014:32:07req_8Fq2LmXo1crt_9Hk3Ws7PzQ—anthropic/claude-haiku-4.5bedrock1,240/380$0.0035808ms200
req_8Fq2LmXo1c200anthropic/claude-haiku-4.5bedrock
Full Request Payload →
Latency
808ms
Cost
$0.0035
Tokens
1,240 → 380
TTFT
402ms
Attempts
1
Router overhead
1.2ms
Request
Request ID
req_8Fq2LmXo1c
Timestamp
14:32:07
Organization
org_7Tq2Wd9KxP
Token
tok_4Rf8zLm2Qa
Correlation ID
order-4411
Customer
—
Session
—
Peer IP
203.0.113.24
Routing
Alias
—
Router ID
rt_9Hk3Ws7PzQ
Model requested
anthropic/claude-haiku-4.5
Model served
anthropic/claude-haiku-4.5
Vendor
bedrock
Variant
geo
Region
us-east-1
Wire id
us.anthropic.claude-haiku-4-5-20251001-v1:0
Prompt binding
Prompt ID
—
File path
—
Revision
—
Latency
Total
808ms
TTFT
402ms
Request upload
180µs
Router overhead
1.2ms
Upstream
806ms
Router response
240µs
Tokens
Prompt
1,240
Completion
380
Request bytes
6,412
Response bytes
2,186
Cost
Total
$0.0035
Input
$0.0014
Output
$0.0021
Cache read
$0
Cache write
$0
Provider response
Request upload
180µs
Router · overhead
1.2ms
Upstream200
806ms
Router · response
240µs
Total 807.6ms
2026-09-3014:32:04req_3Zr6KdWq8nclaude-code-eng—anthropic/claude-sonnet-4.5vertex5,120/1,034$0.033.21s200
2026-09-3014:31:58req_6Ym2HcPa5tsupport-bot—anthropic/claude-sonnet-4.5anthropic0/0$0212ms429
A request opened in Traffic: the summary tiles, the Request, Routing, Prompt binding, Latency, Tokens and Cost sections, and the latency breakdown. Select to enlarge.

The Request section identifies the request and its caller:

FieldShows
Request IDThe request id, such as req_7Kd2Qx9WmB4Tn6Ls, also returned in x-mr-request-id.
TimestampWhen the request arrived.
OrganizationThe organization id.
TokenThe id of the API key that made the request, such as tok_4Gm8Rq2VxN.
Subject typeThe kind of Machine Identity workload, when one made the request.
Machine issuerThe identity provider of the workload.
Machine subjectThe workload identity.
Machine bindingThe machine binding the workload matched.
Correlation IDThe caller's x-mr-correlation-id.
CustomerThe caller's customer id.
SessionThe caller's session id.
Peer IPThe address the request came from.

Routing

The Routing section shows how the request was served:

FieldShows
AliasThe alias the request used, linked to the router.
Router IDThe router that served the request, linked to its page. For a fallback or weighted router, this is the member that served it.
Model requestedThe model named in the request.
Model servedThe catalog model that answered, such as anthropic/claude-sonnet-4.5.
VendorThe provider that served the request.
VariantThe offering variant, such as standard or geo.
RegionThe region that served the request.
Wire idThe provider's own model id, such as claude-sonnet-4-5-20250929.

Prompt binding

The Prompt binding section shows the bound prompt the router applied, with dashes when it applied none. It shows the Prompt ID, File path and Revision used; for a GitHub prompt it also shows the Repository, and the revision is the commit, linked to the file at that commit on GitHub. See Prompt Binding.

Latency

The Latency section splits the request's total time into its parts, and the Provider response chart shows the same parts as a timeline:

FieldMeasures
TotalFrom the request arriving to the response finishing.
TTFTTime to first token: from the request arriving to the first streamed output.
Request uploadReceiving the request body from the caller.
Router overheadMulti-Router preparing the request before sending it to the provider.
UpstreamThe provider handling the request.
Router responseMulti-Router finishing the response after the provider.

Tokens

The Tokens section shows Prompt tokens, with how many were read from the provider's prompt cache and how many were written to it, by cache lifetime, and Completion tokens. It also shows the request and response sizes in bytes.

Cost

The Cost section shows the Total and its parts: Input, Output, Cache read and Cache write, by cache lifetime, and Web search and File search when the request used those tools. See Cost.

Errors

A failed request shows an error card with its category, a readable description and hint, the provider's status code when the provider answered, the error kind and the error message. The categories are Request error, Config error, Upstream error and Other error. Requests refused by Multi-Router's per-key rate limit or by a budget are answered before a record is written, so they appear in the caller's logs and not in Traffic. The error kinds recorded are:

Error kindLabelMeaning
invalid_requestInvalid requestThe request was malformed, or Multi-Router refused it before sending, such as for a model the router does not serve.
auth_errorAuthentication errorThe provider refused the credential.
model_not_foundModel not foundThe provider reported that the requested model is not available.
config_errorConfiguration errorThe router is set up in a way that cannot serve the request, such as a missing provider key.
rate_limit_errorRate limitedThe provider's rate limit was reached.
timeoutTimeoutThe provider took too long to respond.
upstream_errorUpstream errorThe provider returned an error.
cancelledCancelledThe request was cancelled before it finished.
client_disconnectclient_disconnectThe caller closed the connection before the response finished.
metering_failedmetering_failedThe response finished, and its token usage could not be read.
internal_errorinternal_errorMulti-Router hit an internal error while serving the request.

Payloads

Payloads are the full request and response, stored when the router's Request Logging settings capture them. Full Request Payload in the detail header opens them; a newly finished request shows Full Request Payload available in a few minutes, with a countdown, while its payload is being stored. The payload viewer shows:

PartContents
Client requestThe request exactly as the caller sent it.
Provider RequestThe request as Multi-Router sent it to the provider, with any bound prompt applied. Shown when it differs from the client request.
ResponseThe response returned to the caller.

Each part opens as a tree or as raw text, Split view shows two parts side by side, and Headers shows the headers of each part, with credentials such as Authorization replaced by [redacted]. Copy copies a body, and a badge marks a body that was decompressed or truncated. See Request Logging.

Observability

Request Logging

What each router records about its requests, and the Storage Policy that caps it.

Request logging is the setting on each router that decides what Multi-Router records about the router's requests. It has two parts: request metadata, which is the request record shown in Traffic, and payloads, which are the full request and response. An administrator's Storage Policy sets the most any router can record, and each router records at or below that level. A router that has no setting of its own follows the Storage Policy.

Request metadata

Request metadata is the request record shown in Traffic. The Request Metadata setting has these levels:

LevelDashboard descriptionRecords
Full MetadataComplete request metadata is logged.Every field of every request.
Anonymous MetadataAll request metadata except user-identifying fields are logged.Every request, with the customer id, session id and peer IP left empty.
Failures onlyOnly failed requests are logged.Requests that returned a status of 400 or above, or recorded an error during the response.
NoneRequest logging for this router is disabled.No records. Traffic and its stats leave out the router's requests.

Budget enforcement counts every request whatever the router logs. The spend shown on the Budgets pages comes from request records, so it includes a router's requests when the router records them. Anonymous Metadata suits routers that serve end users, keeping usage, latency and cost complete while leaving the identifying fields empty; payloads, when stored, hold the request as sent, including any customer id in its body.

Payloads

The Request and Response Payloads setting decides which requests have their full request and response stored:

LevelStores
All PayloadsEvery request and response.
SampleA chosen percentage of requests, up to the administrator's limit.
Failures onlyRequests that returned a status of 400 or above.
NonePayload storage off.

Payloads depend on metadata: with metadata at None, payload storage is off, and with metadata at Failures only, payloads are stored for failures at most. Sampling picks requests by their request id, so each request's place in the sample is fixed, and the share matches the percentage over many requests.

Setting it in the dashboard

Back to Routers

Support bot

enabledstatic
productionsupportAdd tag

Answers customer questions in the help center chat widget.

rt_9Hk3Ws7PzQ·created Mar 4, 2026·updated Sep 30, 2026
Disable
OverviewTrafficActivity

Aliases

Model

Prompt

Request Logging

What this router records about its requests. Capped by the administrator's Storage Policy.

Request Metadata
NoneRequest logging for this router is disabled.Failures onlyOnly failed requests are logged.Anonymous MetadataAll request metadata except user-identifying fields are logged.Full MetadataComplete request metadata is logged.
Request and Response Payloads
NoneRequest payload logging is disabled.Failures onlyRequest payloads are logged for failed requests only.SampleRequest payloads are logged for a sample of requests only.
Log10% of requests (up to the administrator limit of 10%).
All PayloadsAdministrator managedRequest payloads are logged for all requests.
Unsaved changesCancel Save

Settings

Per-router configuration. Persisted in the router's versioned config blob.

Editing a router's Request Logging: Anonymous Metadata with a 10% payload sample, and All Payloads above the administrator's limit. Select to enlarge.

The Request Logging panel on a router's page shows the current Request metadata and Request payloads. Edit opens both settings; levels above the administrator's Storage Policy are shown as Administrator managed and stay unselected. For a fallback or weighted router, logging follows each member router, and the panel says so; set logging on the members. See Router Settings.

Through the API, the levels are observability.metadata_level with the values full, anonymous, failures_only and none, and observability.request_capture with the values all, sample, error_only and none, with the sample share in observability.payload_sample_pct. Both settings also take inherit, the default, which follows the Storage Policy:

Anonymous metadata and a 10% payload samplebash
curl -X PATCH https://api.multi-router.ai/v1/routers/rt_9Hk3Ws7PzQ \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "observability": { "metadata_level": "anonymous", "request_capture": "sample", "payload_sample_pct": 10 } }'

Storage Policy

The Storage Policy is the administrator setting that caps what every router in the installation can record. Its traffic logging switch turns request records on or off for every router, and its mode sets the cap:

ModeMost a router can record
Anonymous request metadata (usage only)Anonymous Metadata, with payload storage off. This is the default.
All request metadataFull Metadata, with payload storage off.
Store everythingFull Metadata and payloads, up to the administrator's payload capture level and sample percentage.

With the default mode, Traffic holds usage, latency and cost for every request with the customer id, session id and peer IP left empty, and holds usage figures only.

How payloads are protected

Payloads are stored encrypted, separately for each organization, and are read only through the organization's own Traffic page. Before storage, credential headers, which are Authorization, x-api-key, X-MR-Api-Token, anthropic-api-key, cookie, set-cookie and any header whose name contains token, key, secret, password or auth, are replaced with [redacted]. A body larger than 32 MiB is stored up to that size and marked truncated.

Fields by level

The levels differ in which fields they keep:

FieldFull MetadataAnonymous Metadata
Routing, model, provider, regionKeptKept
Tokens, cost, latency, status, errorsKeptKept
API key idKeptKept
Correlation idKeptKept
Customer id (user or metadata.user_id)KeptEmpty
Session idKeptEmpty
Peer IPKeptEmpty

Observability

Cost

How Multi-Router prices each request, and what each part of the cost covers.

Cost is the price of a request, worked out when the request completes from the tokens it used and the price of the model offering that served it. Multi-Router stores the cost with the request, so Traffic, exports and budgets all show the same figure, and a later price change applies to later requests while earlier requests keep the price they were served at.

Where prices come from

Each model offering in the catalog has prices per million tokens for input, output, cache reads and cache writes, and some offerings add prices for long prompts, faster service tiers and tool use. A request is priced at the offering that served it: the model, provider, variant and region in its record. A custom offering, such as a copy of a catalog offering with a negotiated rate, has its own prices, and a router that uses it has its requests priced at them. See Model Catalog and Custom Models & Pricing.

Parts of the cost

The cost of a request is the sum of these parts:

PartTokens or unitsPrice
InputPrompt tokens, less the cached and cache-write tokensInput price
OutputCompletion tokensOutput price
Cache readPrompt tokens read from the prompt cacheCache read price
Cache writePrompt tokens written to the prompt cache, for each cache lifetimeCache write price for that lifetime
Web searchWeb searches the model ranWeb search fee per search
File searchFile searches the model ranFile search fee per search

Example

This example prices a request that reads most of its prompt from the cache. A request to an offering priced at $3.00 per million input tokens, $15.00 per million output tokens and $0.30 per million cache-read tokens sends 12,000 prompt tokens, of which 10,000 are read from the cache, and receives 800 completion tokens:

PartCalculationCost
Input2,000 × $3.00 / 1,000,000$0.0060
Cache read10,000 × $0.30 / 1,000,000$0.0030
Output800 × $15.00 / 1,000,000$0.0120
Total$0.0210

Long prompts and service tiers

Some offerings charge different rates when a prompt reaches a set number of tokens, counting cached and cache-write tokens, and every token part of the request is then priced at the long-context rates; web and file search fees stay the same. Some providers also offer faster or slower service tiers, such as priority or flex, at their own rates; when the provider reports the tier that served a request, the request is priced at that tier's rates, or at $0 when the offering has no price for that tier.

Unpriced usage

When an offering has no price for a part of the cost, such as a model with no catalog price or a missing cache write price, that part is recorded at $0. Pi Proxy requests to Anthropic and OpenAI Codex are recorded at $0. The Budgets page shows the share of unpriced traffic as Unpriced traffic. A router can be moved to a custom offering with prices, which prices its later requests.

Where cost appears

The stored cost of each request appears across the dashboard and in exports:

PlaceShows
Traffic Cost columnThe total cost of each request.
Request detailThe total and each part. See Request Detail.
Traffic statsTotal cost, cost per request, cost per 1,000 tokens, and the cost split.
CSV exportcost_usd_micros, in millionths of a US dollar.
Budget usageSpend for each budget over time, by router and by model.

Observability

Request Tracing

Request, session, correlation and customer ids, and the response headers that describe how a request was served.

Request tracing ties a request in Multi-Router to the caller's own systems. Every response carries the request's id, and callers can attach their own ids, for a conversation, a job or an end customer, which Traffic records, filters and searches. Response headers also report how the request was served, such as the provider and the number of attempts, so an application can log them with its own records.

Ids

An id is a value that names a request, or a group of requests, in Traffic. A request carries these ids:

IdSet bySent asUse
Request idMulti-RouterReturned in x-mr-request-idFinds one request in Traffic.
Session idThe callerx-mr-session-id request headerGroups the requests of one conversation or agent session.
Correlation idThe callerx-mr-correlation-id request header, returned in the responseMatches a request to an entry in the caller's own logs.
Customer idThe callerThe user field on Chat Completions, or metadata.user_id on MessagesAttributes usage to the caller's end customer or tenant.

Traffic records the first 200 characters of a session or correlation id and matches them exactly, and the response returns the correlation id in full. Claude Code sends its own session id, which Traffic records when no x-mr-session-id is sent. With Anonymous Metadata, the router leaves the session id and customer id empty. See Request Logging.

A request with session, correlation and customer idsbash
curl https://api.multi-router.ai/v1/chat/completions \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "x-mr-session-id: conv_8812" \
  -H "x-mr-correlation-id: job-2026-10-01-0042" \
  -d '{
    "model": "multi-router/rt_9Hk3Ws7PzQ",
    "user": "tenant_acme",
    "messages": [{ "role": "user", "content": "Summarize ticket 4521." }]
  }'

Response headers

Response headers describe how Multi-Router served a request. Every response carries x-mr-request-id, and x-mr-correlation-id when one was sent. Successful responses from Chat Completions and Messages also carry the rest:

HeaderValue
x-mr-request-idThe request id, on every response including errors.
x-mr-correlation-idThe caller's correlation id, when one was sent.
x-mr-providerThe provider that served the request.
x-mr-upstream-modelThe catalog model that served the request.
x-mr-upstream-wire-idThe provider's own model id.
x-mr-regionThe region that served the request, when known.
x-mr-attemptsHow many attempts the request took.
x-mr-composerThe fallback or weighted router, when one handled the request.
x-mr-policyHow the router was chosen. The values are in the next table.
x-mr-model-deprecatedtrue when the model served is deprecated.
x-mr-model-replaced-byThe model that replaces a deprecated model.
x-mr-policyThe router was chosen by
staticA router id in the model field.
aliasAn alias in the model field.
headerThe X-MR-Router header.
fallbackA fallback router.
weightedA weighted router.
passthroughA provider and model name in the model field, such as anthropic/claude-sonnet-4.5.
bareA model name on its own in the model field.

Claude Proxy responses also carry x-mr-upstream-wire-id, and Claude Proxy and Pi Proxy responses pass the provider's own headers through.

Finding a request

To find a request from an application log:

  1. Log the x-mr-request-id response header, or send an x-mr-correlation-id with each request.
  2. In Traffic, paste the id into the search box, with a time range that covers the request.
  3. Select the request id to open its detail. See Request Detail.

A session id finds every request of a conversation the same way, and the Customers view and the Customer filter show usage for one end customer.

Organizations & People

OverviewEnterprise

How accounts, organizations and people fit together in Multi-Router, and how access is decided.

Multi-Router organizes a company's work into an account and its organizations. The account holds the company's people, and organizations divide routers, keys, prompts and other resources between teams, such as Engineering and Support, so each team works in its own space. People are invited once to the account and given access to the organizations they work in, with a role in each.

Accounts and organizations

An account is the company's top-level space in Multi-Router. It holds the company's people and its named organizations, and it is also an organization itself, with its own routers, keys and other resources. Named organizations sit inside the account, each with its own resources. A small company can work entirely in the account; a larger one adds an organization per team.

LevelHoldsExample
AccountPeople, account-wide settings such as SSO and user provisioning, and its own resources.example-co
OrganizationIts own routers, API keys, provider keys, prompts, model groups, budgets and settings.Engineering, Support

People

A person is someone in the account, identified by their email. Each person has an account role, and access to any number of organizations. In each organization a person can have a dashboard role, which lets them work in the dashboard, Portal access, which lets them set up tools such as Claude Code for themselves, or both. See Roles and Access.

The access rule

Every access question in Multi-Router comes down to one rule: is the person an admin of the organization they have selected? The account counts as an organization in this rule, so an account admin is an admin of the account itself, and an organization admin manages that organization's people.

For example, Priya is an admin of Engineering and a member at the account level:

SelectedCan Priya manage its people?
EngineeringYes. She is an admin of Engineering.
The accountNo. She is a member of the account.

Selecting an organization

The dashboard always works in one selected organization, or the account itself. The profile menu at the top right shows the account and its organizations, and choosing one switches the dashboard to it: the Routers, Prompts, Traffic and other pages then show that organization's resources. See Organizations.

The other pages in Organizations & People cover each part in detail:

PageCovers
OrganizationsCreating, switching, renaming and deleting organizations.
Roles and AccessAccount roles, dashboard roles, Portal access, and what each allows.
Inviting PeopleInvitations, the access they grant, and accepting them.
Account RosterManaging everyone in the account.
Organization PeopleManaging one organization's people.
Person LifecyclePerson states, suspending and offboarding.
Single Sign-OnSigning in through the company's identity provider.
User Provisioning (SCIM)Managing people from the identity provider.

Organizations & People

OrganizationsEnterprise

Creating, switching, renaming and deleting organizations, and what belongs to each.

An organization is a space inside the account for one team's work, with its own resources and its own people. Organizations are managed on the Organizations page, opened from Manage Organizations in the profile menu, and the profile menu switches between them.

What belongs to an organization

Each organization, and the account itself, holds its own copy of these resources, and a request with an API key reaches the resources of the key's organization:

ResourcePage
Routers and aliasesRouting
API keysAPI Key
Provider keys, with a default key per providerProvider Key
Prompts and prompt reposPrompts
Model groupsModel Group
Tools policiesSelf-Service Provisioning
BudgetsBudgets & Spend Control
Traffic and saved viewsTraffic
Default regionThe fallback region for Bedrock requests, on the Organizations page.

People, single sign-on and user provisioning belong to the account and span its organizations. A provider key can be moved between organizations of the same account. See Provider Key.

Switching organizations

The profile menu lists the account and the organizations where the person has a dashboard role, and choosing one reloads the dashboard in it. The Organizations page lists the named organizations with switch on each row, and marks the selected one as active. Multi-Router remembers the last named organization selected and opens it at the next password sign-in. A person who owns an account and also belongs to another sees the two under Your account and Shared with you.

Creating an organization

  1. Open Manage Organizations from the profile menu, and select Create organization.
  2. Enter a Name of 2 to 80 characters, such as Engineering. Names can repeat.
  3. Keep Switch to this organization after creation checked to start working in it, and select Create.

The person who creates an organization becomes its admin. An account holds up to 25 named organizations, in addition to the account itself. Each organization has an id starting with org_, shown on the Organizations page.

Renaming an organization

The rename action on the organization's row edits its name in place, with the same 2 to 80 character rule. The id stays the same, so API keys and integrations keep working.

Deleting an organization

The delete action on the organization's row removes it, after typing its name to confirm. An organization is deleted once it is empty: when it still has routers, API keys, provider keys, prompt repos, inline prompts or people other than the person deleting it, the page lists them to remove first. Deleting the selected organization switches the dashboard to the account. The account itself is permanent.

Default region

The default region is the fallback region for providers that need one, such as Amazon Bedrock, used when neither the router nor the provider key sets a region. It is set in the Default region card on the Organizations page, for the selected organization. See Regions & Variants.

Organizations & People

Roles and AccessEnterprise

Account roles, dashboard roles and Portal access, and what each allows.

A person's access is made of an account role and, for each organization they belong to, a dashboard role, Portal access, or both. The account role covers the account itself; the dashboard role and Portal access cover one organization each. Together they decide where the person can sign in and what they can manage.

Account roles

The account role is the person's role in the account itself:

Account roleAllows
OwnerEverything an admin can do. The owner created the account, and their account role stays Owner.
AdminManaging the account's people on the Account Roster, creating organizations and tools policies, and account settings such as single sign-on, user provisioning and machine identity issuers. Working in the account's own resources.
MemberWorking in the account's own resources, such as its routers and prompts.
No Operator AccessDashboard access through named organizations only, or the Portal only. Used for people who work in named organizations, or only use the Portal.

An account admin is an admin of the account in the access rule, and sees every person and organization in the account.

Dashboard roles

The dashboard role is the person's role in one named organization:

Dashboard roleAllows
AdminEverything a member can do, and managing the organization's people and their tools policies, budgets, webhook endpoints and GitHub connections, renaming and deleting the organization.
MemberWorking in the organization's resources: routers, prompts, API keys, provider keys, model groups and Traffic.
NonePortal access only, when the person has it there.

Portal access

Portal access lets a person set up tools such as Claude Code and Claude Desktop for themselves from the Portal, in one organization, under a tools policy that decides which tools and models they get. Portal access is separate from the dashboard role: a person can have either, both or neither in each organization. A person with only Portal access signs in to the Portal and is sent there from the dashboard. See Self-Service Provisioning.

Examples

Common combinations and what they give:

PersonAccount roleOrganization accessResult
Platform leadAdminAdmin in every organizationManages everyone and everything.
Team leadMemberAdmin in EngineeringManages Engineering's people and resources, and works in the account's resources.
EngineerNo Operator AccessMember in Engineering, with Portal accessWorks in Engineering in the dashboard, and sets up Claude Code from the Portal.
Support agentNo Operator AccessPortal access in SupportUses Claude Desktop set up from the Portal, and works only in the Portal.

Protections

A few changes are reserved, so that the account always has someone who can manage it:

ChangeRule
The account owner's account roleStays Owner.
Suspending or offboarding the account ownerReserved; the owner stays active.
Suspending or offboarding yourselfAnother admin does it.

Organizations & People

Inviting PeopleEnterprise

Inviting a person with the access they need, and how invitations are accepted.

An invitation adds a person to the account by email, with the access they will receive when they accept: an account role, and access to organizations. Account admins invite from the Account Roster; organization admins invite into their own organization from its People page. The person accepts by signing in with the invited email.

Inviting from the Account Roster

  1. Open Account Roster from the profile menu, and select Invite person.
  2. Enter the person's Name and Email.
  3. Choose their Account role, and whether they get Portal access in the account itself.
  4. Under Organization access, use Add to organization to add each organization, with a Dashboard role, Portal access and a Policy.
  5. Select Send invitation.

An invitation can grant access to up to 25 organizations. Each email has one waiting invitation at a time, and an email already in the account is given access from its panel on the roster.

Inviting into one organization

An organization admin invites from the organization's People page, with Invite person. The invitation sets access in that organization only: a dashboard role, Portal access with a policy, or both. When the person is already invited, the page adds the organization to their invitation; when they are already active in the account, it grants the access at once. With the account itself selected, its People page links to the Account Roster, where account access is managed.

The invitation email

Multi-Router emails the invitation, naming who sent it, the account, the access it grants and when it expires. Invitations expire after 7 days. The invited person also sees the invitation in Notifications once they sign in. When the email cannot be sent, the page says so, and the person can still accept from Notifications.

Accepting an invitation

  1. Open the link in the email, or the invitation in Notifications.
  2. Sign in, or create your login, with the invited email address.
  3. Review the access under Your access, and select Accept, or Decline.

The dashboard then opens in the first organization the invitation grants, or the account. An invitation that grants only Portal access is accepted in the Portal. An invitation is accepted with the email it was sent to; signed in with another address, the page shows Wrong address. In an account with single sign-on, signing in through SSO accepts a waiting invitation. See Signing In.

Managing invitations

Invited people appear on the Account Roster as Invited, or Invite expired after 7 days. Resend invite sends the email again, and Cancel invite withdraws it. Organization admins can change or withdraw the organization's part of an invitation from their People page.

Organizations & People

Account RosterEnterprise

The account-wide list of people, and editing a person's access.

The Account Roster is the list of everyone in the account, with their state, role and access in every organization. It opens from Account Roster in the profile menu for account admins, with the title People and the subtitle Everyone in this account. Each person opens in place to show and edit their access, agents and activity.

The list

Tiles across the top count People, Admins, Portal users and Pending invites. The list searches, filters and sorts across the whole account:

ControlOptions
SearchName or email.
StatusAll, Active, Invited, Invite expired, Suspended or No access.
SourceDirectory or Added by admin, with user provisioning.
AccessAdmin, Member, No Operator Access or Portal access.
OrganizationOne organization, or all.
SortName A–Z, Name Z–A, Recently added or Last sign-in.

With user provisioning connected, the page adds the Rules and Review tabs, the tiles become With access, In directory, Pending approval and Needs review, and Status also offers Not set up and Pending approval. See Rules.

A person's panel

People

Everyone in this account

Invite person
PeopleActivity
People
6
In this account
Admins
2
Can manage this roster
Portal users
1
Have agent portal access
Pending invites
1
0 expired
Search all people in this account
2550
Page 1 of 1 · 6 people

Search, filters and sort run across the whole account.

PersonStatus
AAisha Khanaisha@example.comActive

Identity

Email
aisha@example.com
Person ID
per_9Kd4Xv2QmT
Added
Aug 13, 2026
Last sign-in
Sep 29, 2026

Account access

Account role
Member
Portal access
Off
Admin · role

Organization access

2 of 2
OrganizationDashboard rolePortal accessPolicy
EngineeringMemberOnEngineering agents
Admin · role · Portal · policy
SupportMemberOff—
Admin · role

Agents

Set up by the person in the portal.

OrganizationPolicyCreatedLast usedStatus
EngineeringEngineering agentsSep 2, 2026Sep 30, 2026ActiveRotate keyRetire

Activity

  1. Policy set to Engineering agents in Engineering
    by Priya Shah · Sep 2, 2026, 10:14 AM PDT
  2. Portal access granted in Engineering
    by Priya Shah · Sep 2, 2026, 10:14 AM PDT
  3. Added to Support
    by Priya Shah · Aug 21, 2026, 3:40 PM PDT
  4. Invite accepted
    Aug 14, 2026, 9:05 AM PDT
  5. Added to Engineering
    by Priya Shah · Aug 13, 2026, 4:22 PM PDT
  6. Account created
    by Priya Shah · Aug 13, 2026, 4:22 PM PDT
View all activity

Lifecycle

Resend portal linkSuspendOffboard
DDana Leedana@example.comActive
LLena Parklena@example.comSuspended
MMarco Ruizmarco@example.comActive
PPriya ShahYoupriya@example.comActive
TTom Beckertom@example.comInvited
The Account Roster with one person open: their identity, account and organization access, agents, activity and lifecycle actions. Select to enlarge.

Opening a person shows these cards; Account access and Organization access are edited with their pencil and Save:

CardShows and edits
IdentityEmail, person id, when they were added and their last sign-in, with directory details when provisioned.
Account accessAccount role, and Portal access with a policy in the account itself.
Organization accessOne row per organization: Dashboard role, Portal access and Policy, with Add to organization and Remove from this organization.
AgentsThe tools the person set up from the Portal, by Organization, Policy, Created, Last used and Status, with Rotate key and Retire.
ActivityChanges to the person's access and state, with View all activity.
LifecycleResend invite, Cancel invite, Resend portal link, Suspend, Unsuspend, Give access and Offboard.

Changes that retire agents

Removing a person's Portal access, or the organization that holds it, retires the agents they set up there: their API key is deleted and their router removed. The panel lists the agents, and Save becomes Retire and save; for agents with recent traffic, it first asks to confirm with I understand these agents will stop working.

Activity

The Activity tab lists every change to people in the account, such as invitations, role changes, suspensions and directory updates, with who made it and when. A person's history stays in the activity log after they are offboarded.

Organizations & People

Organization PeopleEnterprise

Managing the people of one organization from its People page.

An organization's People page lists the people with access to the selected organization, and lets its admins invite people and change their access there. It opens from People in the top navigation, shown to admins of the selected organization and to account admins. Account-wide changes, such as account roles and suspension, are made on the Account Roster.

The list

The list shows each person's Name, Access, Policy and Lifecycle. Access reads Operator with their dashboard role, Portal, or No access. Search, filters and sort apply to the page of people shown. The rail filters the list:

ViewLists
Everyone in this orgEveryone with access to the organization.
Managed usersPeople with Portal access.
OperatorsPeople with a dashboard role.
PoliciesThe organization's tools policies.
InvitedPeople invited into the organization.
SuspendedSuspended people with access here.

Changing access

Opening a person shows the Access in this organization card with their dashboard role, Portal access and policy; Edit and Save change them. Setting Dashboard role to None and Portal access to Off removes their access here, and the button becomes Remove and save, or Retire and save when they have agents. Their account role and their access in other organizations are managed on the Account Roster.

Inviting into the organization

Invite person invites someone into this organization with a dashboard role, Portal access, or both. A new person joins the organization when they accept; a person already active in the account receives the access at once. See Inviting People.

Organizations & People

Person LifecycleEnterprise

The states a person moves through, and suspending, restoring and offboarding people.

A person's lifecycle is the set of states they move through in the account, from invitation to offboarding. Account admins change a person's state from the Lifecycle card on the Account Roster, and with user provisioning the directory changes it too. Every change is recorded in the account's activity.

States

A person is in one of these states:

StateMeaning
InvitedInvited, and the invitation is waiting.
Invite expiredThe invitation passed its 7 days. Resend invite sends a new one.
ActiveHolds access in the account or an organization.
No accessStays on the roster with every access removed. Give access restores it.
SuspendedSign-in blocked and agents stopped, with access kept for when they are restored.
Not set upAdded by the directory, waiting for access. With user provisioning.
Pending approvalAdded by the directory, with access waiting for approval. With user provisioning.

Suspending

Suspend stops a person's access at once while keeping it for later. They are signed out of the dashboard and the Portal, and their agents stop working, until an account admin selects Unsuspend. Their organization memberships and settings stay as they were. Suspension applies across the account, so organization admins ask an account admin to suspend or restore someone.

Offboarding

Offboard removes a person from the account permanently. It is available once the person is in No access with no active agents: remove their account access and every organization from their panel first, which also retires their agents. The person's name and history stay in the activity log. A person who returns is invited again. An invited person is removed with Cancel invite.

No access

Removing a person's last access leaves them in the account as No access, on the roster and ready to be given access again. A person with no access who signs in sees the page "You don't have access to Multi Router", which asks them to contact their administrator.

The account owner

The account owner stays Active with the Owner role, so the account always has someone who can manage it. Suspending and offboarding apply to everyone else.

Single Sign-On

OverviewEnterprise

Signing in to Multi-Router through your company's identity provider with OpenID Connect.

Single sign-on (SSO) lets people sign in to Multi-Router with their company account from the organization's identity provider, such as Okta, in place of a separate Multi-Router password. The identity provider decides who can sign in, so access follows the company's own controls, such as multi-factor authentication. With Require SSO turned on, the identity provider is the only way in, so a person removed there can no longer sign in once their session ends. SSO covers both the dashboard and the Portal.

How it works

Multi-Router connects to the identity provider over OpenID Connect. Each SSO connection belongs to the account and is tied to one email domain, such as example.com, that the company proves it owns. A sign-in follows these steps:

  1. The person enters their work email on the Multi-Router sign-in page.
  2. Multi-Router recognizes the email domain and shows Continue with SSO.
  3. The person signs in at the identity provider, which sends them back to Multi-Router.
  4. Multi-Router checks the identity provider's response and signs the person in to the organization the connection provisions into.

Parts of a connection

An SSO connection has these parts, each set on the Single Sign-On (SSO) page in Account Settings:

PartPurpose
Identity providerThe company identity provider, such as Okta.
Email domainThe domain whose people sign in through SSO, verified with a DNS record.
Connection detailsThe identity provider's issuer URL, and the client ID and client secret of the Multi-Router app created there.
New-user accessWhether people the identity provider signs in are added automatically, or only people already invited.
Default role and organizationThe role new people receive, and the organization they are added to.
Require SSOWhether password and social sign-in are turned off for the domain.

SSO and user provisioning

SSO signs people in; user provisioning with SCIM creates, updates and deactivates people in Multi-Router from the identity provider ahead of their first sign-in. SCIM builds on SSO: turning on user provisioning needs an SSO connection, and while provisioning is connected, the directory decides who has access. See User Provisioning (SCIM).

The other pages in Single Sign-On cover each step in detail:

PageCovers
Setting Up SSOEvery field, domain verification, testing and saving.
OktaCreating the Multi-Router app in Okta and connecting it.
Microsoft Entra IDRegistering Multi-Router in Entra ID.
Google WorkspaceCreating an OAuth client in Google Cloud.
Ping IdentityCreating an OIDC web app in PingOne.
JumpCloudCreating a custom OIDC application in JumpCloud.
Other OpenID Connect ProvidersConnecting any OpenID Connect provider.
Signing InWhat people see, first sign-in, and sessions.
Requiring SSOTurning off password and social sign-in for the domain.
TroubleshootingSign-in and setup errors, with causes and fixes.

Single Sign-On

Setting Up SSOEnterprise

Creating an SSO connection: the identity provider, email domain, domain verification, connection details and access settings.

An SSO connection is set up on the Single Sign-On (SSO) page, opened from Authentication (SSO) in Account Settings. The account has one connection. Setting it up takes an app in the identity provider, a DNS record that proves the company owns its email domain, and the connection details from that app.

Before you start

Setting up SSO needs the following:

ItemDetail
An OpenID Connect app in the identity providerA web app with the sign-in redirect URI https://dashboard.multi-router.ai/sso/callback, or the dashboard address of a self-hosted installation, whose ID token includes the email claim. See the page for each identity provider.
Access to DNS for the email domainTo add a TXT record that proves ownership.
The issuer URL, client ID and client secretFrom the identity provider app.

Steps

  1. Open Settings, then Authentication (SSO), and turn on Enable SSO.
  2. Choose the Identity provider and enter the Email domain, such as example.com.
  3. Enter the connection details: Issuer / Discovery URL, Client ID and Client secret.
  4. Choose New-user access, the Default role for new users, and the organization under Provision users into.
  5. Select Test connection, and then Set up SSO to save.
  6. Add the DNS TXT record shown under Verify domain ownership, then select Verify domain.

Sign-in through SSO starts once the domain is verified and the connection is on. Until then, people sign in as before.

Fields

example-co · acc_7Tq2Wd9KxP

Account Settings

Single Sign-On (SSO)

Let your team sign in through your company identity provider. One connection per organization.

Okta
example.com · OpenID Connect
Active
Identity provider
Oktavia OpenID Connect
Okta also supports SAML 2.0 (coming soon). Protocol is inferred from your provider.
Email domain
Users with this email domain will sign in via SSO.
✓ Domain ownership verified
Okta connection details
Issuer / Discovery URL
We auto-discover endpoints + signing keys. Redirect URI: /sso/callback.
Client ID
Client secret
✓ Configured
New-user access
Auto-provisionInvite-only
Anyone your IdP authenticates from example.com is created and added to your organization automatically.
Default role for new users
MemberAdmin
Members use routers, prompts, keys and traffic — they can’t manage members, billing, or SSO.
Provision users into
SSO users are added to this organization and land here when they sign in.
Require SSO for everyone
Disables password & social login for example.com. A break-glass admin login always stays available.
✓ Connection looks good
  • ✓Discovery document: issuer verified; authorization + token endpoints found
  • ✓Signing keys (JWKS): 2 signing key(s) available
Checks the issuer's discovery document + signing keys. Client ID/secret and the redirect URI are verified during an actual sign-in.
Test connectionEdit
A saved Okta connection for example.com: verified domain, connection details, new-user access, and a passing connection test. Select to enlarge.

The connection has these fields:

FieldDescription
Identity providerThe provider, named on the sign-in step as Your team uses Okta.
Email domainThe domain whose people sign in through SSO, such as example.com. A domain belongs to one Multi-Router account.
Also allow subdomain emailsIncludes subdomains such as eng.example.com. Turn it on only when the company controls every subdomain, since verification covers the main domain.
Issuer / Discovery URLThe identity provider's issuer, such as https://example.okta.com/oauth2/default. Multi-Router reads the provider's endpoints and signing keys from it.
Client IDThe client ID of the Multi-Router app in the identity provider.
Client secretThe client secret of that app. It is stored encrypted, and the page shows that one is configured without showing it.
New-user accessAuto-provision or Invite-only. See New-user access below.
Default role for new usersMember or Admin, the account role for people added by Auto-provision.
Provision users intoThe organization people are added to and land in when they sign in. The account itself is listed first.
Require SSO for everyoneTurns off password and social sign-in for the domain. See Requiring SSO.

The issuer must be a public https:// address, and it must match the issuer value in the provider's discovery document exactly. Multi-Router requests the openid, email and profile scopes.

Domain verification

Domain verification proves that the company owns the email domain, so that only the domain's owner can route its people through an identity provider. After the first save, the page shows a TXT record to add at the domain's DNS host:

TypeNameValue
TXT_multi-router-verify.example.commr-verify-…

Many DNS hosts add the domain to the name on their own, so the Name field takes _multi-router-verify alone. Verify domain checks for the record and shows Domain ownership verified when it is found. DNS changes can take a few minutes to appear; when the record is not found yet, wait and select Verify domain again. Changing the email domain creates a new record, and SSO sign-in pauses until it is verified.

Testing the connection

Test connection checks the Issuer / Discovery URL before or after saving. It reports two checks, Discovery document and Signing keys (JWKS), and shows Connection looks good when both pass; an issuer that is not a public https address shows the Issuer URL check instead. The client ID, client secret and redirect URI are checked during a real sign-in, so the last step of setup is signing in with SSO from a private browser window.

New-user access

New-user access decides what happens when someone signs in through SSO for the first time:

SettingBehavior
Auto-provisionAnyone the identity provider signs in from the domain is created and added to the organization under Provision users into, with the Default role for new users.
Invite-onlyOnly people already invited to the organization can sign in through SSO; their invitation is accepted on their first sign-in.

The default role is the account role new people receive: Member, or Admin, which makes them admins of the whole account, every organization and its SSO settings included, whichever organization they are provisioned into. Auto-provision with Member suits most companies, with the identity provider's app assignments deciding who can sign in. See Roles and Access.

Changing and removing the connection

After saving, the connection's fields are locked; Edit unlocks them and shows Remove connection, and Save changes or Cancel finishes. The toggle in the card header turns the connection on and off at once. Remove connection deletes the connection and its domain verification, and people of the domain then sign in with a password or social sign-in; people created through SSO set a password first. While user provisioning with SCIM is connected, the domain, new-user access and the connection itself stay fixed until provisioning is disconnected.

Single Sign-On

OktaEnterprise

Creating the Multi-Router app in Okta and connecting it to Multi-Router.

Okta connects to Multi-Router as an OpenID Connect web app. The app in Okta gives Multi-Router a client ID and client secret, and Okta's assignments decide which people can sign in. This page covers the Okta side; the Multi-Router side is on Setting Up SSO.

Creating the app in Okta

  1. In the Okta Admin Console, open Applications, then Applications, and select Create App Integration.
  2. Choose OIDC - OpenID Connect as the sign-in method and Web Application as the application type.
  3. Name the app, such as Multi-Router, and set Grant type to Authorization Code.
  4. Set Sign-in redirect URIs to https://dashboard.multi-router.ai/sso/callback.
  5. Under Assignments, choose who can use the app, such as a group of engineers, and save.
  6. Copy the Client ID and Client secret from the app's General tab.

Choosing the issuer

The issuer is the Okta authorization server that signs the app's tokens. Okta offers two, and the Issuer / Discovery URL in Multi-Router must match the one the app uses exactly:

Authorization serverIssuer
Default custom authorization serverhttps://example.okta.com/oauth2/default
Org authorization serverhttps://example.okta.com

The org authorization server is available on every Okta org, and the default custom server on orgs with API Access Management. Opening the issuer followed by /.well-known/openid-configuration in a browser shows its discovery document, and its issuer value, exactly as written, is the one to enter.

Connecting it to Multi-Router

On the Single Sign-On (SSO) page in Multi-Router:

  1. Choose Okta as the Identity provider and enter the company's email domain.
  2. Enter the issuer as the Issuer / Discovery URL, and the app's Client ID and Client secret.
  3. Choose New-user access and the organization, select Test connection, then Set up SSO.
  4. Verify the domain with the DNS TXT record the page shows.
  5. From a private browser window, sign in with an Okta user who is assigned to the app.

Assignments and access

Okta lets only assigned people sign in to the app, so assignments are the first control on who reaches Multi-Router. With Auto-provision, an assigned person is added to Multi-Router on their first sign-in. With Invite-only, they also need an invitation. Removing an assignment stops new SSO sign-ins, and with Require SSO on, every new sign-in; sessions already open end when they expire. To create and remove people in Multi-Router from Okta directly, connect user provisioning. See User Provisioning (SCIM).

Single Sign-On

Microsoft Entra IDEnterprise

Registering Multi-Router in Microsoft Entra ID and connecting it.

Microsoft Entra ID, formerly Azure AD, connects to Multi-Router as an app registration using OpenID Connect. The app registration gives Multi-Router a client ID and secret, and the matching enterprise application decides which people can sign in. This page covers the Entra side; the Multi-Router side is on Setting Up SSO.

Registering the app

The app is registered in the Microsoft Entra admin center:

  1. Open App registrations, under Entra ID, and select New registration.
  2. Name the app, such as Multi-Router, and choose Accounts in this organizational directory only.
  3. Under Redirect URI, choose Web and enter https://dashboard.multi-router.ai/sso/callback, then select Register.
  4. Copy the Application (client) ID and the Directory (tenant) ID from the Overview page.
  5. Open Certificates & secrets, add a New client secret, and copy its Value.

Client secrets expire, after 6 months by default and at most 24. Before a secret expires, add a new one and enter it as the Client secret in Multi-Router, so sign-in continues.

The email claim

Multi-Router reads each person's email from the ID token's email claim. In the app registration, open Token configuration, select Add optional claim, choose ID, and add email, accepting the Microsoft Graph email permission Entra offers. The claim carries each user's Mail attribute, so every person who signs in has a Mail value in the company's email domain, the domain set on the SSO connection.

The issuer

The issuer for an Entra tenant is https://login.microsoftonline.com/ followed by the tenant ID and /v2.0, such as https://login.microsoftonline.com/7c1f4a2e-5b3d-4e8f-9a6b-2d1c3e4f5a6b/v2.0.

Connecting it to Multi-Router

On the Single Sign-On (SSO) page in Multi-Router:

  1. Choose Microsoft Entra ID as the Identity provider and enter the company's email domain.
  2. Enter the issuer, the Application (client) ID as the Client ID, and the secret's Value as the Client secret.
  3. Choose New-user access and the organization, select Test connection, then Set up SSO.
  4. Verify the domain with the DNS TXT record the page shows.
  5. From a private browser window, sign in with an Entra user.

Assignments and access

To limit sign-in to chosen people, open Enterprise apps, under Entra ID, select the Multi-Router app, set Assignment required to Yes under Properties, and add users or groups under Users and groups. Entra then signs in only the assigned people. To create and suspend people in Multi-Router from Entra, connect user provisioning. See Microsoft Entra ID provisioning.

Single Sign-On

Google WorkspaceEnterprise

Creating an OAuth client for Multi-Router in Google Cloud and connecting it.

Google Workspace connects to Multi-Router through an OAuth client in a Google Cloud project owned by the Workspace organization. The OAuth client gives Multi-Router a client ID and secret, and an internal consent screen limits sign-in to the organization's own Google accounts.

The consent screen is set up once per Google Cloud project, in Google Auth Platform:

  1. In the Google Cloud console, open Google Auth Platform, shown as APIs & Services, then OAuth consent screen, in older consoles.
  2. Under Branding, enter the app name, such as Multi-Router, and a support email.
  3. Under Audience, choose Internal, so only accounts in the Workspace organization can sign in, and save.

The OAuth client

The OAuth client is the credential Multi-Router signs in with:

  1. Open Clients in Google Auth Platform, or Credentials, then Create credentials, then OAuth client ID, and create a client.
  2. Choose Web application, and name the client, such as Multi-Router.
  3. Under Authorized redirect URIs, add https://dashboard.multi-router.ai/sso/callback, and select Create.
  4. Copy the Client ID and Client secret.

Connecting it to Multi-Router

On the Single Sign-On (SSO) page in Multi-Router:

  1. Choose Google Workspace as the Identity provider and enter the company's email domain.
  2. Enter https://accounts.google.com as the Issuer / Discovery URL, and the Client ID and Client secret.
  3. Choose New-user access and the organization, select Test connection, then Set up SSO.
  4. Verify the domain with the DNS TXT record the page shows.
  5. From a private browser window, sign in with a Workspace account.

The internal consent screen admits every account in the Workspace organization. To admit only some people, use Invite-only in Multi-Router.

Single Sign-On

Ping IdentityEnterprise

Creating an OpenID Connect app for Multi-Router in PingOne and connecting it.

Ping Identity's PingOne connects to Multi-Router as an OIDC web app. The app gives Multi-Router a client ID and secret, and PingOne's access policies decide which people can sign in.

Creating the app in PingOne

The app is created in the PingOne admin console, in the environment that holds the company's users:

  1. Open Applications, then Applications, and add an application.
  2. Name it, such as Multi-Router, choose OIDC Web App, and save.
  3. On the Configuration tab, set Grant type to Authorization Code, add the redirect URI https://dashboard.multi-router.ai/sso/callback, and set Token endpoint authentication method to Client Secret Post.
  4. On the Resources tab, allow the openid, email and profile scopes.
  5. Turn the application on, and copy the Client ID, Client Secret and Environment ID.

The issuer

The issuer is https://auth.pingone.com/ followed by the environment ID and /as, such as https://auth.pingone.com/3b5c7d9e-1f2a-4b6c-8d0e-2f4a6b8c0d1e/as, with the regional domain, such as auth.pingone.eu, for environments outside North America. The app's Configuration tab shows it as the Issuer.

Connecting it to Multi-Router

On the Single Sign-On (SSO) page in Multi-Router:

  1. Choose Ping Identity as the Identity provider and enter the company's email domain.
  2. Enter the issuer, the Client ID and the Client Secret.
  3. Choose New-user access and the organization, select Test connection, then Set up SSO.
  4. Verify the domain with the DNS TXT record the page shows.
  5. From a private browser window, sign in with a PingOne user.

PingOne's application access settings, such as required groups, decide which people can sign in.

To create and suspend people in Multi-Router from PingOne, see Ping Identity provisioning.

Single Sign-On

JumpCloudEnterprise

Creating a custom OIDC application for Multi-Router in JumpCloud and connecting it.

JumpCloud connects to Multi-Router as a custom application with OpenID Connect sign-on. The application gives Multi-Router a client ID and secret, and the user groups bound to it decide which people can sign in.

Creating the application

The application is created in the JumpCloud admin portal:

  1. Open SSO Applications, select Add New Application, and choose Custom Application.
  2. Choose Manage Single Sign-On (SSO), with Configure SSO with OIDC, and name the application, such as Multi-Router.
  3. On the SSO tab, add the redirect URI https://dashboard.multi-router.ai/sso/callback, set Client Authentication Type to Client Secret Post, and enter https://dashboard.multi-router.ai/sso/start?domain=example.com, with the company's domain, as the Login URL.
  4. Under attribute mapping, include the email and profile scopes.
  5. On the User Groups tab, bind the groups whose members can sign in, and activate the application.
  6. Copy the Client ID and Client Secret shown on activation.

The issuer

JumpCloud's issuer is https://oauth.id.jumpcloud.com/. Enter it exactly as the issuer value in https://oauth.id.jumpcloud.com/.well-known/openid-configuration shows it, including its trailing slash.

Connecting it to Multi-Router

On the Single Sign-On (SSO) page in Multi-Router:

  1. Choose JumpCloud as the Identity provider and enter the company's email domain.
  2. Enter the issuer, the Client ID and the Client Secret.
  3. Choose New-user access and the organization, select Test connection, then Set up SSO.
  4. Verify the domain with the DNS TXT record the page shows.
  5. From a private browser window, sign in with a JumpCloud user in a bound group.

To create and suspend people in Multi-Router from JumpCloud, see JumpCloud provisioning.

Single Sign-On

Other OpenID Connect ProvidersEnterprise

Connecting any identity provider that supports OpenID Connect.

Multi-Router connects to any identity provider that supports OpenID Connect sign-in with the authorization code flow, such as Keycloak, Auth0 or Authentik, as well as the providers with their own pages. Choose Other / Generic as the Identity provider, and create an app in the identity provider with the settings below.

Requirements

The identity provider app needs these settings:

SettingValue
Application typeA web application, also called a confidential client, with a client secret.
Grant typeAuthorization code, with PKCE allowed.
Redirect URIhttps://dashboard.multi-router.ai/sso/callback
Client authenticationClient secret sent in the request body, often called client_secret_post.
Scopesopenid, email and profile.
ID token claimsemail, with email_verified true or absent.
Signing algorithmRS256, RS384, RS512, PS256, PS384, PS512, ES256 or ES384.
IssuerA public https address that serves /.well-known/openid-configuration.

Finding the issuer

The issuer is the issuer value in the provider's discovery document, at the issuer address followed by /.well-known/openid-configuration. Enter it exactly as the document shows it, including any trailing slash. Common forms:

ProviderIssuer form
Keycloakhttps://sso.example.com/realms/example
Auth0https://example.us.auth0.com/
Authentikhttps://auth.example.com/application/o/multi-router/

Every person who signs in has an email in the company's email domain, the domain set on the SSO connection, and apps that sign tokens with HS256 are switched to RS256.

Connecting it

Choose Other / Generic on the Single Sign-On (SSO) page, enter the email domain, issuer, client ID and client secret, and finish the steps on Setting Up SSO. Test connection checks the discovery document and signing keys, and a sign-in from a private browser window checks the rest.

Single Sign-On

Signing InEnterprise

What people see when they sign in with SSO, what happens on the first sign-in, and how sessions work.

Signing in with SSO starts on the normal Multi-Router sign-in page. The page asks for a work email first, and when the email's domain has an SSO connection, it sends the person to their identity provider. People sign in the same way to the dashboard and to the Portal.

The sign-in steps

  1. On the sign-in page, enter your Work email and select Continue.
  2. The page shows Single sign-on, with Your team uses Okta. Select Continue with SSO.
  3. Sign in at the identity provider, which returns you to Multi-Router.
  4. Multi-Router opens the dashboard in the organization the connection provisions into, or the Portal when you started there.

The profile menu shows an SSO badge for a session started through SSO.

The first sign-in

On a person's first sign-in through SSO, Multi-Router matches them to a person in the account by email:

SituationResult
A person with the same email and access in the connection's organizationThe SSO identity is linked to them, and they sign in with their existing access.
A person with the same email and no access there, with Auto-provisionThey are added to the organization with a role from their account role.
A person with the same email and no access there, with Invite-onlyThe sign-in is refused.
A pending invitation, before it expiresThe invitation is accepted with its role and organizations; the person then needs access in the connection's organization, as above.
No person exists, with Auto-provisionA person is created with the default role in the connection's organization.
No person exists, with Invite-onlyThe sign-in is refused, with a message to ask an admin for an invitation.
The person is suspended or offboardedThe sign-in is refused.

A person who already used a password or social sign-in keeps their Multi-Router name, history and access once their SSO identity is linked, and the person's page shows Sign-in: SSO linked. A person created through SSO takes a display name from their email address.

Sessions

A session started through SSO lasts 8 hours, the same as any Multi-Router session, after which the person signs in again. Signing out ends the Multi-Router session; the identity provider session continues, so a later sign-in may complete without a new password prompt.

Starting from a link

A sign-in can also start from the address https://dashboard.multi-router.ai/sso/start?domain=example.com, which goes straight to the identity provider for the domain. It suits a bookmark or an intranet link. For a Multi-Router tile in Okta's app dashboard, set the app's Initiate login URI to this address, with Login initiated by allowing Okta.

Single Sign-On

Requiring SSOEnterprise

Turning off password and social sign-in for the domain, so everyone signs in through the identity provider.

Requiring SSO makes the identity provider the only way people of the email domain sign in. With Require SSO for everyone turned on, password sign-in and social sign-in, such as Google or GitHub, are turned off for every email in the domain, so the company's sign-in policies, such as multi-factor authentication, apply to every session.

When it applies

Requiring SSO takes effect once the connection is on and the domain is verified. A password or social sign-in attempt for the domain then returns Your organization requires single sign-on. Please sign in with SSO. Invited people of the domain accept their invitation by signing in through SSO.

The account owner

The account owner keeps password sign-in while SSO is required, so the account stays reachable when the identity provider is unavailable or misconfigured. Keeping the owner's password current keeps that path ready.

Sessions already open

Sessions that started before SSO was required continue until they expire, within 8 hours. From then on, every sign-in for the domain goes through the identity provider.

Rolling it out

A safe order for turning on SSO for a whole company:

  1. Set up the connection with Require SSO off, and verify the domain.
  2. Sign in through SSO with a test person, and check their organization and role.
  3. Tell people that sign-in moves to the identity provider.
  4. Turn on Require SSO for everyone.

Single Sign-On

TroubleshootingEnterprise

SSO setup and sign-in errors, with their causes and fixes.

SSO errors appear in two places: on the Single Sign-On (SSO) page while setting up a connection, and on a Single Sign-On error page with Back to sign in during a sign-in. The tables below list the messages, their causes and the fixes.

Setup errors

Saving, testing or verifying a connection can return these messages:

MessageCause and fix
the OIDC issuer must be a public https:// URLThe issuer uses http, or a private or internal address. Use the identity provider's public https issuer.
issuer mismatch in discovery documentThe issuer entered differs from the issuer in the provider's discovery document. Copy the issuer value from the discovery document.
the JWKS endpoint returned no keysThe identity provider publishes no signing keys for this issuer. Check the authorization server.
that email domain is already used for SSO by another organizationAnother Multi-Router account has an SSO connection for the domain.
client secret is requiredA new connection needs the client secret.
We couldn't find the TXT record yet. DNS can take a few minutes to propagateThe DNS record is missing or not yet visible. Check its name and value, wait a few minutes, and verify again.
disconnect directory provisioning before changing SSO settingsUser provisioning is connected. Disconnect it to change the domain.
disconnect the directory before changing JIT or disabling SSOUser provisioning is connected. Disconnect it to change new-user access or turn the connection off.
disconnect directory provisioning before removing SSOUser provisioning is connected or set up. Turn it off to remove the connection.

Sign-in errors

A sign-in through SSO can stop on the Single Sign-On error page with these messages:

MessageCause and fix
No SSO is configured for that email domain.The email domain has no SSO connection, or the connection is turned off. Check the connection's email domain and toggle.
This organization hasn't verified ownership of its email domain yet.Verify the domain on the Single Sign-On (SSO) page.
SSO is not enabled for this organization.The connection was turned off during the sign-in. Turn it on in the card header.
Couldn't reach your identity provider.Multi-Router couldn't read the issuer's discovery document. Check the issuer and retry.
The identity provider rejected the sign-in.The client secret or redirect URI is wrong. Check the client secret, and that the app allows https://dashboard.multi-router.ai/sso/callback.
Sign-in could not be verified.The token's issuer or client ID differs from the connection, it has no email claim, it expired, or the identity provider reports the email as unverified.
Your identity provider signed you in with an email outside this organization's domain. If you are signed in to your identity provider as someone else, sign out there and try again.The identity provider session belongs to another account. Sign out at the identity provider and try again.
Your account isn't a member of this organization. Ask an admin to invite you.Invite-only is on and the person has no invitation, or the person is suspended or offboarded.
Your sign-in session expired. Please try again.The sign-in took more than 10 minutes. Start again.
Portal access is not configured for this organization.The person started at the Portal, and the organization has no Portal access for them. See Self-Service Provisioning.

Checklist

Most SSO problems come from one of these settings:

CheckWhere
The redirect URI is exactly https://dashboard.multi-router.ai/sso/callbackThe identity provider app
The issuer matches the discovery document, including /oauth2/default for OktaIssuer / Discovery URL
The issuer is copied exactly from the discovery document, with a trailing slash only when the document has oneIssuer / Discovery URL
The ID token includes the email claimThe identity provider app
The person is assigned to the appThe identity provider
The domain is verified and the connection is onThe Single Sign-On (SSO) page
The person has an invitation, when Invite-only is onPeople

User Provisioning (SCIM)

OverviewEnterprise

Keeping Multi-Router's people and their access in step with your identity provider's directory.

User provisioning lets the company's identity provider, such as Okta, manage the people in Multi-Router. The identity provider sends people and groups to Multi-Router over SCIM 2.0, the standard protocol for directory sync: new hires appear in Multi-Router, changes to their name or department follow, and people deactivated in the identity provider are suspended in Multi-Router. Rules turn directory groups and attributes into access, such as an organization role or Portal access with a tools policy, so access follows the directory without manual invitations.

How it works

Provisioning connects three things:

PartRole
The directoryThe identity provider pushes people and groups to Multi-Router's SCIM endpoint with a directory token.
RulesEach rule matches people by a pushed group or an attribute such as department, and grants a bundle of access.
ReviewChanges that need a person to decide, such as access that no longer matches any rule, wait in Review.

A person's path through provisioning:

  1. The identity provider pushes the person. Multi-Router matches them to an existing person by email, or adds them as Not set up.
  2. Rules that match the person grant access, immediately or after approval, depending on the arrival setting.
  3. The person signs in through SSO and uses what they were granted, such as setting up Claude Code from the Portal.
  4. When the identity provider deactivates the person, Multi-Router suspends them and disables their routers and API keys.

Provisioning and SSO

User provisioning builds on single sign-on: it needs an SSO connection that is on, with a verified domain. While provisioning is connected, the directory decides who has access, and SSO signs people in with the access the directory and its rules granted. SSO's new-user access is set to invite-only for the duration, and returns to its earlier setting when provisioning is turned off. See Single Sign-On.

Built-in safeguards

Directory changes grant access and suspend people, and removing access is an admin decision:

SafeguardBehavior
Account admin grantsWhen a rule grants account admin, the person's rule access waits for an account admin's approval.
Lost matchesAccess that no longer matches a rule goes to Review, where an admin keeps or removes it.
Account ownerThe directory can suspend anyone in the account except the account owner.
Email changesA changed email in the directory is shown in Review, and the person keeps their Multi-Router email.
Admin grantsRules leave access granted by an admin as it is, and admins leave a rule's access to the rule.

The other pages in User Provisioning cover each part in detail:

PageCovers
Connecting Your DirectoryTurning on provisioning, arrival settings, the endpoint and directory tokens.
OktaSetting up provisioning in Okta.
Microsoft Entra IDSetting up provisioning in Entra ID.
Ping IdentitySetting up provisioning in PingOne.
JumpCloudSetting up provisioning in JumpCloud.
Other DirectoriesConnecting any SCIM 2.0 directory.
RulesGranting access from groups and attributes.
Review and NotificationsApprovals, lost matches, email changes, and alerts.
LifecycleArrival, updates, suspension, deletion and reactivation.
SCIM EndpointResources, attributes, filters, PATCH and errors.
TroubleshootingConnection health and common problems.

User Provisioning (SCIM)

Connecting Your DirectoryEnterprise

Turning on user provisioning, choosing what happens when someone new arrives, and managing directory tokens.

Connecting a directory turns on user provisioning for the account and gives the identity provider an endpoint and a token to push people to. It is done on the User provisioning (SCIM) page, opened from Settings in the account, after single sign-on is set up and its domain verified.

Steps

Connecting takes a few minutes in Multi-Router and the identity provider:

  1. Set up SSO and verify its domain. See Setting Up SSO.
  2. Open Settings, then User provisioning (SCIM), and turn on User provisioning.
  3. Choose what happens When someone new arrives.
  4. Select Save. Multi-Router turns on provisioning and issues a directory token.
  5. Copy the SCIM base URL into the identity provider, then copy the token with Copy on its row and paste it there too.
  6. Run the identity provider's connection test. The connection health panel then shows the identity provider's contact.

When someone new arrives

The arrival setting decides what rules do each time the directory pushes a person, on arrival and on every later update:

SettingBehavior
Apply rules automaticallyMatching rules grant access at once. Access that includes account admin waits for approval.
Require admin approvalMatching rules create approval requests, and access starts when an admin approves.
Do nothingRules grant nothing, and people get access when an admin grants it. Access that no longer matches a rule still goes to Review. This is the default.

To change the arrival setting while provisioning is on, turn provisioning off and on again. See Rules.

The SCIM base URL

The SCIM base URL is the address the identity provider sends requests to. It belongs to the account and has the form https://api.multi-router.ai/scim/v2/acc_7Tq2Wd9KxP. The page shows it with a Copy button.

Directory tokens

A directory token is the credential the identity provider sends with each request, in the Authorization header. Tokens start with mr_scim_ and work only for the SCIM endpoint of their own account. The page lists each token with its first characters, when it was created and when it was last used:

ActionEffect
Issue tokenCreates a token. Up to two can be enabled at once.
CopyCopies the full token to the clipboard.
DisableStops the token from working. Enable restores it.
DeleteRemoves a disabled token permanently, after a confirmation.

A directory token can suspend everyone in the account except the owner, so it is kept in the identity provider and nowhere else. To rotate a token: issue a new one, copy it into the identity provider, confirm a successful sync, then disable the old one.

Connection health

example-co · acc_7Tq2Wd9KxP

Account Settings

User provisioning (SCIM)

On. Okta can add, update and suspend people in this account.

When someone new arrives

To change this, turn user provisioning off and on again.

Connection health

Okta last contacted us 10/1/2026, 9:42:18 AM

Last request succeeded · 128 recorded · Multi Router keeps the last 500 recorded requests

Refresh status

Endpoint and tokens

Copy this account-scoped URL into your directory provider. A token from another account is refused.

Copy

A directory token can suspend everyone except the account owner. Rule changes do not automatically remove existing access; removals require review.

Up to two tokens can be enabled at once for rotation. Copy the new token into Okta, confirm a sync there, then disable the old one.

Issue token
  • mr_scim_Xq7v••••••••••••••••••••••••••••Copy
    Created 9/14/2026 · Last used 10/1/2026, 9:42:18 AM
    DisableDelete

Notification rules

Send one batched notification when matching directory events occur.

Add notification rule
EventRecipientsStatusActions
Approval neededAll account adminsOn
Turn offEditDelete
Connection failureit-ops@example.com, security@example.comOn
Turn offEditDelete

2 of 20 rules used.

Failed background work

Refresh status

No failed background work.

Recent requests

Refresh history

Use your directory provider’s connector test. No request bodies, person fields, resource IDs, or token details appear here.

  • PATCH Users · 200Request req_Vb7Kq2Xn9TzR4wLp · 10/1/2026, 9:42:18 AM
  • GET Users · 200Request req_H3mPq8Zs1YcN6dFu · 10/1/2026, 9:42:17 AM
  • POST Users · 201Request req_Lw5Rt0Gk7JxB2vQe · 10/1/2026, 9:31:04 AM
  • PATCH Groups · 200Request req_Ce9Nz4Ua6MhW1sKy · 10/1/2026, 9:31:02 AM
User provisioning on for Okta: connection health, the SCIM base URL, a directory token, notification rules and recent requests. Select to enlarge.

The connection health panel shows whether the identity provider is reaching Multi-Router:

StateMeaning
Waiting to hear from your directoryProvisioning is on, and no request has arrived yet.
{provider} last contacted us {time}Requests are arriving. The detail line shows whether the last one succeeded.
Directory connection stopped workingA request used a disabled token, or three requests in a row failed on the server side or reached the wrong account. The reason is shown.

Recent requests lists the latest requests with their method, resource, status, reason and request id, and Refresh status updates the panel. A request Multi-Router refuses as invalid shows in Recent requests and leaves the connection healthy. The panel returns to healthy on the next successful request. See Troubleshooting.

Turning provisioning off

Turning off User provisioning disables every directory token and returns SSO's new-user access to its earlier setting. People and their access stay as they are. Turning provisioning on again re-enables the newest token.

While provisioning is on, the SSO connection's domain, new-user access and the connection itself stay fixed; turn provisioning off to change them. Requests that reach the endpoint while provisioning is off return 401 with invalid bearer token, because every token is disabled.

User Provisioning (SCIM)

OktaEnterprise

Setting up provisioning to Multi-Router in Okta: the app, the connection, and pushing users and groups.

Okta provisions people to Multi-Router through a SCIM app with header authentication. The app holds the SCIM base URL and directory token from Multi-Router, sends Multi-Router the people assigned to it, and pushes the groups that rules match on. This page covers the Okta side; the Multi-Router side is on Connecting Your Directory.

Adding the app

The app comes from Okta's catalog:

  1. In the Okta Admin Console, open Applications, then Browse App Catalog.
  2. Search for SCIM 2.0 Test App (Header Auth), select it, and choose Add Integration.
  3. Name the app, such as Multi-Router Provisioning, and finish adding it.

Connecting it

The app's Provisioning tab holds the connection to Multi-Router:

  1. On the app's Provisioning tab, select Configure API Integration and turn on Enable API integration.
  2. Enter the SCIM base URL from Multi-Router as the Base URL, and the directory token as the API Token.
  3. Select Test API Credentials. Multi-Router's connection health panel shows Okta's contact.
  4. Save.

Provisioning features

Under Provisioning, then To App, turn on the features Multi-Router supports:

Okta featureEffect in Multi-Router
Create UsersAssigned people are added to Multi-Router.
Update User AttributesChanges to a person's name, department and other attributes follow.
Deactivate UsersUnassigned or deactivated people are suspended.

The attributes Multi-Router reads include the username, email, name, title, department, division, organization, cost center and employee number. See SCIM Endpoint.

Assigning people

Okta provisions the people and groups assigned to the app. Assign groups on the Assignments tab, and Okta pushes their members to Multi-Router. Each person then follows the arrival setting and the rules.

Pushing groups

Group rules in Multi-Router match groups that Okta pushes. On the Push Groups tab, push each group a rule should match, such as Engineering. Okta then keeps the group's name and members in step, and Multi-Router re-checks the rules for each member that joins or leaves. Push a group after its members are provisioned, so every member exists in Multi-Router when the group arrives.

User Provisioning (SCIM)

Microsoft Entra IDEnterprise

Provisioning people and groups to Multi-Router from Microsoft Entra ID.

Microsoft Entra ID provisions people to Multi-Router from an enterprise application with automatic provisioning. The enterprise application holds the SCIM base URL and directory token from Multi-Router, sends the users and groups assigned to it, and repeats on Entra's provisioning cycle. This page covers the Entra side; the Multi-Router side is on Connecting Your Directory.

The enterprise application

Provisioning runs from the Multi-Router enterprise application created with the app registration for single sign-on, or from a new non-gallery enterprise application. In the Microsoft Entra admin center, open Enterprise apps, under Entra ID, and select it. See Microsoft Entra ID.

Connecting it

The application's Provisioning page holds the connection:

  1. Open Provisioning, and set Provisioning Mode to Automatic, or select New configuration.
  2. Enter the SCIM base URL from Multi-Router as the Tenant URL, and the directory token as the Secret Token.
  3. Select Test Connection, and save. Multi-Router's connection health panel shows Entra's contact.

Mappings

Under Mappings, keep Provision Microsoft Entra ID Users and Provision Microsoft Entra ID Groups turned on. The default user mappings send the username, email, name, active state and job details that Multi-Router reads, including department, which Multi-Router rules can match. See SCIM Endpoint.

Scope and starting

Set Scope to Sync only assigned users and groups, assign the users and groups on Users and groups, and turn Provisioning Status On. Entra then sends the assigned people and their groups, and keeps them in sync on each cycle, about every 40 minutes; Provision on demand sends one person at once to test. Assigning a group sends its direct members, and the group itself. Multi-Router group rules match that group. Assigning groups needs Entra ID P1 or P2.

User Provisioning (SCIM)

Ping IdentityEnterprise

Provisioning people and groups to Multi-Router from PingOne.

PingOne provisions people to Multi-Router through a SCIM outbound provisioning connection. The connection holds the SCIM base URL and directory token from Multi-Router, and provisioning rules decide which users and groups it sends.

Creating the connection

The connection is created in the PingOne admin console:

  1. Open Integrations, then Provisioning, and add a new connection of type Identity Store.
  2. Choose SCIM Outbound, and name the connection, such as Multi-Router.
  3. Enter the SCIM base URL from Multi-Router as the SCIM base URL, and choose OAuth 2 Bearer Token authentication with the directory token.
  4. Allow users to be created, updated, and disabled or deleted, test the connection, and save.

Provisioning rules

A provisioning rule on the connection chooses the users to send, such as members of a PingOne group, and maps their attributes. Map username, email, given and family names, and the job attributes that Multi-Router rules match, such as department. Group provisioning sends groups for group rules. See Rules.

User Provisioning (SCIM)

JumpCloudEnterprise

Provisioning people and groups to Multi-Router from JumpCloud.

JumpCloud provisions people to Multi-Router from the same custom application used for single sign-on, through its identity management settings. JumpCloud sends the members of the user groups bound to the application, and can send the groups as well.

Connecting it

The connection is set on the application's Identity Management tab:

  1. Open SSO Applications, and select the Multi-Router application. See JumpCloud.
  2. On the Identity Management tab, choose SCIM 2.0 as the API type.
  3. Enter the SCIM base URL from Multi-Router as the Base URL, and the directory token as the Token Key, sent as a Bearer token.
  4. Select Test Connection, then Activate. Multi-Router's connection health panel shows JumpCloud's contact.

Users and groups

JumpCloud provisions the members of the user groups bound to the application, and keeps their attributes and active state in sync. Turn on group management to send the groups themselves, which Multi-Router group rules match. See Rules.

User Provisioning (SCIM)

Other DirectoriesEnterprise

Connecting any directory that provisions users over SCIM 2.0.

Multi-Router's SCIM endpoint follows SCIM 2.0, so any directory or identity provider that provisions over SCIM 2.0 can connect, such as OneLogin or a custom script. The directory needs the SCIM base URL and a directory token. It sends users, and sends groups when group rules are used.

Settings

A SCIM client connects with these settings:

SettingValue
Base URLThe SCIM base URL from Multi-Router, such as https://api.multi-router.ai/scim/v2/acc_7Tq2Wd9KxP.
AuthenticationA bearer token in the Authorization header, with the directory token.
User matchingThe primary email in emails, or userName when there are no emails. Lookups filter on userName, externalId or id.
DeprovisioningSet active to false, or delete the user. Both suspend the person.
GroupsPush groups after their members, with each member by the SCIM id Multi-Router returned.

The attributes, filters, page sizes up to 100, PATCH operations and errors the endpoint supports are on SCIM Endpoint.

Google Workspace

Google Workspace provisions users to applications listed in its own app catalog. People from Google Workspace sign in to Multi-Router through single sign-on, and with Auto-provision they are added on their first sign-in. See Google Workspace.

User Provisioning (SCIM)

RulesEnterprise

Granting access from directory groups and attributes.

A rule grants access to the people who match it. Each rule has one condition, a pushed group or an attribute value, and a bundle of access to grant, the same bundle an invitation sets: an account role, roles in organizations, and Portal access with a tools policy. Rules are managed on the Rules tab of the People page.

Conditions

A rule's condition is one of these:

ConditionMatches
GroupMembers of a group the identity provider pushes, such as Engineering.
DepartmentPeople whose department equals a value.
DivisionPeople whose division equals a value.
OrganizationPeople whose organization attribute equals a value.
Cost centerPeople whose cost center equals a value.
TitlePeople whose title equals a value.
User typePeople whose user type equals a value.

Attribute values match regardless of letter case and surrounding spaces. Group conditions list the groups the identity provider has pushed; No pushed groups found means group push is still to be turned on there. An account holds up to 100 rules.

What a rule grants

A rule's bundle can grant:

GrantValues
Account roleAdmin, Member or none.
Organization roleAdmin, Member or none, for each organization.
Portal accessAccess to the Portal with a tools policy, which decides the tools the person can set up.

A person matched by several rules receives every rule's grants together, up to five rules. A person matched by more than five rules appears in Review under Rules that could not run, and the rules apply once they are narrowed. Rule access for an invited person starts when they accept their invitation, and rules apply to people whose directory record is active. Portal access lets the person set up tools such as Claude Code from the Portal, which then creates their router and API key. See Self-Service Provisioning.

Approvals

When a matching rule grants the account Admin role, all of the person's rule access waits for an account admin's approval, whatever the arrival setting. With Require admin approval, every grant waits. Approvals appear in Review under Waiting for your approval, and a grant that changes the tools policy of existing access appears under Policy changes to review. An account admin's Apply to all counts as approval. See Review and Notifications.

Applying a rule to existing people

A new or changed rule applies to people as the directory next updates them, following the arrival setting. To apply it to everyone who already matches, turn the rule on, preview it, and select Apply to all. Apply to all handles rules that match up to 250 people; a rule that matches more is split into narrower rules, such as by group, and each is applied.

Turning off and deleting rules

The two ways to retire a rule treat existing access differently:

ActionExisting access
Turn the rule offStays. The rule stops granting to new matches.
Delete the ruleGoes to Review, where an admin keeps or removes each grant.

Rule and admin grants

Each grant records where it came from: a rule or an admin. A rule changes only its own grants, and an admin changes a rule's grant by changing the rule. Keep access in Review converts a rule's grant into an admin grant that stays when the rule no longer matches.

Example

A company pushes an Okta group named Engineering and creates a rule: Group is Engineering, with organization role Member in Engineering and Portal access with the tools policy Claude Code for engineers. With Apply rules automatically, each engineer Okta pushes becomes a member of the Engineering organization and can set up Claude Code from the Portal on their first sign-in. When someone leaves the Engineering group, their access goes to Review for an admin to keep or remove.

User Provisioning (SCIM)

Review and NotificationsEnterprise

Directory changes that wait for a decision, and notifications that tell admins about them.

Review is the queue of directory changes that need a person to decide. It lives on the Review tab of the People page, with a count of open items. Notification rules send email about Review items and connection problems, so admins learn about them without checking the page.

Review items

Review holds these kinds of items:

SectionArises whenActions
Waiting for your approvalA rule grants the account Admin role, or any grant under Require admin approval.Approve and apply, Decline, or Change proposed access to edit the access before approving.
Policy changes to reviewA rule would change the tools policy of a person's existing access.Approve the new policy, or keep the current one as admin-owned access.
Access that no longer matches a ruleA person leaves a group or changes an attribute, a group is deleted, or a rule is deleted.Keep access, or Remove access.
Directory email changesThe directory changes a person's email.View person details. The person keeps their Multi-Router email.
Rules that could not runMore than five rules match a person.Fix the rule.

Keep access turns the rule's grant into an admin grant. Remove access removes the grant; for Portal access, it also removes the person's tools, with their routers and API keys. Review also counts people who arrived that no rule covers, with a link to them in the Not set up roster.

Notification rules

A notification rule sends email when a kind of directory event happens. Rules are added with Add notification rule on the User provisioning (SCIM) page, up to 20 per account. Each rule pairs an event with recipients:

EventSent when
Any directory eventAny of the events below.
Approval neededA grant waits for approval.
Person suspendedThe directory suspends a person.
Access no longer matchesAccess stops matching a rule.
Uncovered arrivalA person arrives whom no rule covers.
Rule errorToo many rules match a person.
Connection failureThe directory connection stops working.
RecipientsWho receives it
Send emailUp to 10 email addresses.
The organization's adminsThe admins of the organization the event concerns. Available for every event except Any directory event and Connection failure.
All account adminsThe account's admins.

Emails group events that happen close together into one message, with a subject such as example-co: 3 directory updates need attention. A message is sent one minute after the last event, or 10 minutes after the first, whichever comes first, to up to 250 recipients. The page reports failed deliveries under Notification delivery.

User Provisioning (SCIM)

LifecycleEnterprise

What Multi-Router does when the directory adds, updates, deactivates, deletes and reactivates people.

The lifecycle is the path a person takes as the identity provider changes them, from arrival to suspension and back. Multi-Router applies each directory change to the matching person, and records it on the Activity tab of the People page with the directory as the actor.

Directory changes

Each directory change has this effect in Multi-Router:

Directory changeEffect
A person is addedMatched to an existing person by email, or added as Not set up. Rules then apply under the arrival setting.
A person already invited by hand is addedThey stay Invited, and their invitation keeps its grants as admin grants.
Name or attributes changeThe person's attributes update, and their name too while their directory email matches their Multi-Router email. Rules are checked again under the arrival setting.
Email changesA Review item; the person keeps their Multi-Router email.
Group membership changesRules are checked again for the person.
A person is deactivatedAn active person is suspended: their sessions end, and the routers and API keys of the tools they set up from the Portal are disabled.
A person is deletedThe same as deactivation. The person is suspended, and stays in Multi-Router.
A person is reactivatedA person the directory suspended returns to active, and their tools' routers and API keys are enabled again.

Suspension

Suspension stops a person's access while keeping their history. A suspended person's dashboard and Portal sessions end on their next request, and the routers and API keys of the tools they set up from the Portal are disabled, so tools such as Claude Code stop working at once. Invited and Not set up people who are deactivated are marked inactive, which blocks their sign-in and their invitation.

The directory can suspend anyone except the account owner. Removing a person from Multi-Router entirely is an admin action on the People page, called offboarding.

Reactivation

Reactivating a person in the identity provider restores a person whom the directory suspended, with their routers and API keys, and checks the rules for them again. A person an admin suspended by hand stays suspended until an admin restores them. A person who was offboarded and is pushed again arrives as a new Not set up person.

User Provisioning (SCIM)

SCIM EndpointEnterprise

The SCIM 2.0 resources, attributes, filters, PATCH operations, limits and errors Multi-Router supports.

Multi-Router's SCIM endpoint implements SCIM 2.0 for Users and Groups. Identity providers call it on their own, so this page serves teams that check compatibility or connect a directory other than Okta. Every request carries a directory token and uses the account's SCIM base URL.

Authentication

Each request sends the directory token in the Authorization header, as Bearer mr_scim_… or the token on its own. Responses use application/scim+json, and requests send application/scim+json or application/json.

List usersbash
curl https://api.multi-router.ai/scim/v2/acc_7Tq2Wd9KxP/Users?count=50 \
  -H "Authorization: Bearer $MR_SCIM_TOKEN"

Endpoints

The endpoint serves these paths under the base URL:

PathMethods
/ServiceProviderConfigGET
/ResourceTypesGET
/SchemasGET
/UsersGET, POST
/Users/{id}GET, PUT, PATCH, DELETE
/GroupsGET, POST
/Groups/{id}GET, PUT, PATCH, DELETE

Creating a user that already exists, matched by external id, email or username, updates it and returns 200; a new user returns 201. Deleting a user suspends the person in Multi-Router and returns 204; the user then returns 404 and leaves lists, and creating it again restores it. Group lists leave out members, and a single group includes them. List requests accept filter, startIndex, count, attributes and excludedAttributes, and return every attribute.

User attributes

Multi-Router reads these User attributes:

AttributeUse
userNameRequired, up to 320 characters. Stored in lowercase.
emailsThe primary email, or the first, is the person's email. With no emails, the userName is used.
displayNameThe person's display name. Without it, name.formatted, then the email.
externalIdThe directory's id for the person, up to 256 characters.
activefalse suspends the person. Accepts true and false as booleans or strings.
name.givenNameGiven name.
name.familyNameFamily name.
titleMatched by Title rules.
userTypeMatched by User type rules.
departmentEnterprise extension. Matched by Department rules.
divisionEnterprise extension. Matched by Division rules.
organizationEnterprise extension. Matched by Organization rules.
costCenterEnterprise extension. Matched by Cost center rules.
employeeNumberEnterprise extension.

Group membership is sent through Groups, with each member's SCIM id, the id value Multi-Router returned for the user, such as scusr_3Hn8Wq2LdK. Every member must exist as a user before the group lists them.

Filtering and paging

List requests accept a filter with the eq operator and a quoted value, on id, externalId and userName for Users, and id, externalId and displayName for Groups, written exactly in that case. Paging uses startIndex from 1 and count up to 100.

Filter by usernametext
GET /Users?filter=userName eq "dana@example.com"

PATCH

PATCH accepts the operations add, replace and remove, 1 to 20 per request. On Users, operations without a path set the attributes above, and paths name the attributes above, such as these; Multi-Router applies the paths it reads and returns 200:

PathExample
A work emailemails[type eq "work"].value
A name partname.givenName
An enterprise attributeurn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department
Activeactive

On Groups, PATCH changes the display name and external id, and adds, replaces or removes members, returning 204:

Remove a group memberjson
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [
    { "op": "remove", "path": "members[value eq \"scusr_3Hn8Wq2LdK\"]" }
  ]
}

Limits

The endpoint applies these limits:

LimitValue
Requests per directory token600 per minute
Requests with a missing or unknown token, per network1,200 per minute
Page size100
Request body1 MiB
Enabled directory tokens2

A request over the rate limit receives 429 with Retry-After: 60, and identity providers retry it on their own.

Errors

Errors use the SCIM error format with a status, a detail and, for some, a scimType:

StatusDetailscimTypeCause
401invalid bearer tokenThe token is missing, unknown, disabled or for another account, or provisioning is off.
400invalid JSONThe body is not valid JSON.
400invalid UserA User body is missing userName or has invalid values.
400invalid GroupA Group body has invalid values.
400unsupported filterinvalidFilterA filter other than eq on a supported attribute.
400invalid startIndexstartIndex is below 1.
400invalid countcount is above 100.
400unsupported list parameterA list parameter outside the supported ones.
400invalid PATCH operationsinvalidPathThe Operations list is empty, too long or malformed.
400required attribute cannot be removedinvalidPathA PATCH removes userName or emails.
400unsupported PATCH pathinvalidPathA Group PATCH path outside the supported ones.
403directory cannot suspend account ownerA deactivation or deletion of the account owner.
404not foundThe user or group is absent, or a group member is still to be provisioned.
405operation not availableA write while provisioning is still being turned on.
409directory identity changed; retryThe request's external id, email and username point to different existing users, or another change to the same person happened at the same time.
413request too largeThe body is larger than 1 MiB.
415unsupported content typeThe request is not JSON.
429rate limit exceededOver the rate limit.
503directory temporarily unavailableMulti-Router could not complete the request. The identity provider retries.

User Provisioning (SCIM)

TroubleshootingEnterprise

Reading connection health, and fixing common provisioning problems.

Provisioning problems appear in two places: the connection health panel and Recent requests on the User provisioning (SCIM) page, and the Review tab of the People page. The connection panel shows whether requests reach Multi-Router and succeed; Review shows people whose access needs a decision.

Connection reasons

When the connection stops working, the panel shows one of these reasons:

ReasonCauseFix
disabled tokenThe identity provider used a token that is disabled, such as after rotation or turning provisioning off.Copy an enabled token into the identity provider.
account mismatchA token from another account, or the wrong SCIM base URL.Copy this account's base URL and token.
connection disabledThe account's SSO connection is off.Turn on the SSO connection.
server errorMulti-Router could not complete requests.The panel clears on the next success, as the identity provider retries.

Common problems

These are the most common provisioning problems:

ProblemCause and fix
Recent requests stays emptyRequests from the identity provider are reaching a different address. Check the base URL, and run the provider's connection test.
Recent requests shows 400 responsesMulti-Router refused the requests as invalid. The detail in each row names the attribute or filter to fix in the identity provider.
A user push fails with directory identity changed; retryThe user was deleted and recreated in the identity provider with a new id. Contact support to relink the person.
People arrive with no accessThe arrival setting is Do nothing, or no rule matches them. Look for Uncovered arrival in Review, and add a rule.
A group rule shows No pushed groups foundGroup push is off in the identity provider. Push the group from the provider.
A group push fails with not foundA member of the group is still to be provisioned. Assign and push the members first.
A new admin has no admin accessAccount admin grants wait for approval. Approve it in Review.
Access stayed after someone left a groupLost matches go to Review. Keep or remove the access there.
A suspended person cannot be reactivated from the directoryAn admin suspended them by hand. Restore them on the People page.

Self-Service Provisioning

OverviewEnterprise

Letting people set up approved AI tools for themselves, under policies the organization controls.

Self-service provisioning lets people set up AI tools such as Claude Code and Claude Desktop for themselves, from a web page called the Portal, while the organization decides what each tool may do. An admin writes a tools policy once, with the route, models, credentials and budget for each tool, and assigns it to people. Each person then opens the Portal, picks a tool, and receives their own router, key and setup instructions to paste into the tool.

Policies, the Portal and agents

Self-service provisioning has three parts:

PartWhat it is
Tools policyThe organization's rules for each tool: route, region, credentials, allowed models and a budget per person.
The PortalThe web page where people with Portal access set up the tools their policy grants, see their budget and get help.
AgentOne tool set up by one person: a Claude Proxy router of their own and a key limited to it.

How it works

A tool reaches a person in five steps:

  1. An admin creates a tools policy for an organization, such as Standard Engineering, with Claude Code and Claude Desktop.
  2. People receive Portal access with that policy, by invitation, from the roster, or from a user provisioning rule.
  3. Each person signs in to the Portal, chooses a tool and selects Set up. Multi-Router creates their agent.
  4. They paste the settings the Portal shows into the tool, and start working.
  5. When the admin edits the policy, every person's agent follows the new revision, with the same key.

What the organization controls

A tools policy sets these controls for each tool, and every person's agent follows them:

ControlEffect
RouteWhich provider serves the tool, such as Anthropic or Bedrock.
RegionWhere the provider serves it, on routes with regions, such as the US.
CredentialsThe organization's provider key, or each developer's own Claude login for Claude Code on the Anthropic route.
ModelsThe models the tool can use, with any switched off.
Per-person capEach person's spending limit per period, with optional caps per model.
Budget rulesNotifications and blocks at chosen points of the cap.

Each agent is a Claude Proxy, so Traffic, request logging and the organization's budgets apply to it as to any router. See Claude Proxy.

Supported tools

Policies grant Claude Code and Claude Desktop. The Portal shows each person the tools their policy grants. See Claude Code and Claude Desktop.

The other pages in Self-Service Provisioning cover each part in detail:

PageCovers
Tools PoliciesCreating and editing policies, and how edits reach everyone.
Assigning PoliciesGiving people Portal access with a policy, and taking it away.
The PortalWhat people see and do in the Portal.
Managing AgentsAdmins' view of each person's agents: keys, retiring, usage.

Self-Service Provisioning

Tools PoliciesEnterprise

Creating and editing the policies that decide how each tool works for the people on them.

A tools policy is an organization's set of rules for the AI tools it grants, one set per tool. Policies belong to an organization and are listed under Policies on the organization's People page. Each policy has revisions: every save writes the next revision, and applies it to everyone on the policy.

Creating a policy

A policy is created on the organization's People page:

  1. On the organization's People page, open Policies and select New policy.
  2. Enter a Name, such as Standard Engineering. The policy's Tag, such as policy:standard-engineering, comes from the name.
  3. Under AI Agents, select Add to policy on Claude Code or Claude Desktop, and set up its Routing, Models, Budget and Budget rules.
  4. Select Save Claude Code, or Save Claude Desktop. Tools are added one at a time, each saved before the next.
  5. Select Create policy. Agents are created when people on the policy set them up.

Names are up to 120 characters. Every router the policy creates carries its tag, which is fixed once the policy is created, up to 73 characters, and unique in the organization. The Budget and Budget rules cards appear when budgets are on for the installation.

Routing

The Routing card sets how the tool's requests are served, with the same choices as a Claude Proxy:

SettingValues
RouteA route from the catalog, such as Anthropic or Bedrock (Geo), or Custom with a model group.
RegionThe region, on routes with regions. Anthropic and Custom serve without one.
CredentialsOrganization Key, with a provider key, or Developer Credentials on the Anthropic route.
Model groupOn the Custom route, the group of models, with a provider key for each member.

Claude Desktop uses the Organization Key, since the app sends one credential. Developer Credentials let each person's Claude Code use their own Claude login, billed to them. See Claude Proxy.

Models

The Models card lists the route's models with their version, provider model id and price, and Serve switches each on or off. A switched-off model stays listed and is refused when requested.

Budget

The Budget card sets each person's spending for the tool:

FieldDescription
Per-person capThe most each person can spend per period, such as $200. More than $0.
PeriodDaily, reset every day; Weekly, reset on Mondays; Monthly, reset on the 1st; all at 00:00 UTC. Lifetime never resets.
Per-model capsA cap for one model, per person, inside the per-person cap.

The Budget rules card adds notifications and blocks at points of the per-person cap, every model cap, or one model's cap, such as an email to a team lead at 80% and a block at 100%. Email goes to an address the admin enters, of a person active in the organization. Notifications send once per period, and a block answers 429 until the period resets, or the cap is raised on a Lifetime budget. Each tool's budget appears on the Budgets page, managed by the policy. See Rules and Alerts.

Editing and revisions

Edit policy opens the policy for changes, and Save changes writes the next revision and applies it at once to everyone set up on the policy. Each person's agent keeps its router and key, and takes the new route, models and budget. Save changes is available once something has changed. Removing a tool that people have set up asks for confirmation, and saving retires that tool's agents for everyone.

When an edit affects tools in use, saving asks for confirmation first:

ChangeConfirmation
Removes a model or tool used in the last seven daysSaving is blocked, with See who listing the routers and models affected. Save anyway to continue.
Removes a model or tool with no recent useAcknowledge and save.
Lowers the per-person capAcknowledge and save.

The policy page shows the Revision each person is on. When an edit could not be applied to someone, the page names them, and they stay on the earlier revision until the policy is saved again.

The policy page

People · Engineering

Standard Engineering

policypolicy:standard-engineering
Add peopleEdit policy
People
4
3 set up
AI Agents
1
Claude Code
Spend this period
$288 of $600
resets Oct 1 · 14 days
Revision
3
everyone set up is on 3
AI Agents
Claude Code
Anthropic API · $200 per person, monthly · 3 of 3 models · 2 rules
3 of 4 set up
Route
Anthropic API
Credentials
The company's credentialsAnthropic production
Budget
$200 per person, monthly · $288 of $600 spent
anthropic/claude-opus-5$120 per person
Budget rules
NotifyAt 80% of the per-person capemail priya@example.comFired this period
BlockAt 100% of the per-person capblock requestsLive
Models3 of 3 served
ModelWire idStatusPrice / 1M in · out
Claude Opus 5 anthropic/claude-opus-5claude-opus-5Served$5.00 / $25.00
Claude Sonnet 4.5 anthropic/claude-sonnet-4.5claude-sonnet-4-5Served$3.00 / $15.00
Claude Haiku 4.5 anthropic/claude-haiku-4.5claude-haiku-4-5Served$1.00 / $5.00
People4 on this policy
Search this page…
NameAI AgentsSpendRev
Priya Shah
priya@example.com
Code $64.20/$200$64.203
Dana Lee
dana@example.com
Code $182.40/$200$182.403
Marco Ruiz
marco@example.com
Code $41.75/$200$41.753
Aisha Khan
aisha@example.com
Nothing set up——
Showing 4 of 4
PreviousNext
Organization
Engineering
Created
Jun 12, 2026
Created by
Priya Shahpriya@example.com
Revision
3 · Sep 2, 2026
Spend this periodOpen in Budgets
$288of $600
Claude Code$288 of $600
3 of 4 on this page have spent.
History
  • Revision 3 · Claude Code
    Sep 2, 2026 · Priya Shah (priya@example.com)
  • Revision 2 · Claude Code
    Jul 21, 2026 · Priya Shah (priya@example.com)
  • Policy created
    Jun 12, 2026 · Priya Shah (priya@example.com)
A tools policy with Claude Code: its routing, budget, rules and models, the people on it, and its history. Select to enlarge.

A policy's page summarizes it:

AreaShows
PeopleHow many people are on the policy, and how many have set up a tool.
AI AgentsThe tools on the policy.
Spend this periodSpend against the per-person cap times the people set up, and when it resets.
RevisionThe live revision, and how many people are on an older one.
Tool sectionsEach tool's Route, Credentials, Budget, Budget rules and Models.
People tableEach person's name, spend per tool, total spend and revision, with a remove button that retires their agents.
HistoryEach revision, with its tools, date and who saved it.

Open in Budgets opens the budget of the policy's first tool. See Budgets & Spend Control.

Validation

A policy saves when each tool's settings fit together. The editor names the setting to fix, such as:

MessageFix
Pick at least one AI agent to create this policy.Add Claude Code or Claude Desktop.
Choose a route and region.Pick a route, and a region on routes that have one.
Choose a provider key for the Organization credential.Pick a provider key for the route.
Claude Desktop needs the company's credentials; pick Organization.Use the Organization Key for Claude Desktop.
Set a per-person cap.Enter a per-person cap.
The cap must be more than $0.Enter an amount above $0.
a policy with tag "x" already exists in this organizationChoose a different name.

Self-Service Provisioning

Assigning PoliciesEnterprise

Giving people Portal access with a tools policy, changing it, and taking it away.

A person uses the Portal in an organization when they have Portal access there with a tools policy. Portal access and the policy are set together, per person and organization, and a person has one policy in each organization. Portal access is separate from the dashboard role, so a person can have the Portal, the dashboard, or both.

Ways to assign a policy

A policy reaches people in four ways:

WhereHow
The policy pageAdd people, then choose a person and Add to policy.
An invitationTurn on Portal access for an organization and pick the Policy. See Inviting People.
A person's panelOn the Account Roster or the organization's People page, turn on Portal access and pick the Policy.
A user provisioning ruleA rule grants Portal access with a policy to people matched in the directory. See Rules.

On the policy page, a person on another policy is added after removing that policy; on a person's panel, picking a different policy moves them directly. A person with Portal access and No policy can sign in to the Portal, and sees that no tools are set up for them. When a user provisioning rule would change a person's policy, the change waits in Review. See Inviting People and Rules.

Changing a person's policy

Picking a different policy on a person's panel moves them to it. Agents for tools both policies grant keep their router and key and take the new policy's settings; agents for tools the new policy leaves out are retired. The panel shows how many agents a change retires, with their requests over the last 7 days, before saving; Save becomes Retire and save, and agents with recent traffic need I understand these agents will stop working ticked.

Removing a policy

Removing a person's policy, or their Portal access, retires their agents in that organization: their keys stop working and their routers are removed. The person runs setup again after another policy is assigned.

Telling people

An invitation with Portal access tells the person where to go, and after accepting they land on a Welcome page with Choose your first AI agent. For active people with Portal access, Resend portal link on their Account Roster panel emails them a link to the Portal.

Self-Service Provisioning

The PortalEnterprise

What people see in the Portal, and how they set up and use their tools.

The Portal is the page where people set up the AI tools their organization grants them, separate from the dashboard. It shows the tools each person can set up, the settings to paste into each, their budget and their key. People who only use the Portal sign in there, and are sent there from the dashboard.

Signing in

The Portal sign-in asks for the work email first, then signs the person in with single sign-on or a password. A person invited with Portal access sets a password, or uses SSO, when they accept. A person with access in more than one organization switches between them with Switch organization in the menu.

Your AI agents

The home page, Your AI agents, lists the tools the person has set up, with the period's spend, such as This month, At this pace with when the cap is reached at the current pace, their Status, and a hint of their key. Add an AI agent opens Choose an AI agent, which lists the tools the policy grants, with Set up on each, and the approved models and cap of tools already set up. A person with no policy sees No AI agents are set up for you yet, with a prompt to contact their IT department.

Setting up a tool

A tool is set up from its page in four steps:

  1. On Choose an AI agent, select Set up on Claude Code or Claude Desktop.
  2. Install the tool, following the first step on the page. For Claude Desktop, this includes turning on Developer Mode.
  3. Copy the settings under Point it at your organization's router, and paste them where the page shows.
  4. Check it works: run the tool, quitting and reopening Claude Desktop first, and ask it to say hello.

For Claude Code, the settings come in four forms, one per tab:

TabWhere the settings go
macOS / Linux~/.claude/settings.json
Windows%USERPROFILE%\.claude\settings.json
Environment variables~/.zshrc or ~/.bashrc
Managed by IT/Library/Application Support/ClaudeCode/managed-settings.json

For Claude Desktop, the page lists the fields to fill under Developer, then Configure Third-Party Inference, then Connection: Inference provider, Gateway base URL, Credential kind, Gateway API key, Gateway auth scheme and Custom inference headers, each with a copy button. The settings hide the key; Copy copies the working settings, and Reveal key shows it. See Claude Code and Claude Desktop.

A tool's page

Your AI agents

Claude Code

3 models approved · $200 per month · last used Sep 17
set up

Your budget

on track
Monthly · resets Oct 1, 00:00 UTC (in 14 days)
$64.20of $200
32%
Left
$135.80
Your pace
$4.52/day
average over the last 3 days
Cap reached
beyond reset
at this pace
Resets
in 14 days
Oct 1, 00:00 UTC

Need more room this month?

Ask Engineering for a higher limit until the month resets.
Request more budget

Spend by day

this month
Sep 1Sep 17

Set by Engineering IT

Cap
$200 per month, per person
Models
3 allowed, Opus 5 capped at $120
At 80%
notifies you
At 100%
requests paused until reset

Allowed models

3 models on your policy · 1 with its own cap
Claude Opus 5on track
anthropic/claude-opus-5
$41.30 of $120 this month, your own cap34%
last used Sep 17
Claude Sonnet 4.5
anthropic/claude-sonnet-4.5
$19.85 this month · counts toward the $200 total, no separate cap
last used Sep 16
Claude Haiku 4.5
anthropic/claude-haiku-4.5
$3.05 this month · counts toward the $200 total, no separate cap
last used Sep 9
Asking for any other model is refused. A model with its own cap pauses on its own when it reaches it; the others keep working until the $200 total is reached.
Your key …7Qx2 · issued Sep 2
Shown once when it was issued. Issuing a new key deletes this one immediately.
Issue a new key Remove

Install, set up and troubleshoot Claude Code

Install, point it at Engineering’s router, check it works, and what the error messages mean.
1

Install Claude Code

Install it, then come back here.
Install Claude Code using Anthropic's official installer.
2

Point it at Engineering’s router

Add these values to your settings. The router header is what routes you; without it the key works but nothing answers.
macOS / LinuxWindowsEnvironment variablesManaged by IT
~/.claude/settings.json
Reveal keyCopy
{
  "env": {
    "ANTHROPIC_BASE_URL": "https://api.multi-router.ai",
    "ANTHROPIC_AUTH_TOKEN": "sk-mr-••••••••••••••••",
    "ANTHROPIC_CUSTOM_HEADERS": "X-MR-Router: rt_8Kd2Pq6VnW"
  }
}
The key is hidden. Copy still copies the working settings.
3

Check it works

Run Claude Code and ask it to say hello.
If you get an answer, you are routed through Engineering and the request shows in Spend by day above within a few seconds. If not, see Troubleshooting below.

Troubleshooting

What Claude Code shows, and what it means.
“invalid api key”
The key in your settings is wrong or was replaced. Paste the current one, or issue a new key from Your key above.
“budget … is exhausted for this period”
You reached this period’s cap. It resets on the date shown above; nothing to change on your side.
“model ‘…’ is disabled on this router”
You asked for a model that is not on your policy. Use one under Allowed models.
A person's Claude Code page in the Portal: budget, allowed models, key, and the setup steps. Select to enlarge.

Each tool the person set up has its own page:

SectionShows
Your budgetSpend against the cap, what is left, the pace over the last 3 days, when the cap is reached at that pace, when it resets, and Set by your organization's IT with the cap, model caps and rules.
Allowed modelsEach model's spend, with its own cap or the shared total, and when it was last used.
Your keyA hint of the key and when it was issued, with Issue a new key and Remove.
Install, set up and troubleshootThe setup steps, and fixes for common errors.

When a budget pauses the tool, the page shows Paused until the reset, with the tool receiving 429 budget_exhausted until then; the key and access stay.

Requesting more budget

When the budget has request approvers, a cap and a period other than Lifetime, and no request waiting, Request more budget lets the person ask for a higher limit for the current period, with a New monthly limit above the current one and an optional message of up to 500 characters. The page then shows the request as pending, approved with the new limit, or not approved. See Overrides and Requests.

Keys

Issue a new key replaces the tool's key: the old one stops working at once, and the budget and usage carry over. Remove deletes the key and the tool's router, and Set it up again creates a new agent.

Self-Service Provisioning

Managing AgentsEnterprise

Admins' view of the agents people set up: keys, usage, retiring, and suspension.

An agent is one tool one person set up from the Portal: a Claude Proxy router named for the tool, and a key limited to that router. Account admins see and manage each person's agents on the Account Roster and the person's page, and organization admins see them on the organization's People page. Each agent's requests appear in Traffic under its router.

Where agents appear

Agents appear in three places, each with its own actions:

PlaceShowsActions
Account Roster, Agents cardOrganization, Policy, Created, Last used and Status of each agent.Rotate key, Retire.
The person's page, AI agents cardEach agent, and the tools the policy allows that are still to be set up.Rotate key, Setup help, Traffic, Retire.
Organization People, a person's Agents cardTool, Status, Budget and Last used.View only.
ActionEffect
Rotate keyIssues a new key and shows it once to copy. The old key stops working at once.
Setup helpShows the settings for the tool as the Portal shows them, with Reveal key.
TrafficOpens Traffic filtered to the agent's router.
RetireDeletes the key and removes the router. The person can set the tool up again from the Portal. An alias that points to the router is removed before retiring.

Agent routers

Each agent's router is a Claude Proxy in the organization, tagged with the person, the policy, and the policy's tool, such as person: with the person's id and policy: with the policy's tag. Its routing follows the policy, and changes come from editing the policy. Its spend counts toward the policy's budget for the tool.

Suspension and offboarding

Suspending a person disables their agents' keys and routers, and unsuspending enables them again. Removing a person's access retires their agents, and offboarding comes after that. See Person Lifecycle.

Activity

The account's activity records Portal access granted and revoked, policy changes such as Policy set to and Policy removed, agents created and retired, and keys rotated, with who did each and when.

Machine Identity

OverviewEnterprise

Letting workloads call Multi-Router with the identity their own platform gives them, in place of a stored API key.

Machine identity lets an application authenticate to Multi-Router with a short-lived token its own platform issues, such as a Kubernetes service account token or a GitHub Actions OIDC token, instead of an API key stored as a secret. The platform signs the token, Multi-Router checks the signature and the token's subject on every request, and the caller reaches only the routers its binding allows. The platform's own short-lived tokens are the only credential, so there is nothing for the team to store or rotate.

Issuers and bindings

Machine identity has two parts, each set on the Machine identity page in Settings:

PartBelongs toPurpose
IssuerThe accountA platform that signs tokens, such as an EKS cluster or GitHub Actions, registered by its issuer URL. Each issuer gets a token audience that its tokens must carry.
BindingAn organizationA caller, identified by the token's subject such as system:serviceaccount:payments:api, and the routers it may use.

An issuer is registered once for the account and authorized for the organizations that may bind callers to it. Each organization then creates bindings for its own callers and routers.

How a request is authenticated

Every machine request is checked in four steps:

  1. The workload asks its platform for a token for the issuer's token audience, and sends it as Authorization: Bearer.
  2. Multi-Router finds the issuer by the audience, and checks the token's issuer, signature, expiry and lifetime.
  3. It finds the binding that matches the token's subject, and checks that the binding allows the router the request names.
  4. The request is served by that router, and Traffic records the issuer, subject and binding.

Machine identity and API keys

Machine identity and API keys both authenticate callers, for different kinds of caller:

API keyMachine identity
CredentialA long-lived sk-mr- key stored as a secretA short-lived token the platform issues on demand
RotationReplace the keyAutomatic, as the platform issues new tokens
ReachChosen scopes and routersInference, and optionally listing models, on the binding's routers
Identity in TrafficThe key idThe issuer, subject and binding
Suited toScripts, tools, and applications on other platformsWorkloads in Kubernetes clusters and CI jobs

Supported platforms

Machine identity works with any issuer that publishes an OpenID Connect discovery document and signing keys at a public https address:

PlatformIssuer type in the dashboardSubjects
Amazon EKS clusterAWS EKS clusterKubernetes service accounts, exact or by namespace
Google GKE clusterGoogle GKE clusterKubernetes service accounts, exact or by namespace
Azure AKS cluster, with its OIDC issuer turned onAzure AKS clusterKubernetes service accounts, exact or by namespace
GitHub ActionsGitHub ActionsWorkflow runs, exact or by repository
GitLab.comGitLab.comExact CI job subjects
CircleCICircleCIExact CI job subjects
Google AccountsGoogle AccountsExact subjects
Any other OpenID Connect issuerCustom issuerExact subjects

The other pages in Machine Identity cover each part in detail:

PageCovers
IssuersRegistering issuers, token audiences, key fetching and health.
BindingsSubjects, matching, routers and permissions.
KubernetesCalling Multi-Router from a pod with a service account token.
GitHub ActionsCalling Multi-Router from a workflow with an OIDC token.
Calling Multi-RouterToken requirements, router selection, limits and Traffic.
TroubleshootingAuthentication errors, with causes and fixes.

Machine Identity

IssuersEnterprise

Registering the platforms that sign your workloads' tokens, and keeping them healthy.

An issuer is a platform that signs tokens for workloads, such as a Kubernetes cluster or GitHub Actions, registered in Multi-Router by its issuer URL. Issuers belong to the account and are managed on the Issuers tab of Machine identity in Account Settings. Multi-Router reads the issuer's signing keys from its discovery document and uses them to check every token.

Registering an issuer

An issuer is registered in five steps:

  1. Open Settings in the account, then Machine identity, and on the Issuers tab select Register issuer.
  2. Enter a Display name, such as Production cluster, and the Issuer URL.
  3. Choose the setting for when the issuer cannot be reached, and the Maximum token lifetime.
  4. Add the Authorized organizations whose callers will use this issuer.
  5. Select Fetch keys and register. Multi-Router reads the discovery document and signing keys, and registers the issuer.

The Issuer URL is the token's iss value, such as https://oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE or https://token.actions.githubusercontent.com. Its discovery document at /.well-known/openid-configuration must be reachable over public https and report the same issuer. As the URL is typed, the form shows how Multi-Router classifies it.

Fields

An issuer has these settings:

FieldDescription
Display nameA name for the issuer, such as Production cluster.
Issuer URLThe issuer, fixed after registration. Trusting a different system means registering it separately.
If the issuer cannot be reachedKeep serving with cached keys, the default, or Reject requests. See Key fetching below.
Maximum token lifetime (seconds)Tokens valid for longer than this are refused. The default is 7200, and the maximum 86400 (24 hours).
Authorized organizationsThe organizations that may create bindings on this issuer, up to 48.
EnabledA disabled issuer stops authenticating its callers within about a minute.

Token audience

example-co · acc_7Tq2Wd9KxP

Account Settings

Machine identity

Let applications authenticate with tokens their own platform issues, instead of an API key.

Register issuer
Issuers1Bindings0
Production clusterHealthyhttps://oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE
Token audience
mr-aud_PtYgjmUhBel31iEl2hpChYgCfrL1spNxnyVmihA_2O7Copy

Put this in the pod spec. Tokens from this issuer are only accepted if they carry it.

Issuer type
AWS EKS cluster
If the issuer cannot be reached
Keep serving with cached keys
Maximum token lifetime
7,200 seconds

Authorized organizations

Only authorized organizations can create bindings on this issuer.

Engineeringorg_3Fh8Wq2LdK

Bindings using this issuer

2 bindings across 1 organization. Read-only — bindings are created and deleted inside each organization.

system:serviceaccount:payments:apiEngineering · mib_7Hn2Qw5RtX
system:serviceaccount:billingEngineering · mib_K4vB9mZp3L
Delete issuer Edit
A registered EKS cluster issuer: its token audience, settings, authorized organization and the bindings that use it. Select to enlarge.

Each issuer receives a token audience when it is registered, a value starting with mr-aud_ shown under Token audience with a Copy button. Workloads request tokens for this audience from their platform, and Multi-Router accepts a token only when it carries the audience. The audience identifies the issuer registration; it is safe to place in configuration such as a pod spec or a workflow file.

Issuer types

Multi-Router classifies each issuer from its URL, which decides how bindings can match subjects:

TypeExamplesBindings can
ClusterAWS EKS cluster, Google GKE cluster, Azure AKS clusterMatch an exact service account, or every service account in a namespace.
Shared platformGitHub ActionsMatch an exact subject, or every run of a named repository.
Shared platformGitLab.com, CircleCI, Google AccountsMatch exact subjects.
Custom issuerAny other issuerMatch exact subjects.

A shared platform issues tokens for every one of its customers, so bindings on it name a specific repository or subject. A cluster issues tokens only for its own workloads.

Key fetching

Multi-Router keeps the issuer's signing keys, and fetches them again on the first request after they are 10 minutes old, and at once when a token is signed with a new key. The setting for when the issuer cannot be reached decides what happens when a fetch fails:

SettingBehavior
Keep serving with cached keysRequests keep being checked with the last keys fetched, for up to 24 hours. A key the issuer revoked stays trusted for that time.
Reject requestsAuthentication stops until the keys can be fetched again, with the next attempt up to 10 minutes later.

Health

Each issuer card shows a health badge:

BadgeMeaning
HealthyKeys are current.
Loading keysKeys are being fetched.
DegradedThe last refresh failed, and cached keys are in use.
Failed closedThe last refresh failed, and requests are refused.
Stale keys expiredThe keys were last fetched more than 24 hours ago, such as after a quiet day. The next request fetches them again.
Unknown key IDs suppressedMany tokens used unknown keys, and Multi-Router paused fetching for them.

Editing, disabling and deleting

Edit changes everything except the Issuer URL. The Enabled toggle on the card turns the issuer off and on, within about a minute. Delete issuer removes the registration permanently once its bindings and organization authorizations are removed; the card says how many remain, and which organizations hold the bindings. An account holds up to 100 issuers.

Machine Identity

BindingsEnterprise

Naming the callers that may use an issuer's tokens, and the routers each may reach.

A binding connects a caller, identified by the subject in its tokens, to the routers it may use. Bindings belong to an organization and are managed under Machine identity bindings in that organization's settings; bindings on the account's own routers are on the Bindings tab in Account Settings. An organization creates bindings on issuers it is authorized for.

Creating a binding

Engineering · org_3Fh8Wq2LdK

Org Settings

Machine identity bindings

Which callers may reach which routers in Engineering, and what they are allowed to do.

Callers connect to https://api.multi-router.ai Copy

New binding

Identify the caller, then choose what it may reach.

Issuer
Token audience
mr-aud_PtYgjmUhBel31iEl2hpChYgCfrL1spNxnyVmihA_2O7Copy

Put this in the pod spec. Tokens from this issuer are only accepted if they carry it.

Routers this binding may use
Payments classifieropenai / openai/gpt-5-mini · rt_8Mc5Jx3VbN
Add router

With one router no routing header is sent — just a base URL and a credential.

What this binding allows
CancelCreate binding
system:serviceaccount:billingprefixkubernetes service accountProduction cluster · 2 routers · inference
A new binding for a Kubernetes service account, with one router. Select to enlarge.

A binding is created in six steps:

  1. In the organization, open Settings, then Machine identity, and select Add binding.
  2. Choose the Issuer, and the Caller type: Kubernetes service account or CI job.
  3. Choose the Match, This exact subject or Anything under a prefix, and enter the Subject.
  4. Under Routers this binding may use, add one or more routers.
  5. Under What this binding allows, Send inference requests is always granted; turn on List available models when the caller lists models.
  6. Select Create binding.

Subjects

The subject is the token's sub value, which names the caller. Its form depends on the platform:

PlatformSubject example
Kubernetessystem:serviceaccount:payments:api
GitHub Actions, a branchrepo:example-co/billing:ref:refs/heads/main
GitHub Actions, a job with an environmentrepo:example-co/billing:environment:production
GitHub Actions, a pull requestrepo:example-co/billing:pull_request

Subjects are case sensitive, up to 700 bytes, with no surrounding spaces. A GitHub job that declares an environment uses the environment form, so a binding for a branch matches jobs without one; an organization that customizes GitHub's subject format uses its own form.

Matching

A binding matches subjects in one of two ways:

MatchMatchesAvailable on
This exact subjectOne subject.Every issuer.
Anything under a prefixThe prefix itself, and subjects that continue it after a colon.Cluster issuers and GitHub Actions.

Prefixes follow these rules:

IssuerPrefix ruleExample
ClusterStops after the namespace, and covers every service account in it.system:serviceaccount:payments
GitHub ActionsStarts with a complete owner and repository, and can go deeper.repo:example-co/billing

Prefixes match at the colon between parts, so repo:example-co/billing covers repo:example-co/billing:ref:refs/heads/main and leaves repo:example-co/billing-v2 out. Prefixes are literal text, without wildcards such as *. When several bindings in an organization match a subject, an exact binding wins, then the longest prefix, and the binding chosen applies even when it is disabled.

Routers

A binding names 1 to 100 routers of its organization, by router id rather than alias. With one router, a caller sends only the base URL and its token, and requests go to that router. With several, each request names its router in the model field as multi-router/rt_9Hk3Ws7PzQ or in the x-mr-router header; when both are present, the model field wins.

Permissions

A binding allows inference, and optionally listing models:

PermissionScopeAllows
Send inference requestsinferenceRequests to the binding's routers. Always on.
List available modelsmodels:readGET /v1/models, listing the models of the binding's router, or of the router named in x-mr-router when it has several.

Bindings across organizations

A subject belongs to one organization: a binding that overlaps another organization's binding, as the same subject or one prefix inside another, is refused with a message naming the organization. Within one organization, overlapping bindings are allowed and resolved by the matching order above, and each subject and match pair is bound once.

Kubernetes UID changes

For an exact Kubernetes binding, Multi-Router remembers the service account's unique id from its first token. When a later token carries a different id, the binding shows UID changed, meaning the service account was deleted and recreated, or the name now belongs to something else. Requests keep being served; review the binding and disable it if the change was unexpected.

Editing and deleting

Editing a binding changes everything except its issuer. Binding enabled turns it off and on, and deleting it stops the caller from authenticating. Changes take effect across Multi-Router within about a minute. An issuer holds up to 1,000 bindings.

Machine Identity

KubernetesEnterprise

Calling Multi-Router from a pod with a projected service account token.

A pod in a Kubernetes cluster authenticates to Multi-Router with a projected service account token: a token the cluster issues to the pod's service account for a chosen audience, and rotates on its own. The cluster is the issuer, and the service account is the subject. This page uses an EKS cluster; GKE and AKS clusters work the same way with their own issuer URLs.

Registering the cluster

The cluster is registered once, as an issuer:

  1. Find the cluster's OIDC issuer URL. For EKS, it is shown on the cluster's Overview tab as OpenID Connect provider URL.
  2. Register it as an issuer in Multi-Router, and authorize the organization that owns the routers. See Issuers.
  3. Copy the issuer's token audience.

Binding the service account

In the organization, add a binding with Caller type Kubernetes service account and the subject system:serviceaccount:payments:api, for the service account api in the namespace payments, with the router it calls. See Bindings.

Configuring the pod

The pod mounts a projected token for the issuer's audience, with a lifetime at or below the issuer's maximum token lifetime:

Pod specyaml
apiVersion: v1
kind: Pod
metadata:
  name: api
  namespace: payments
spec:
  serviceAccountName: api
  containers:
    - name: api
      image: example-co/payments-api:1.4.0
      env:
        - name: MR_BASE_URL
          value: https://api.multi-router.ai/v1
      volumeMounts:
        - name: mr-token
          mountPath: /var/run/secrets/multi-router
          readOnly: true
  volumes:
    - name: mr-token
      projected:
        sources:
          - serviceAccountToken:
              path: token
              audience: mr-aud_EXAMPLEp3Qk9Zt2Lw8Vd4Nc6Ys1Hb7Rm0Xf5Jg2TaQe
              expirationSeconds: 3600

The application reads the token from the file for each request, since the cluster replaces it before it expires, and sends it as a bearer token:

A request from the podbash
curl $MR_BASE_URL/chat/completions \
  -H "Authorization: Bearer $(cat /var/run/secrets/multi-router/token)" \
  -H "Content-Type: application/json" \
  -d '{ "messages": [{ "role": "user", "content": "Classify this payment dispute." }] }'

With one router on the binding, the request needs no model or router name. With the OpenAI SDK, set the base URL and pass the token file's contents as the API key, read again before it expires.

Every service account in a namespace

A binding with Anything under a prefix and the subject system:serviceaccount:payments covers every service account in the payments namespace, so new services there reach the binding's routers without a new binding.

Machine Identity

GitHub ActionsEnterprise

Calling Multi-Router from a GitHub Actions workflow with its OIDC token.

A GitHub Actions workflow authenticates to Multi-Router with the OpenID Connect token GitHub issues to each run. GitHub is the issuer, and the subject names the repository and what triggered the run, such as a branch or an environment. The workflow's only credential is that token.

Registering GitHub Actions

Register https://token.actions.githubusercontent.com as an issuer, authorize the organization that owns the routers, and copy the token audience. See Issuers.

Binding the repository

Add a binding with Caller type CI job, and one of these subjects:

SubjectMatchCovers
repo:example-co/billing:ref:refs/heads/mainThis exact subjectRuns on the main branch.
repo:example-co/billing:environment:productionThis exact subjectJobs in the production environment.
repo:example-co/billingAnything under a prefixEvery run of the repository.

Prefixes on GitHub Actions name a complete owner and repository, because GitHub issues tokens for every repository on GitHub. See Bindings.

The workflow

The workflow requests the id-token: write permission, fetches a token for the issuer's audience, and sends it as a bearer token:

.github/workflows/summarize.ymlyaml
name: Summarize release notes
on:
  push:
    branches: [main]
permissions:
  id-token: write
  contents: read
jobs:
  summarize:
    runs-on: ubuntu-latest
    steps:
      - name: Get a Multi-Router token
        run: |
          TOKEN=$(curl -s -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
            "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=mr-aud_EXAMPLEp3Qk9Zt2Lw8Vd4Nc6Ys1Hb7Rm0Xf5Jg2TaQe" | jq -r .value)
          echo "::add-mask::$TOKEN"
          echo "MR_TOKEN=$TOKEN" >> "$GITHUB_ENV"
      - name: Call Multi-Router
        run: |
          curl https://api.multi-router.ai/v1/chat/completions \
            -H "Authorization: Bearer $MR_TOKEN" \
            -H "Content-Type: application/json" \
            -d '{ "messages": [{ "role": "user", "content": "Summarize the changes in this release." }] }'

GitHub's tokens last a few minutes, within the issuer's maximum token lifetime. A long job fetches a new token before the old one expires.

Machine Identity

Calling Multi-RouterEnterprise

Token requirements, router selection, endpoints, limits, and how machine requests appear in Traffic.

A workload with a binding calls Multi-Router like any caller, with its platform token in place of an API key. The token goes in the Authorization header as a bearer token, and Multi-Router recognizes it as a signed token and checks it against the issuer and binding.

Token requirements

Multi-Router accepts a token that meets every one of these requirements:

RequirementDetail
audIncludes the issuer's token audience.
issEquals the registered Issuer URL.
subMatches a binding, which is enabled.
exp and iatPresent, with the token currently valid, allowing 60 seconds of clock difference.
LifetimeNo longer than the issuer's maximum token lifetime.
kidNames one of the issuer's signing keys.
Signing algorithmRS256, RS384, RS512, PS256, PS384, PS512, ES256 or ES384.

Endpoints

Machine identity works on every inference endpoint: Chat Completions, Messages and token counting, and the Claude Proxy and Pi Proxy endpoints. With List available models, it also works on GET /v1/models. On a Claude Proxy with the Developer credential source and on a Pi Proxy, the token goes in X-MR-Api-Token, since Authorization carries the provider login. Management endpoints, such as /v1/routers, are for API keys.

Choosing the router

A binding with one router sends every request to it. With several, each request names one, in the model field as multi-router/rt_9Hk3Ws7PzQ or in the x-mr-router header. A router outside the binding returns 404 the requested model or router was not found, the same as for a router that is absent. On the Claude Proxy's Bedrock and Vertex endpoints the message is Claude Proxy not found.

Rate limits

Each binding and subject has the same rate limit as an API key, 300 inference requests per minute, so each workload has its own allowance. See Rate Limit.

Traffic and budgets

Traffic records each machine request with its identity type, issuer, subject and binding, shown in the request detail as Subject type, Machine issuer, Machine subject and Machine binding. Machine issuer, Machine subject and Machine binding are also filters, and all four are optional columns. Machine requests count toward the budgets of the routers they use; a router for each workload gives each workload its own budget. See Request Detail and Budgets & Spend Control.

Machine Identity

TroubleshootingEnterprise

Machine identity authentication errors, with their causes and fixes.

A machine request that fails authentication returns 401, or 503 when signing keys or bindings are briefly unavailable. On the OpenAI-format endpoints, such as Chat Completions, Models and Responses, a 401 has the type authentication_error and code invalid_machine_token, and a 503 has the type server_error and code machine_identity_unavailable. Messages and the Claude Proxy endpoints use their own error format with the same message.

Errors

Each message points to one part of the setup:

MessageCause and fix
invalid or missing machine credentialThe token is malformed, or its audience matches no issuer. Request the token for the issuer's token audience.
machine identity issuer is disabledThe issuer is turned off. Enable it.
machine token issuer does not match the registered issuerThe token's iss differs from the Issuer URL. Check for a trailing path or a different cluster.
machine token uses an unsupported signing algorithmThe platform signs with an algorithm outside the supported list.
machine token has no usable key identifierThe token has no kid header. Use a platform that sets it.
machine issuer does not recognize the token signing keyThe token's key is missing from the issuer's published keys.
machine issuer signing keys are temporarily unavailableThe issuer cannot be reached and Reject requests is set, or cached keys expired. Check the issuer card.
machine token signature is invalidThe signature fails verification, or the signing key's type differs from the token's algorithm.
machine token is expired or outside its valid time windowThe token expired, or the workload's clock is more than 60 seconds ahead. Read or request a fresh token for each request.
machine token exceeds the issuer lifetime limitThe token is valid for longer than the issuer's Maximum token lifetime. Shorten the token or raise the limit.
machine token has no caller binding for this subjectNo enabled binding matches the token's subject. Check the subject in the binding.
machine caller binding is disabledThe binding chosen for the subject is turned off. Enable it.
machine identity authority is temporarily unavailableMulti-Router could not read bindings just now. Retry.
machine identity is not permitted to use this endpointStatus 403, code insufficient_scope. The endpoint needs a scope the binding lacks, such as List available models, or is a management endpoint.
the requested model or router was not foundThe request names a router outside the binding, names no router when the binding has several, or names a model with no router.

Finding the subject and audience

Decoding a token shows its claims. The token's middle part is base64url-encoded JSON, which this Python line prints:

Print a token's claimsbash
python3 -c 'import sys,base64,json; p=open(sys.argv[1]).read().split(".")[1]; print(json.dumps(json.loads(base64.urlsafe_b64decode(p+"="*(-len(p)%4))),indent=2))' token

Compare its iss, sub, aud and expiry with the issuer's URL and token audience and the binding's subject.

Budgets & Spend Control

OverviewEnterprise

Capping and tracking spend on AI models, with notifications and blocks at the points you choose.

A budget tracks what an organization spends on AI models over a period and acts at the points you set. Each budget covers part of the organization's traffic, such as everything, a list of routers, or every router with a tag, and holds caps on spend, in total, per router and per model. Rules on the budget decide what happens as spend approaches a cap: an email, a webhook, or blocking requests until the period resets.

Parts of a budget

A budget is made of these parts:

PartPurpose
ScopeWhich traffic the budget covers: the whole organization, a list of routers, or routers with a tag.
PeriodDaily, weekly or monthly, reset at 00:00 UTC, or lifetime, which never resets.
CapsA total cap, caps per router, and caps per model.
RulesWhat happens at a point of a cap: notify by email or webhook, or block.

Tracking and enforcing

A budget's Notify rules track spend and send notifications; a Block rule refuses requests once spend reaches its line, until the period resets or the cap is raised. Multi-Router checks budgets before each request and updates spend every few seconds, so a block takes effect within seconds of the line. Requests already running when the line is crossed finish and are counted, so spend can pass a cap by the cost of those requests.

What counts

Spend is the cost of each inference request, worked out from the model's price as in Traffic. Every request a router serves counts: failed requests count $0, and a stream the caller closed early counts the usage that had arrived. Token counting is free and stays outside budgets. Requests to models with no price count at $0. See Cost.

Several budgets

Every enabled budget that covers a request is checked on its own, and a request is refused when any one of them blocks it. A router in several budgets therefore stops at the lowest line among them. Raising one budget lifts only that budget's block.

Budgets from tools policies

Each tool in a tools policy has a budget, created and managed by the policy, that caps each person's spend on the tool. These budgets appear on the Budgets page as Managed by policy, and their caps and rules change by editing the policy. See Tools Policies.

The other pages in Budgets & Spend Control cover each part in detail:

PageCovers
Creating a BudgetScopes, caps and periods.
Rules and AlertsNotifications and blocks.
WebhooksSending budget alerts to your own systems.
Overrides and RequestsRaising a cap for one period, and requests for more.
Status and UsageThe Budgets page, status labels and usage charts.
When a Budget BlocksWhat callers receive, on each endpoint.
Budgets APIManaging budgets with /v1/budgets.

Budgets & Spend Control

Creating a BudgetEnterprise

Choosing a budget's scope, caps and period.

Budgets are created on the Budgets page, opened from Budgets in the top navigation, for the selected organization. New budget opens the form, with the Budget, Caps and Rules cards on the left and a live preview of the covered traffic on the right.

Steps

Creating a budget takes four steps:

  1. On the Budgets page, select New budget, and enter a Name of up to 120 characters, such as Production API.
  2. Choose the Scope, and add routers or tag rules for those scopes.
  3. Set the caps and the Period.
  4. Add rules, and select Create budget.

Scope

A budget has one scope:

ScopeCoversLines
OrgEverything the organization sends, including requests that name a model with no router.None.
RoutersOnly the routers you list.Up to 200 routers, each with its own cap.
Tag rulesEvery router carrying a tag you list, including routers tagged later.Up to 50 tags, each with a cap for each router.

A request counts toward the router it was sent to: for a fallback or weighted router, that is the fallback or weighted router itself. A router that matches several tag rules counts once toward the total, and its own cap is the lowest among the rules. See Router Settings.

Caps

A budget holds up to three kinds of cap, all optional:

CapLimits
Total capSpend across everything the budget covers.
Router or tag capSpend on each router, set on each line.
Model capsSpend on one model, up to 50 models. On an Org budget, across the organization; on a Routers or Tag rules budget, on each router, inside its cap.

A budget with lines and no total cap lets each line stand alone; a total cap below the sum of the lines makes the lines share it. A cap left empty means no cap. Model caps are catalog slugs, and on a Routers or Tag rules budget each model cap fits inside every per-router cap. New budgets start with the Routers scope.

Period

The period decides when spend resets, at 00:00 UTC:

PeriodResets
DailyEvery day.
WeeklyEvery Monday.
MonthlyOn the 1st of each month. This is the default.
LifetimeNever.

Example

A team caps its production support bots at $5,000 a month in total, with no single router over $2,000, and keeps its most expensive model under $1,000. It creates a Tag rules budget for the tag production with a per-router cap of $2,000, a total cap of $5,000, the period Monthly, and a model cap of $1,000 on its most expensive model. A Notify rule at 80% of the total emails the team lead, and a Block rule at 100% of each router's cap stops a runaway router while the others keep serving.

Changing and deleting

Edit budget, the pencil on a budget's row, opens the same form, and Save changes applies the change at once. Turning off Enabled pauses the budget's tracking and blocking while keeping it. Delete budget, the trash icon on the row, removes the budget after a confirmation. Budgets managed by a policy change through the policy.

Budgets & Spend Control

Rules and AlertsEnterprise

Notifying people and blocking requests at the points of a cap you choose.

A rule says what happens when spend reaches a point of a cap, such as an email at 80% of the total or a block at 100%. Rules are set in the Rules card of a budget, up to 50 per budget, and a budget's caps only track spend until a rule says what happens at a line.

Kinds of rules

A rule either notifies or blocks:

RuleActionEffect
Notifysend an email toEmails a person in the organization once per period.
Notifypost toSends a webhook to an endpoint once per period.
Blockblock requestsOn the total cap: refuses every request the budget covers until the period resets.
Blockblock that routerOn router caps: refuses the router that crossed its line, while the others keep serving.
Blockblock that modelOn model caps: refuses that model only, while the others keep serving.

When a rule acts

Each rule watches one cap and acts at a percentage of it:

SettingValues
WatchesThe total cap, each per-router cap, each model cap, or one model's cap.
When spend reachesA percentage of the cap, from 0.01% to 200%. New rules start at 80%.
When projected to reachOn the total cap: when the current pace reaches the percentage within a chosen number of minutes, 30 by default, up to a week.

A projected rule warns before the line is crossed, such as an alert when the total is projected to reach 100% within 60 minutes. A rule can hold several actions, such as an email, a webhook and a block together.

Firing

A notification fires once per period for each subject it watches, such as each router: the rule shows Firing while it delivers, then Fired this period. A delivery that fails shows Fired · failed, and the rule fires again in a later period when spend reaches the line again. A block holds for as long as spend stays over its line, and the rule shows Holding; raising the cap or the period resetting lifts it.

Email notifications

An email notification goes to the address entered on the rule, which belongs to a person active in the organization, with the subject Production API has reached 80% of its budget and a link to the budget. The Rules card offers email and webhook actions when the installation can send them.

Example

A typical set of rules on a monthly budget:

RuleWatchesAtAction
1Total cap80%Email the team lead.
2Total capProjected 100% within 120 minutesPost to the on-call webhook.
3Each per-router cap100%Block that router.
4Total cap110%Block requests.

Budgets & Spend Control

WebhooksEnterprise

Sending budget alerts to your own systems, and verifying them.

A webhook endpoint is an https address where Multi-Router posts budget alerts, such as a chat channel's incoming webhook or an internal alerting service. Endpoints belong to an organization and are managed under Webhook endpoints in its Settings, and budget rules use them with the post to action.

Adding an endpoint

An endpoint is added in three steps:

  1. Open Settings, then Webhook endpoints, and select New endpoint.
  2. Enter a Name, such as Ops alerts, and the URL, such as https://hooks.example.com/mr.
  3. Select Create, and copy the signing secret, which is shown once.

The list shows each endpoint's URL, how many rules use it under Used by, and its last delivery. Rotate secret, then Generate and replace, replaces the secret, and the new one is shown once. An endpoint is deleted once no rule posts to it, and the post to action is offered once the organization has an endpoint.

Delivery

Multi-Router posts each alert as JSON over https with a verified certificate, and waits up to 10 seconds for a response. A timeout, a connection error or a 408, 425, 429 or 5xx response is retried, up to three attempts in all. Each alert has an id that stays the same across attempts, so the receiver can ignore a repeat.

Payload

Each alert is a JSON object with the alert's id, type, organization and details:

budget.threshold_reachedjson
{
  "id": "budget:bdg_4Rk8Vb2NwQ#monthly:2026-10@UTC#rule_1#rt_9Hk3Ws7PzQ#act_3f9c2a7e1b4d8065",
  "type": "budget.threshold_reached",
  "organization_id": "org_7Tq2Wd9KxP",
  "data": {
    "budget_id": "bdg_4Rk8Vb2NwQ",
    "budget_name": "Production API",
    "period": "monthly:2026-10@UTC",
    "rule_id": "rule_1",
    "action_index": 0,
    "subject": "rt_9Hk3Ws7PzQ",
    "watch": "per_router",
    "model": null,
    "spend_micros": 1620000000,
    "cap_micros": 2000000000,
    "threshold_micros": 1600000000,
    "threshold_ratio": 0.8,
    "remaining_micros": 380000000,
    "burn_rate_micros_per_min": 950000,
    "projected_cap_at": "2026-10-09T18:40:00Z",
    "period_resets_at": "2026-11-01T00:00:00Z",
    "label": "nearing"
  }
}

Amounts are in millionths of a US dollar, so 2000000000 is $2,000. subject is what crossed the line: the budget id for the total cap, the router id for router caps, and the model, or the router and model, for model caps. period names the period, such as daily:2026-10-09@UTC or lifetime@UTC. remaining_micros, projected_cap_at and period_resets_at can be null, and label is the budget's status.

Verifying the signature

Each delivery carries the header x-multi-router-signature-256 with sha256= and the hex HMAC-SHA256 of the raw body, keyed with the endpoint's signing secret. The receiver computes the same value and compares them:

Verify a deliverypython
import hmac, hashlib

def verify(body: bytes, header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, header)

Requests also carry Content-Type: application/json and User-Agent: multi-router-webhook/1.

Budgets & Spend Control

Overrides and RequestsEnterprise

Raising a cap for the current period, and letting people ask for more.

A budget override replaces one of a budget's caps for the current period only, such as raising a router's $2,000 cap to $3,000 for a launch month. Overrides are set from the budget's row on the Budgets page, and end when the period resets. People can also request more budget, which the budget's approvers approve or deny.

Setting an override

The Budget Overrides section of a budget's row lists the overrides for this period, with the Target, Base limit, Effective limit, Change and who set it. An override targets the total cap, one router's cap, one model's cap on an Org budget, or one model on one router, and sets a new absolute amount above $0, higher or lower, for a cap that exists. Rules then act on the new cap, and a rule that already fired can fire again against it. Overrides apply to daily, weekly and monthly budgets.

Requests for more budget

People on a tools policy can ask for a higher limit for the current period from the Portal, with Request more budget, one request at a time, for an amount above their current limit and an optional message. Request approvers, up to 10 email addresses set on the budget's row, receive an email about each request. Pending requests appear on the budget's row, where an admin of the organization approves them, with an amount they can adjust, or denies them; an approved request becomes an override of the person's router cap for the period.

Budgets from tools policies

A policy's budgets take overrides and approvers on the Budgets page, while their caps and rules come from the policy. Setting approvers there lets people on the policy request more from the Portal. See The Portal.

Budgets & Spend Control

Status and UsageEnterprise

The Budgets page, status labels, and each budget's usage.

The Budgets page lists the organization's budgets with their spend and status for the current period, and each budget's usage page charts its spend over time. Amounts are in US dollars, with periods measured in UTC.

The Budgets page

Engineering · org_3Fh8Wq2LdK

All budgets

New budget
Status:
allnearingimminentcapped
Search budgets…
Total spend
$18,580
Effective limits
$28,500
Capped
1
Imminent
0
Nearing
1
Unpriced traffic
0.4%
Budgets
1–5 of 525 / page
ColumnsUpdated
NameScopePeriodSpend / effective limitUsedStatusUpdated
Production API
bdg_7Qm2Xc9LpR
Tag · productionMonthly
$4,310/$5,000
86%Nearing2h ago
Production APIbdg_7Qm2Xc9LpRTag · productionMonthly
Detailed usage →
Spent
$4,310
Effective limit
$5,000
Remaining
$690
Per-router limit
$2,000base
Period
Sep 1 – Sep 30resets Oct 1
Pending requests
0
Routers over cap
0 of 4
Spend per day · daily · Sep 1 – Sep 30
SpendEffective limit/day
$0$100$200$300$400
Effective $166.67/day
Sep 1
Sep 8
Sep 15
Sep 22
Sep 29
Request approvers
Requests on
Emailed when someone asks for a higher limit in the Portal. The Portal hides its request button while this list is empty.
priya@example.com
name@example.com
Add
1 of 10
Budget Overrides · this period
Sep 1 – Sep 30 · UTC. Each override replaces one limit until the period ends.
Add Budget Override
TargetBase limitEffective limitChangeSet by
No Budget Overrides this period. Every limit is at its base.
Pending requests
Approving creates a Router override for this period. Adjust the amount before approving.
No pending requests.
Standard Engineering · claude-codePolicy1 request
bdg_2Lk5Pz8NcV
Tag · policy:standard-engineeringMonthly
$288/$200 per router
—No cap1d ago
Support routers
bdg_9Bv3Rt6YfJ
Routers · 3Monthly
$1,500/$1,500
100%Capped3d ago
Engineering org
bdg_4Hn8Ws1KdT
OrgMonthly
$11,842/$20,000
59%On track6d ago
Research sandbox
bdg_6Tx1Jm4GqW
Routers · 1Lifetime
$640/$2,000
32%Lifetime12d ago
Limits are server-resolved effective limits for the current UTC period.
The Budgets page with a budget open: status tiles, budgets of each scope, and the open budget's spend, approvers and overrides. Select to enlarge.

Tiles across the top show Total spend, Effective limits, and how many budgets are Capped, Imminent and Nearing, with the share of Unpriced traffic. The rail filters by status, overrides, pending requests, Managed by policy and scope. The table lists each budget's Name, Scope, Period, Spend / effective limit, Used, Status and when it was Updated. Opening a row shows Spent, Effective limit, Remaining, its limits, Period, Pending requests and Routers over cap, with its overrides and approvers.

Status

Each budget has a status for the current period:

StatusMeaning
On trackSpend is on pace to stay under the total cap.
OverSpend is at or above the total cap, with no Block rule holding.
NearingAt the current pace, spend reaches the total cap before the period ends.
ImminentSpend is at 90% of the total cap, or projected to reach it within the longest projection window of the budget's rules, or 30 minutes.
CappedA Block rule is holding.
No capThe budget has no total cap. Its router and model caps and their rules still apply.
LifetimeThe budget never resets.
Check tagA tag rule matches no routers.
UnavailableSpend could not be read just now.

Usage

Detailed usage on a budget's row opens its usage page. It shows Spent, Effective limit, Remaining, Used, Period and Resets, a Spend over time chart by hour or day or cumulative against the limit, spend by router with each router's share, and the top models by spend.

Spend and request logging

Spent, the Spend column and the tiles count every request. The usage chart, the router table and the model breakdown come from request records, so they include a router's requests when the router records them; for routers set to log failures only or nothing, the breakdowns add up to less than Spent. See Request Logging.

Budgets & Spend Control

When a Budget BlocksEnterprise

What callers receive when a budget refuses a request, on each endpoint.

A budget block refuses requests with status 429 before they reach a provider. The response names the budget by id and says when it resets, and the Retry-After header gives the seconds until then, so clients can wait or switch work. Requests already running when the block starts finish normally.

The message

A budget with a period, and a lifetime budget, return these messages:

BudgetMessageRetry-After
Daily, weekly or monthlybudget bdg_4Rk8Vb2NwQ is exhausted for this period; it resets at 2026-11-01T00:00:00ZSeconds until the reset.
Lifetimebudget bdg_4Rk8Vb2NwQ is exhausted3600

Response by endpoint

Each endpoint returns the block in its own error format:

EndpointBody
Chat Completions, Responses, and the Pi Proxy OpenAI endpoints{"error":{"message":"…","type":"budget_exhausted","code":"budget_exhausted"}}
Messages, the Claude Proxy, and the Pi Proxy Anthropic endpoint{"type":"error","error":{"type":"budget_exhausted","message":"…"}}
The Claude Proxy Bedrock endpoints{"message":"…"}
The Claude Proxy Vertex endpoints{"error":{"code":429,"message":"…","status":"RESOURCE_EXHAUSTED"}}

Fallback and weighted routers

Budgets that cover a fallback or weighted router are checked for each member: when they block a member's model, the router tries the next member, so a block on one model moves traffic to the others. A block on the fallback or weighted router as a whole refuses the request.

Handling a block

A client that receives a budget block can:

ActionWhen
Wait the seconds in Retry-AfterFor a daily budget near its reset.
Show the message to the userFor interactive tools such as Claude Code, which display it.
Ask for more budgetFor people on a tools policy, with Request more budget in the Portal.

Token counting requests stay outside budgets.

Budgets & Spend Control

Budgets APIEnterprise

Creating, reading, changing and deleting budgets with the /v1/budgets endpoints.

The budgets endpoints manage an organization's budgets with an API key, for teams that keep budgets in code or read their status from monitoring. Amounts are integers in millionths of a US dollar, so 5000000000 is $5,000. The full list of endpoints, fields and errors is on Budgets in the API Reference.

Endpoints

The main endpoints and the scope each needs:

MethodPathDescriptionScope
POST/v1/budgetsCreate a budget. Accepts dry_run and an Idempotency-Key.budgets:write
GET/v1/budgetsList budgets, with limit up to 200 and cursor.budgets:read
GET/v1/budgets/{id}Read one budget with its current status.budgets:read
PATCH/v1/budgets/{id}Change the fields sent. Accepts dry_run.budgets:write
DELETE/v1/budgets/{id}Delete a budget. Accepts dry_run.budgets:delete
POST/v1/budgets/{id}/model-capsAdd a model cap.budgets:write
POST/v1/budgets/{id}/alertsAdd an alert.budgets:write
POST/v1/budgets/{id}/overridesRaise a cap for the current period.budgets:write
POST/v1/budgets/{id}/budget-requests/{request_id}/approveApprove a budget request.budgets:write

Creating a budget

A budget is created with its scope, period, caps and alerts in one request:

A Tag rules budget with alertsbash
curl -X POST https://api.multi-router.ai/v1/budgets \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production API",
    "scope": { "tags": [{ "tag": "production", "cap_micros": 2000000000 }] },
    "period": { "kind": "monthly" },
    "total_cap_micros": 5000000000,
    "model_caps": [{ "model": "anthropic/claude-opus-5", "cap_micros": 1000000000 }],
    "alerts": [
      { "threshold_percent": 80, "watches": { "kind": "total" },
        "actions": [{ "kind": "send_email", "recipient": "lead@example.com" }] },
      { "threshold_percent": 100, "watches": { "kind": "per_router" },
        "actions": [{ "kind": "block" }] }
    ]
  }'

The body has these fields:

FieldDescription
nameRequired.
scopeRequired. Exactly one of {"organization":true}, {"routers":[{"router_id","cap_micros"}]} or {"tags":[{"tag","cap_micros"}]}, where a tag's cap applies to each router with the tag.
periodRequired. {"kind":"daily"}, weekly, monthly or lifetime.
total_cap_microsOptional total cap.
model_capsOptional list of a model slug and cap.
alertsOptional alerts: threshold_percent above 0 up to 200, watches, optional projected_within_minutes, and actions.
timezoneOptional. Defaults to UTC.
enabledOptional. Defaults to true.

watches is {"kind":"total"}, {"kind":"per_router"}, {"kind":"per_model"}, or {"kind":"model","model":"…"}. The recipient of an email is the address of a person active in the organization. Actions are {"kind":"block"}, {"kind":"send_email","recipient":"…"} and {"kind":"send_webhook","endpoint_id":"…"}. The response is 201 with organization_id and the budget in data, a Location header and the budget's ETag. After creation, model caps and alerts change through their own endpoints, conditional on the budget's ETag.

Reading status

GET /v1/budgets/{id} includes a status object for the current period, with spend_micros, cap_micros, remaining_micros, burn_rate_micros_per_min, projected_cap_at, label, what is restricted, and unpriced_share. label is ok, nearing, imminent or capped. A new budget's status is null for its first few seconds, until Multi-Router first computes it.

Errors

The budgets endpoints return these errors:

ErrorStatusCause
invalid_budget400A field is missing or invalid. The message names it.
invalid_scope400The scope does not hold exactly one of organization, routers or tags.
invalid_threshold400An alert threshold is out of range or has more than two decimal places.
insufficient_scope403The API key lacks the scope.
budget_not_found404No budget with that id exists in the organization.
budget_policy_managed409The budget belongs to a tools policy.
budgets_off404Budgets are off for the installation.
use_sub_collection400A PATCH sends model_caps, alerts or overrides, which change through their own endpoints. The valid field of the error lists the endpoint path template, such as /v1/budgets/{id}/alerts; {id} is not replaced with the object id.
catalog_unavailable503The model catalog could not be read to check model caps. Retry.
version_conflict412The If-Match ETag is stale. The response carries the current budget.

Self-Hosting

OverviewEnterprise

Running Multi-Router in your own infrastructure, under an enterprise agreement.

Self-hosting runs the whole of Multi-Router, the API, the dashboard and the Portal, inside a company's own cloud account or data center. Requests, prompts, request records and provider keys stay in infrastructure the company controls, and Multi-Router reaches model providers from there. Self-hosting is available under an enterprise agreement, which provides the Multi-Router server and support for installing and running it.

Architecture

A self-hosted installation is made of these parts:

PartRequiredRole
Multi-Router serverYesOne program that serves the API, the dashboard and the Portal on port 8080. It keeps its data in the database, so several copies can run behind a load balancer. See Operations.
DatabaseYesPostgreSQL, or Amazon DynamoDB, holding accounts, routers, keys, prompts, policies and settings.
Secret storeYesAWS Secrets Manager, or a file, holding the database connection and the encryption keys.
Load balancer or reverse proxyYesTerminates TLS and forwards requests to the server.
ClickHouseFor TrafficStores request records for Traffic, request stats and budget reporting.
Payload storageFor payloadsAmazon S3 or a local directory, for request and response payloads.
SMTP serverFor emailSends invitations, Portal links and budget alerts.

What stays the same

A self-hosted installation runs the same server as the hosted service, with the same API, dashboard, Portal, routers, prompts, budgets, single sign-on, user provisioning and machine identity. The rest of this documentation applies, with the addresses replaced by the installation's own.

The other pages in Self-Hosting cover each step:

PageCovers
RequirementsInfrastructure, addresses and network access.
InstallationStarting the server and the installation wizard.
ConfigurationThe Admin pages: addresses, sign-in, email, Traffic, budgets and more.
OperationsUpgrades, scaling, backups, logs and the model catalog.

Self-Hosting

RequirementsEnterprise

The infrastructure, addresses and network access a self-hosted installation needs.

A self-hosted installation needs somewhere to run the server, a database, a secret store, a load balancer with TLS, and three addresses for the API, the dashboard and the Portal. This page lists each requirement, so they can be prepared before installation.

The server

The Multi-Router server is a single program for Linux, provided under the enterprise agreement, that listens on port 8080. It runs anywhere a container runs, such as Kubernetes, Amazon ECS or a virtual machine with a process manager. It answers health checks at /health, which returns 200 while the process runs, and /health/ready, which returns 200 once the installation is complete and ready to serve.

Database

The database holds everything except request records and payloads. Two databases are supported:

DatabaseNotes
PostgreSQLThe database is created ahead of installation, named for the installation's environment and stack joined by an underscore, such as prod_main. The wizard asks for the server address and adds this name. The server uses up to 20 connections per copy.
Amazon DynamoDBTables are created with the prefix <env>-<stack>- in the chosen region.

The server creates and upgrades the tables it needs when it starts.

Secret store

The secret store holds the values the server needs before it can reach the database: the database connection and the encryption keys. Two secret stores are supported:

Secret storeSuited to
AWS Secrets ManagerInstallations in AWS, and any installation with several copies of the server.
A fileA single copy of the server, with the file on a protected volume.

Addresses and TLS

A self-hosted installation serves three surfaces, each at its own https address, such as these, with DNS records pointing at the load balancer:

SurfaceExample addressServes
Dashboardhttps://dashboard.ai.example.comThe dashboard, sign-in and Admin pages.
APIhttps://api.ai.example.comThe /v1 API, the Claude Proxy and Pi Proxy endpoints including /bedrock, /vertex and /backend-api, and the SCIM endpoint.
Portalhttps://portal.ai.example.comThe Portal, for self-service provisioning.

The load balancer terminates TLS, forwards requests to port 8080, and appends the caller's address to X-Forwarded-For, which the server uses for rate limits. The dashboard and Portal addresses share a parent domain, which the session cookie is scoped to; the API address uses API keys.

Network access

The server reaches these services, depending on the features in use:

DestinationUsed for
Each model provider's APIServing requests.
dns.google over httpsVerifying the email domain of an SSO connection.
The identity provider's discovery and key addressesSingle sign-on and machine identity.
github.comGitHub sign-in, and connecting GitHub accounts for prompts.
api.github.comReading GitHub prompts.
Google sign-inSigning in with Google.
models.multi-router.ai, or the catalog address set on the Catalog pageModel catalog updates, when turned on.
The databasePostgreSQL, or the DynamoDB endpoint for the region.
AWS Secrets ManagerThe secret store, when used.
The SMTP serverEmail, when configured.
ClickHouseTraffic, when configured.
Amazon S3Payloads, when configured.
Webhook endpointsBudget alerts, over https.
Custom model endpointsCustom models, over https.

The identity provider reaches the API address for user provisioning, and GitHub reaches the dashboard address for GitHub prompts. Identity providers for single sign-on and machine identity are reached at public https addresses.

Self-Hosting

InstallationEnterprise

Starting the server for the first time and completing the installation wizard.

Installation starts the server with a few options and then completes a short wizard in the browser, which connects the database, creates the encryption keys, sets the addresses and creates the first administrator. The wizard runs once; afterwards the installation is managed from the Admin pages.

Starting the server

The server takes these options:

Start the serverbash
mr-server --env=prod --stack=main --secret-backend=aws-sm --region=us-west-2
OptionDescription
--envThe environment name, such as prod. Lowercase letters and digits, up to 16 characters.
--stackThe stack name, such as main. Lowercase letters and digits, up to 16 characters.
--secret-backendaws-sm for AWS Secrets Manager, or file:/path/to/secrets.json for a file.
--regionThe AWS region, with AWS Secrets Manager.

The environment and stack names identify the installation: secrets are named prod-main/database and so on, and the PostgreSQL database is prod_main.

The installation wizard

Until installation is complete, the server sends every request to the wizard at /install. The wizard is open to whoever reaches it first, so the server stays on a private network until the wizard is finished, with one copy running. A load balancer checks /health during installation. After a Welcome screen, the wizard has these steps:

  1. Secrets store: the wizard checks that the server can reach and read the secret store.
  2. Database: choose PostgreSQL or DynamoDB, enter the connection, and test it. The wizard saves it to the secret store.
  3. Encryption: the wizard creates the encryption keys and the session signing secret, and saves them to the secret store.
  4. Admin: enter the dashboard, API and Portal addresses, the shared cookie domain, and the first server administrator's email and a password of 8 or more characters. More administrators can be added here.
  5. Done: restart the server, and every copy of it, which then serve with the new settings.

After the restart, the dashboard address shows the sign-in page, and /health/ready returns 200. The first administrator signs in and continues in the Admin pages. See Configuration.

Encryption keys

The encryption keys protect the secrets Multi-Router stores in its database, such as API keys, provider keys, single sign-on and email credentials, and the session signing secret signs sign-in sessions. They live in the secret store, which is their only copy. The stored secrets are readable with these keys alone, so the secret store is backed up with the same care as the database, and kept apart from it.

Self-Hosting

ConfigurationEnterprise

The Admin pages that configure a self-hosted installation.

A self-hosted installation is configured from the Admin pages, opened from Admin in the profile menu by server administrators. Settings are stored in the database, with their secrets encrypted, so every copy of the server shares them. A strip across the top of every Admin page shows the mode, environment, stack, build, database and catalog, with the installation's addresses.

Admin pages

Each Admin page covers one part of the installation:

PageSets
OverviewA read-only summary of the installation.
UsersUsers: activating, deactivating, deleting, and Promote to server admin.
DatabaseA view of the database tables.
VaultEach secret the server reads at startup, with its status. Values stay hidden.
TrafficThe ClickHouse connection, the Storage Policy, and payload storage.
WebhooksWebhook delivery for the whole installation, with its switch.
BudgetsBudgets on or off, with reconciliation reports.
EmailThe SMTP server, branding and templates, with a test send.
FeaturesGitHub integration for prompts, with the GitHub App credentials.
CatalogCatalog fetching, its source address, and approving new catalog versions.
AuthenticationGoogle and GitHub sign-in credentials, Enterprise SSO, and the session cookie domain.

Traffic

The Traffic page connects ClickHouse, which stores request records. It accepts ClickHouse Cloud or a self-hosted ClickHouse over HTTPS or the native protocol, with or without TLS. The same page sets the Storage Policy, which caps what every router records, and payload storage in Amazon S3 or a local directory. Saving the connection creates the ClickHouse database, named like the PostgreSQL one. See Request Logging.

Email

The Email page sets the SMTP server, with its SMTP host, Port, Encryption (STARTTLS or TLS), Username, Password, From address, From name and Reply-to, and the branding of Multi-Router's emails, including the company name and address for the footer. Email delivery is turned on with its switch once the SMTP settings are saved, and a test send checks them. Invitations, Portal links and budget alerts then go out through it.

Feature switches

Feature switches turn parts of Multi-Router on for the installation, each on its own Admin page:

SwitchPageDefaultTurns on
Enterprise SSOAuthenticationOffSingle sign-on and user provisioning.
GitHub integration for promptsFeaturesOnGitHub prompts, with the GitHub App credentials.
Webhook deliveryWebhooksOnBudget alerts to webhook endpoints.
Catalog fetchingCatalogOffModel catalog updates.
BudgetsBudgetsOnMetering, budgets and budget rules. Read when the server starts, so a change applies after a restart, shown as restart pending.

Authentication

The Authentication page sets how people sign in: Google and GitHub sign-in with their client credentials, Enterprise SSO, and the session cookie domain shared by the dashboard, API and Portal addresses. Changing the cookie domain signs everyone out, and asks to type INVALIDATE ALL SESSIONS to confirm. See Single Sign-On.

Addresses

The dashboard, API and Portal addresses set in the wizard decide which surface each request reaches: the API address serves the API, the Portal address serves the Portal, and other addresses serve the dashboard. The address row in the strip at the top of the Admin pages changes the Deployment base URL, Portal URL and API URL, and every copy of the server picks up a change within 30 seconds.

Self-Hosting

OperationsEnterprise

Upgrading, scaling, backing up and monitoring a self-hosted installation.

Operating a self-hosted installation means keeping the server current, running enough copies for the traffic, backing up what the installation cannot rebuild, and watching its logs. This page covers each task.

Upgrading

An upgrade replaces the server with the new version and restarts it. The server applies database changes when it starts, one copy at a time on PostgreSQL, and on SIGTERM finishes requests in progress for up to 25 seconds before stopping. With several copies behind a load balancer, replacing them one at a time keeps the installation serving.

Scaling

The server keeps its data in the database, so adding copies behind the load balancer adds capacity. Rate limits, such as 300 inference requests per minute per API key, are counted within each copy. Installations with several copies use AWS Secrets Manager for secrets, and S3 or a shared directory for payloads.

Backups

A complete backup covers these parts:

PartHolds
The databaseAccounts, people, routers, keys, prompts, policies, budgets and settings.
The secret storeThe database connection, the encryption keys and the session signing secret.
Payload storageStored request and response payloads.
ClickHouseRequest records for Traffic.

Restoring the database also needs the secret store from the same time, so the two are backed up together.

Logs

The server writes its logs to standard output, with each request's id, the same id returned in the x-mr-request-id header. The level is set with the RUST_LOG environment variable, at info by default.

The model catalog

The model catalog, with its models, offerings and prices, ships inside the server and updates with each upgrade. With Catalog fetching turned on, the server also fetches new catalog versions from models.multi-router.ai, or the address set on the Catalog page, applied automatically or after approval on the Catalog page. Models the catalog lacks can be added as custom models. See Model Catalog and Custom Models & Pricing.

API Reference

Overview

Conventions shared by every /v1 endpoint: authentication, errors, headers, conditional writes, idempotency, pagination and ids.

The Multi-Router API is a REST API at https://api.multi-router.ai/v1 that sends requests to models and manages everything an organization configures: routers, provider keys, model groups, prompts, budgets, people, single sign-on and machine identity. Requests and responses are JSON, and every request authenticates with an API key. The pages in this section list each endpoint with its scope, request, response and errors; the conventions on this page apply to all of them. The full contract is published as OpenAPI 3.1 at GET /v1/openapi.yaml, which needs no API key.

Resources

The API groups its endpoints by resource:

ResourceEndpointsScope
Chat CompletionsPOST /v1/chat/completionsinference
MessagesPOST /v1/messagesinference
Proxy EndpointsToken counting, Responses, OpenAI Codex, Bedrock and Vertexinference
ModelsGET /v1/modelsmodels:read
Catalog/v1/catalogmodels:read
Routers/v1/routersrouters read, write and delete
Provider Keys/v1/provider-keysprovider_keys read, write and delete
Model Groups/v1/model-groupsmodel_groups read, write and delete
Prompts/v1/promptsprompts read, write and delete
Prompt repos/v1/prompt-reposprompt_repos read, write and delete
GitHub connections/v1/github/connectionsgithub read and delete
Budgets/v1/budgetsbudgets read, write and delete
Traffic/v1/traffictraffic:read
TagsGET /v1/tagstags:read
API Tokens/v1/api-tokensapi_tokens:read
Agents/v1/agentsagents read and delete
Organizations/v1/organizationsorganizations read, write and delete
Invites/v1/people/invitesinvites read, write and delete
SSO/v1/ssosso read, write and delete
Machine Identity/v1/machine-identitymachine_identity read, write and delete

Authentication

Each request sends an API key in the Authorization header as a bearer token. Keys are created on the API Keys page, and their scopes decide which endpoints they reach. A missing or invalid key returns 401, and a key without the endpoint's scope returns 403 insufficient_scope, with a message that names the scope. Management scopes are named for their resource and level, such as routers:write: GET needs read, POST and PATCH need write, DELETE needs delete, and each level includes the ones below it. A few GET endpoints need write, as their pages note. See Authentication and Scope.

An API key belongs to one organization, and acts on that organization. Endpoints that manage the whole account, such as creating organizations, inviting people, configuring SSO or registering machine identity issuers, need a key of the account organization, and return 403 to a key of a named organization. A key limited to some routers cannot manage organizations, invites or agents, and returns 403 router_scoped_token.

An authenticated requestbash
curl https://api.multi-router.ai/v1/routers \
  -H "Authorization: Bearer $MR_API_KEY"

Errors

Management endpoints return errors with a type, a stable code and a message, and, when they apply, the field at fault, the valid values, and details:

A management errorjson
{
  "error": {
    "type": "invalid_request",
    "code": "unknown_field",
    "field": "settings",
    "message": "Unknown router field: settings"
  }
}

Inference endpoints use the OpenAI error format, except Messages, token counting and a Claude Proxy's model list, which use the Anthropic format, and the Claude Proxy's Bedrock and Vertex endpoints, which use those platforms' formats:

OpenAI formatjson
{
  "error": {
    "message": "no router `rt_unknown` in this organization",
    "type": "invalid_request_error",
    "code": "router_not_found"
  }
}
Anthropic formatjson
{
  "type": "error",
  "error": { "type": "not_found_error", "message": "the requested model or router was not found" }
}

The inference error types are:

TypeMeaning
invalid_request_errorThe request is invalid, or names something absent. Fix the request.
authentication_errorA machine identity token failed authentication.
rate_limit_errorA rate limit was reached. Retry later.
budget_exhaustedA budget blocks the request. Retry-After gives the seconds until it resets.
upstream_errorThe provider failed or returned an error. The status is the provider's, and error.upstream can name the provider, its code and status.
server_errorMulti-Router failed. Retry.

Codes shared by many endpoints:

CodeStatusMeaning
invalid_json400The body is not valid JSON.
invalid_body400The body does not match the endpoint's schema.
unknown_field400The body sends a field the endpoint does not accept. On routers it is also returned for every read-only field except etag, on provider keys for read-only fields such as id and provider, and on prompts for current_version and variables. The field names it.
use_sub_collection400A PATCH body sends a list that has its own endpoints: a budget's model_caps, alerts or overrides; a model group's members or tags; a prompt's tags; a prompt repo's files or tags; or an issuer's organization_ids. Change them through those endpoints. On routers, members, tags and aliases return unknown_field. For budgets, model groups, prompts and prompt repos, the valid field of the error lists the endpoint path template, such as /v1/budgets/{id}/alerts; {id} is not replaced with the object id.
field_read_only400A PATCH body sends a read-only field. Budgets, model groups, prompt repos, invites, SSO, organizations and machine identity return it for every read-only field other than the lists that return use_sub_collection; prompts for every read-only field except current_version and variables (unknown_field) and tags (use_sub_collection); routers only for etag; provider keys for etag, custom and bedrock.access_key_id, bedrock.auth_mode and bedrock.session_token.
invalid_field400A field has an invalid value. The field names it.
invalid_if_match400The If-Match header is not one ETag.
invalid_dry_run400dry_run is not true or false.
invalid_cursor400The cursor is not one this list returned.
insufficient_scope403The API key lacks the scope. The message names the scope.
route_not_found404No endpoint has this method and path.
version_conflict412The If-Match ETag is stale. The response carries the current object in current.
rate_limit_exceeded429Over 300 inference requests per minute for the key, counted across every inference endpoint.
budget_exhausted429A budget blocks the request.

Headers

Multi-Router reads these request headers:

HeaderUse
AuthorizationThe API key, as Bearer sk-mr-….
If-MatchThe ETag a write expects the object to have. A stale ETag returns 412.
Idempotency-KeyMakes a create safe to retry.
x-mr-routerSelects a router, or a Claude Proxy or Pi Proxy, by id or alias.
x-mr-session-idGroups requests of one session in Traffic. Up to 200 characters.
x-mr-correlation-idMatches a request to the caller's own logs, and is returned on the response.

Every response carries x-mr-request-id, and successful inference responses carry the x-mr-* headers that describe how they were served. Reads and writes of a changeable object return its ETag, and creates return a Location header. See Request Tracing.

Changing objects

Objects that can change carry an etag, a quoted counter such as "3", equal to the response's ETag header. Sending it back in If-Match makes a write apply only if nobody changed the object in between; a stale ETag returns 412 version_conflict with the current object, so the caller can read it again and decide. Writes to an object's sub-collections, such as a router's members or tags, are conditional on the parent's ETag.

PATCH changes only the fields sent. A field left out keeps its value, a field sent as null is cleared, nested objects merge field by field, and fields marked as replaced whole, such as a budget's scope, take the value sent. Unknown fields are refused with unknown_field. Read-only fields are refused with field_read_only on invites, SSO and organizations, and on budgets, model groups, prompt repos and machine identity other than the lists below. On prompts, read-only fields return field_read_only except current_version and variables, which return unknown_field. On routers, only etag returns field_read_only, and other read-only fields such as id and kind return unknown_field. On provider keys, etag, custom, bedrock.access_key_id, bedrock.auth_mode and bedrock.session_token return field_read_only, and other read-only fields such as id and provider return unknown_field. Lists change through their own endpoints, not the parent's PATCH. Sending one in a PATCH returns use_sub_collection: a budget's model_caps, alerts and overrides, a model group's members and tags, a prompt's tags, a prompt repo's files and tags, and an issuer's organization_ids. For budgets, model groups, prompts and prompt repos, the error's valid field lists the endpoint path template, such as /v1/budgets/{id}/alerts; {id} is not replaced with the object id. On routers, members, tags and aliases return unknown_field.

Endpoints that accept dry_run=true validate the request and return the result without saving anything. Each page notes which endpoints accept it.

A conditional changebash
curl -X PATCH https://api.multi-router.ai/v1/routers/rt_9Hk3Ws7PzQ \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H 'If-Match: "3"' \
  -H "Content-Type: application/json" \
  -d '{ "name": "Support bot v2" }'

Idempotency

An Idempotency-Key makes a create request safe to retry: after a successful response, a retry with the same key and the same request returns that response again, including its Location header. A failed request releases the key, so its retry runs again. Keys are kept per organization and do not expire, and a key that completed a request stays tied to it. Keys hold up to 255 visible ASCII characters, and each create uses a new one, such as a UUID. Creates accept a key, and a provider key transfer requires one.

CaseResponse
Same key, same requestThe first response again, with its status.
Same key, different request409 idempotency_key_reused.
Same key while the first is running409 idempotency_in_progress.
A key longer than 255 characters or with other characters400 invalid_idempotency_key.

Pagination

Lists return a page of results in data, with next_cursor and has_more. Pass limit, from 1 to 200 with a default of 50, and cursor set to the previous page's next_cursor, and continue until next_cursor is null. A filtered page can hold fewer results than the limit while more follow.

A list responsejson
{
  "organization_id": "org_7Tq2Wd9KxP",
  "data": [ { "id": "rt_9Hk3Ws7PzQ", "object": "router", "kind": "static", "name": "Support bot" } ],
  "next_cursor": "eyJpZCI6InJ0XzlIazNXczdQelEifQ",
  "has_more": true
}

Responses and ids

Management responses carry organization_id and the object in data. Each object names its type in object. Deletes return organization_id, the id, the object and "deleted": true. Times are RFC 3339. Amounts are integers in millionths of a US dollar. Model slugs, such as anthropic/claude-sonnet-4.5, are catalog model ids. Ids start with a prefix for their kind:

PrefixResource
rt_Router
llpk_Provider key
mg_Model group
bdg_Budget
bdr_Budget alert
ip_Inline prompt
pf_GitHub prompt
pr_Prompt repo
org_Organization
per_Person
inv_Invite
tok_API key
wl_Agent
mii_Machine identity issuer
mib_Machine identity binding
req_Request
sk-mr-API key value

API Reference

Chat Completions

POST /v1/chat/completions: OpenAI-compatible chat requests to any provider.

POST /v1/chat/completions sends a conversation to a model and returns its reply, in the OpenAI Chat Completions format, for every provider Multi-Router supports. It needs the inference scope, accepts bodies up to 4 MiB, and streams with server-sent events when stream is true.

The model field

The model field names a router or a model:

FormExampleServed by
A routermulti-router/rt_9Hk3Ws7PzQThe router, with its model, prompt and settings.
An aliasmulti-router/productionThe router the alias points to.
A provider and modelanthropic/claude-sonnet-4.5The organization's default provider key for the provider.
A model nameclaude-sonnet-4-5A router or alias with that id, or the provider the name belongs to.
A catalog offeringanthropic/claude-sonnet-4.5:anthropic:standardThat exact model, provider and variant.

A router named in the model field takes precedence over the x-mr-router header. See Routing and Supported Providers.

Request body

The body takes the OpenAI Chat Completions fields:

FieldType
modelstring, required
A router, alias, provider and model, or model name.
messagesarray, required
The conversation, as system, user, assistant and tool messages.
streamboolean
Streams the reply as server-sent events.
stream_options.include_usageboolean
Adds token usage to the last streamed chunk.
max_tokensinteger
The most tokens to generate. max_completion_tokens is also accepted.
temperaturenumber
Sampling temperature.
top_pnumber
Nucleus sampling.
stopstring or array
Stop sequences.
toolsarray
Function tools the model can call.
tool_choicestring or object
none, auto, required, or a named function.
parallel_tool_callsboolean
Allows several tool calls in one turn.
response_formatobject
text, json_object, or json_schema with a schema.
reasoning_effortstring
Reasoning effort for reasoning models.
seedinteger
Seed for repeatable sampling where the provider supports it.
userstring
The caller's end user, recorded in Traffic as the customer id.
prompt_variablesobject
Values for the router's bound prompt variables. See Prompt Variables.

Other OpenAI fields, such as presence_penalty, logprobs, metadata and service_tier, are passed to providers that support them. See Prompt Variables.

Example

A request to a router and its response, which echoes the request's model field:

Requestbash
curl https://api.multi-router.ai/v1/chat/completions \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "multi-router/rt_9Hk3Ws7PzQ",
    "messages": [{ "role": "user", "content": "Summarize ticket 4521 in one sentence." }],
    "max_tokens": 200
  }'
Response 200json
{
  "id": "chatcmpl-8Kd2Qx9WmB",
  "object": "chat.completion",
  "created": 1790000000,
  "model": "multi-router/rt_9Hk3Ws7PzQ",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "The customer was charged twice for March and wants a refund." },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 412,
    "completion_tokens": 18,
    "total_tokens": 430,
    "prompt_tokens_details": { "cached_tokens": 0 }
  }
}

Errors

Beyond the common errors, Chat Completions returns these:

CodeStatusMeaning
router_not_found400The router or alias is absent from the organization.
router_disabled400The router is disabled.
model_not_found400The model is absent from the catalog.
unknown_model400A model name matches no router, alias or model.
model_not_found404The request names a router outside the API key's routers.
unknown_provider400The provider prefix is unsupported.
no_provider_key_configured400The organization has no provider key for the provider.
provider_key_disabled400The provider key in use is disabled.
missing_prompt_variables400The request leaves out a variable of the router's prompt.
not_supported400A parameter the provider cannot serve, such as n above 1, or a tool type other than function.
empty_messages400The messages array is empty.

API Reference

Messages

POST /v1/messages: Anthropic-compatible requests, through routers or a Claude Proxy.

POST /v1/messages accepts requests in the Anthropic Messages format, so Anthropic SDKs and tools work with Multi-Router by changing their base URL to https://api.multi-router.ai. It needs the inference scope, accepts bodies up to 32 MB, accepts the key in x-api-key as well as Authorization, and returns errors in the Anthropic format.

Routing

A Messages request is served in one of two ways:

RequestServed by
A router or alias in the model fieldThat router, as on Chat Completions.
x-mr-router names a Claude ProxyThe Claude Proxy, which serves the request on its route with the route's model id and key.
x-mr-router names a Pi ProxyThe Pi Proxy, which forwards it to Anthropic with the developer's own login.
OtherwiseThe router named in x-mr-router, or the model in the model field.

Request body

The body takes the Anthropic Messages fields:

FieldType
modelstring, required
A router, alias, or model, as on Chat Completions.
messagesarray, required
The conversation, with text, image and tool content blocks.
max_tokensinteger, required
The most tokens to generate.
systemstring or array
The system prompt, as text or content blocks with cache control.
streamboolean
Streams the reply as server-sent events.
temperaturenumber
Sampling temperature.
top_pnumber
Nucleus sampling.
top_kinteger
Top-k sampling.
stop_sequencesarray
Stop sequences.
toolsarray
Tools with an input schema.
tool_choiceobject
auto, any, a named tool, or none.
thinkingobject
Extended thinking, with a token budget.
metadata.user_idstring
The caller's end user, recorded in Traffic as the customer id.
prompt_variablesobject
Values for the router's bound prompt variables.

Example

A request to a router and its response, which echoes the request's model field:

Requestbash
curl https://api.multi-router.ai/v1/messages \
  -H "x-api-key: $MR_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "multi-router/rt_9Hk3Ws7PzQ",
    "max_tokens": 200,
    "messages": [{ "role": "user", "content": "Summarize ticket 4521 in one sentence." }]
  }'
Response 200json
{
  "id": "msg_01Kd2Qx9WmB4Tn6Ls",
  "type": "message",
  "role": "assistant",
  "model": "multi-router/rt_9Hk3Ws7PzQ",
  "content": [{ "type": "text", "text": "The customer was charged twice for March and wants a refund." }],
  "stop_reason": "end_turn",
  "usage": { "input_tokens": 412, "output_tokens": 18 }
}

Errors

Errors use the Anthropic format, with a type that follows the status: 400 invalid_request_error, 401 authentication_error, 403 permission_error, 404 not_found_error, 413 request_too_large, 429 rate_limit_error, 503 overloaded_error, and api_error for other statuses. The message carries the detail.

API Reference

Proxy Endpoints

The endpoints that Claude Proxies and Pi Proxies serve to coding agents.

Proxy endpoints serve Claude Code, Claude Desktop and Pi.Dev through a Claude Proxy or Pi Proxy, in the format each tool sends. Each needs the inference scope and an x-mr-router header naming the proxy. A Claude Proxy serves each request on its route with the route's model id and key; a Pi Proxy forwards it to the provider as sent. See Claude Proxy, Pi Proxy and Agents & Editors.

Claude Proxy

A Claude Proxy serves these endpoints:

EndpointFormatUse
POST /v1/messagesAnthropicRequests on the Anthropic route.
POST /v1/messages/count_tokensAnthropicToken counting at no cost, with its own rate limit of 300 per minute. Served only through a Claude Proxy.
POST /bedrock/model/{model}/invokeAmazon BedrockRequests on Bedrock routes.
POST /bedrock/model/{model}/invoke-with-response-streamAmazon BedrockStreaming requests on Bedrock routes.
POST /vertex/projects/{project}/locations/{location}/publishers/anthropic/models/{model}:rawPredictGoogle VertexRequests on Vertex routes.
POST /vertex/projects/{project}/locations/{location}/publishers/anthropic/models/{model}:streamRawPredictGoogle VertexStreaming requests on Vertex routes.
POST /vertex/projects/{project}/locations/{location}/publishers/anthropic/models/count-tokens:rawPredictGoogle VertexToken counting on Vertex routes.
GET /v1/modelsAnthropicThe models the proxy serves, with models:read.

On the Vertex endpoints, the project and location in the path come from the tool's settings, and on every endpoint the Claude Proxy serves the request from its own route, region and provider key.

Pi Proxy

A Pi Proxy serves these endpoints, with the developer's own provider login:

EndpointFormatForwards to
POST /v1/messagesAnthropicAnthropic
POST /v1/chat/completionsOpenAI Chat CompletionsOpenAI
POST /v1/responsesOpenAI ResponsesOpenAI
POST /backend-api/codex/responsesOpenAI CodexOpenAI Codex, streaming only

The Multi-Router key goes in X-MR-Api-Token, since Authorization carries the provider login.

API Reference

Models

GET /v1/models: the models available to the organization.

GET /v1/models lists the models the organization can use, in the OpenAI list format, so tools that read a model list work with Multi-Router. It needs the models:read scope. With an x-mr-router header naming a Claude Proxy, it lists that proxy's models in the Anthropic format instead.

The list

The list holds each active chat model in the catalog for the providers the organization has an enabled provider key for, and, with an enabled custom provider key, the installation's custom models. Each id is the model's catalog slug, which the model field accepts, and created is the catalog version's publish time:

Response 200json
{
  "object": "list",
  "data": [
    { "id": "anthropic/claude-opus-5", "object": "model", "created": 1790000000, "owned_by": "anthropic" },
    { "id": "anthropic/claude-sonnet-4.5", "object": "model", "created": 1790000000, "owned_by": "anthropic" },
    { "id": "openai/gpt-5.1", "object": "model", "created": 1790000000, "owned_by": "openai" }
  ]
}

A Claude Proxy's models

With x-mr-router naming a Claude Proxy, the response lists the models the proxy serves, in the Anthropic format: each entry has type, id as the provider's model id, display_name, created_at, max_input_tokens and max_tokens, with has_more, first_id and last_id. Claude Desktop and Claude Code use this list for their model pickers. An unknown router returns 404 the requested model or router was not found, and a disabled proxy 403 this Claude Proxy is disabled. A workload signed in with machine identity whose binding has several routers names one, or receives 400 router_required.

API Reference

Catalog

Reading the model catalog: models, their offerings, regions and prices, publishers and providers.

The catalog endpoints read the model catalog that routers and model groups name their models from. They need the models:read scope and have no writes. A model's offerings list each provider and variant that serves it, with the regions, wire ids and prices of each, which are the values a static router's routing.variant and routing.region take. See Model Catalog and Regions & Variants.

Endpoints

MethodPathDescriptionScope
GET/v1/catalog/modelsList models by slug. Filter with publisher, provider and region. Paginated.models:read
GET/v1/catalog/models/{publisher}/{model}Get one model with its limits, capabilities, modalities and offerings.models:read
GET/v1/catalog/publishersList publishers, such as anthropic.models:read
GET/v1/catalog/providersList providers, such as bedrock.models:read

The model path takes the slug's two parts as separate segments, so anthropic/claude-sonnet-4.5 is read at /v1/catalog/models/anthropic/claude-sonnet-4.5. Prices are US dollars per million tokens.

Models Bedrock serves in eu-west-1bash
curl "https://api.multi-router.ai/v1/catalog/models?provider=bedrock&region=eu-west-1&limit=20" \
  -H "Authorization: Bearer $MR_API_KEY"
GET /v1/catalog/models/anthropic/claude-haiku-4.5 (abridged)json
{
  "organization_id": "org_7Tq2Wd9KxP",
  "data": {
    "id": "anthropic/claude-haiku-4.5",
    "object": "catalog_model",
    "publisher": "anthropic",
    "name": "Claude Haiku 4.5",
    "offerings": [
      {
        "provider": "bedrock",
        "variant": "geo",
        "regions": [
          { "region": "eu-central-1", "wire_id": "eu.anthropic.claude-haiku-4-5-20251001-v1:0", "pricing": { "input": 1.1, "output": 5.5 } }
        ]
      }
    ]
  }
}

Errors

CodeStatusMeaning
invalid_query400A filter has an unknown value, or the query has an unknown parameter.
invalid_path400A path segment is invalid.
model_not_found404No catalog model has that publisher and name.

API Reference

Routers

Creating, reading, changing, enabling, disabling and deleting routers, and managing their members, aliases and tags.

The routers endpoints manage an organization's routers of every type, with their members, aliases and tags. Reading needs routers:read, changing needs routers:write, and deleting needs routers:delete. Writes take the router's canonical rt_ id. Each router type's behavior is described in Routing.

Endpoints

MethodPathDescriptionScope
GET/v1/routersList routers, newest change first. Filter with kind and tag.routers:read
POST/v1/routersCreate a router. Returns 201. Accepts dry_run and an Idempotency-Key.routers:write
GET/v1/routers/{id}Get one router, with its tags and aliases.routers:read
PATCH/v1/routers/{id}Change the fields sent. Enables and disables a router through enabled. Accepts dry_run.routers:write
DELETE/v1/routers/{id}Delete a disabled router. Accepts dry_run.routers:delete
GET/v1/routers/{id}/membersList the members of a fallback or weighted router, in order.routers:read
POST/v1/routers/{id}/membersAdd a member: router_id, weight for weighted routers, and an optional zero-based position. Returns 201.routers:write
PATCH/v1/routers/{id}/members/{key}Change a member's weight or position. The key is the member's router id.routers:write
DELETE/v1/routers/{id}/members/{key}Remove a member.routers:delete
GET/v1/routers/{id}/routesList a Claude Proxy's resolved routes: model, provider, variant, region and provider key.routers:read
POST/v1/routers/{id}/aliasesAttach an alias label, sent as alias. Returns 201, or 200 when the alias is already here or moves from another router.routers:write
DELETE/v1/routers/{id}/aliasesDetach the alias named in the alias query parameter.routers:delete
GET/v1/routers/{id}/tagsList the router's tags.routers:read
POST/v1/routers/{id}/tagsAdd a tag, sent as tag. Returns 201, or 200 when the router already has it.routers:write
DELETE/v1/routers/{id}/tags/{key}Remove a tag.routers:delete

Member and tag writes are conditional on the router's ETag and accept dry_run. Alias writes take no If-Match.

Kinds

The kind field sets what a router is, and is fixed once created:

KindRouter typeCreate with
staticStatic routerrouting.provider, routing.model, routing.provider_key_id, routing.region for every provider except custom, routing.variant for catalog providers, and prompt.system_message_mode.
fallbackFallback routerrouting.routers: 1 to 50 static router ids, in order.
weightedWeighted routerrouting.routers: 1 to 50 objects with router_id and a positive weight.
claude_proxyClaude ProxyOptionally routing: route, region, credential_source, provider_key_id, model_group, model_keys and disabled_models.
pi_proxyPi ProxyNothing beyond the kind.

Every kind takes optional name, description, tags and rate_limits (rpm and tpm). A create always makes an enabled router: enabled is accepted only as true, and false returns 400 invalid_field. Disable a router with PATCH. Static routers also take parameter_defaults, and static routers and proxies take observability. An alias is a label on a router, not a router: attach it with the aliases endpoint, and callers name it in the model field or in x-mr-router. See Alias.

Creating a static router

Requestbash
curl -X POST https://api.multi-router.ai/v1/routers \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "static",
    "name": "Support bot",
    "routing": {
      "provider": "anthropic",
      "model": "anthropic/claude-sonnet-4.5",
      "variant": "standard",
      "region": "global",
      "provider_key_id": "llpk_4Vd8Qm2RtL"
    },
    "prompt": { "system_message_mode": "merge" }
  }'
Response 201json
{
  "organization_id": "org_7Tq2Wd9KxP",
  "data": {
    "id": "rt_9Hk3Ws7PzQ",
    "object": "router",
    "etag": "\"1\"",
    "kind": "static",
    "name": "Support bot",
    "description": null,
    "enabled": true,
    "tags": [],
    "aliases": [],
    "rate_limits": { "rpm": null, "tpm": null },
    "routing": {
      "provider": "anthropic",
      "model": "anthropic/claude-sonnet-4.5",
      "variant": "standard",
      "region": "global",
      "provider_key_id": "llpk_4Vd8Qm2RtL",
      "model_source": "request"
    },
    "prompt": { "binding": null, "system_message_mode": "merge" },
    "parameter_defaults": {
      "temperature": null, "max_tokens": null, "top_p": null, "stop_sequences": [],
      "response_format": null, "tool_choice": null, "parallel_tool_calls": null,
      "reasoning_effort": null, "seed": null, "verbosity": null,
      "presence_penalty": null, "frequency_penalty": null
    },
    "observability": { "metadata_level": "inherit", "request_capture": "inherit", "payload_sample_pct": null },
    "created_at": "2026-10-01T17:02:11Z",
    "updated_at": "2026-10-01T17:02:11Z"
  }
}
Create a fallback routerbash
curl -X POST https://api.multi-router.ai/v1/routers \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "fallback", "name": "Support with backup", "routing": { "routers": ["rt_9Hk3Ws7PzQ", "rt_2Lw6Yd8NcV"] } }'

Router fields

FieldType
idstring
The router id. Fixed.
objectstring
Always router.
etagstring
The quoted version, for If-Match.
kindstring
static, fallback, weighted, claude_proxy or pi_proxy. Fixed.
namestring
A display name.
descriptionstring or null
A description.
enabledboolean
Whether the router serves requests.
tagsarray
Tag names. Changed through the tags endpoints.
aliasesarray
Alias labels on this router. Changed through the aliases endpoints.
rate_limitsobject
rpm and tpm, or null for no limit.
routingobject
Static routers: provider, model, variant, region, provider_key_id and model_source. Fallback and weighted routers: routers. Proxies: route, region, credential_source, provider_key_id, model_group, model_keys and disabled_models.
promptobject
Static routers: the bound prompt in binding, and system_message_mode. See Prompt Binding.
parameter_defaultsobject
Static routers: default request parameters.
observabilityobject
Static routers and proxies: metadata_level, request_capture and payload_sample_pct.
routesarray
Claude Proxies: the resolved routes, read-only.
created_atstring
When the router was created.
updated_atstring
When the router last changed.

See Static Router, Router Settings and Prompt Binding for every setting.

Changing a router

PATCH /v1/routers/{id} changes only the fields sent: name, description, enabled, rate_limits, routing, prompt, parameter_defaults and observability. Nested objects merge field by field. A change to a static router's routing includes routing.region, except on custom endpoints. Fallback and weighted members change through the members endpoints. A field that does not apply to the kind returns field_not_supported_for_kind.

Change a router's modelbash
curl -X PATCH https://api.multi-router.ai/v1/routers/rt_9Hk3Ws7PzQ \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H 'If-Match: "1"' \
  -H "Content-Type: application/json" \
  -d '{ "routing": { "model": "anthropic/claude-opus-5", "region": "global" } }'
Disable a routerbash
curl -X PATCH https://api.multi-router.ai/v1/routers/rt_9Hk3Ws7PzQ \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": false }'
Attach an aliasbash
curl -X POST https://api.multi-router.ai/v1/routers/rt_9Hk3Ws7PzQ/aliases \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "alias": "production" }'

Attaching an alias that is on another router moves it in one step, and the response names the previous router in moved_from. A router is deleted only once it is disabled and has no aliases.

Errors

The routers endpoints return these errors beyond the common ones:

CodeStatusMeaning
router_not_found404The id matches no router.
missing_field400A required field is missing, such as routing.region on a static router.
missing_variant400A static router on a catalog provider names no variant.
field_not_supported_for_kind400A field does not apply to the router kind, such as prompt on a fallback router.
unknown_provider400The provider is unsupported.
model_not_found400The model is absent from the catalog.
invalid_variant400The model has no offering with that variant.
region_not_offered400The offering has no such region.
provider_key_not_found400The provider key is absent from the organization.
provider_key_disabled400The provider key is disabled.
provider_key_mismatch400The provider key belongs to another provider.
invalid_member400A member is absent, or is not a static router.
too_many_members400More than 50 members.
member_exists400The router is already a member.
member_not_found404The key names no member.
tag_not_found404The router has no such tag.
router_enabled409A delete names an enabled router. Disable it first.
has_dependent_aliases409A delete names a router with aliases. Detach them first.
policy_managed409The router belongs to a tools policy, and changes through the policy.
field_not_supported_for_kind409The routes endpoint names a router that is not a Claude Proxy, or a members endpoint names a router that is not a fallback or weighted router.
model_group_changed409The Claude Proxy's model group changed at the same time. Read it again and retry.
alias_changed409The alias changed at the same time. Retry.
prompt_validation_rate_limited429Too many prompt binding checks. Retry later.

API Reference

Provider Keys

Adding, reading, changing, rotating, transferring and deleting provider keys with /v1/provider-keys.

The provider keys endpoints manage the credentials an organization uses to call model providers. Reading needs provider_keys:read, adding, changing, rotating and transferring need provider_keys:write, and deleting needs provider_keys:delete. Reads never return a secret. See Provider Key.

Endpoints

MethodPathDescriptionScope
GET/v1/provider-keysList provider keys, without secrets. Paginated.provider_keys:read
POST/v1/provider-keysAdd a key. Returns 201 with the secret once. Accepts dry_run and an Idempotency-Key.provider_keys:write
GET/v1/provider-keys/{id}Get one key.provider_keys:read
PATCH/v1/provider-keys/{id}Change name, description, enabled, is_default and provider settings. Accepts dry_run.provider_keys:write
DELETE/v1/provider-keys/{id}Delete a disabled key that nothing uses.provider_keys:delete
POST/v1/provider-keys/{id}/rotateReplace the secret, keeping the key id. Returns 200 with the secret once.provider_keys:write
POST/v1/provider-keys/{id}/transferCopy or move the key to another organization in the account. Requires an Idempotency-Key.provider_keys:write; a move also provider_keys:delete

Key fields

FieldType
idstring
The key id, prefixed llpk_, used in provider_key_id.
namestring, required on create
1 to 80 characters.
descriptionstring or null
Up to 2,000 characters.
providerstring, required on create
The provider id. Fixed.
secretstring, required on create and rotate
The credential. Never returned by reads.
enabledboolean
Defaults to true.
is_defaultboolean
Whether this is the provider's default key. Setting true moves the default from the previous key.
bedrockobject
AWS Bedrock: region, auth_mode, access_key_id, and on create session_token. PATCH changes only region.
vertexobject
Google Vertex: project_id and region.
openaiobject
OpenAI: data_residency, and on create organization. PATCH changes only data_residency.
customobject
Custom endpoints: base_url and api_format. Fixed after create.
key_prefixstring
The start of the secret, to recognize it.
etagstring
The quoted version, for If-Match.
Add a keybash
curl -X POST https://api.multi-router.ai/v1/provider-keys \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  --data @key-create.json
key-create.jsonjson
{ "name": "Anthropic production", "provider": "anthropic", "secret": "sk-ant-api03-…" }
Response 201json
{
  "organization_id": "org_7Tq2Wd9KxP",
  "data": {
    "id": "llpk_4Vd8Qm2RtL",
    "object": "provider_key",
    "name": "Anthropic production",
    "description": null,
    "provider": "anthropic",
    "key_prefix": "sk-ant-api",
    "enabled": true,
    "is_default": true,
    "etag": "\"1\"",
    "created_at": "2026-10-01T17:02:11Z",
    "updated_at": "2026-10-01T17:02:11Z"
  },
  "secret": "sk-ant-api03-…"
}

The secret is in the response to the first successful create or rotate only; a dry run or an idempotent replay returns the key without it.

Transferring a key

POST /v1/provider-keys/{id}/transfer takes the destination organization_id, mode (copy or move), and an optional new name and description. Copy creates a new key in the destination. Move creates the destination key and deletes the source in one step, and refuses a default key or one that routers or model groups use. A transfer needs a key of the account organization whose creator is an admin of both organizations, requires an Idempotency-Key, has no dry run, and never accepts or returns a secret.

Copy a key to another organizationbash
curl -X POST https://api.multi-router.ai/v1/provider-keys/llpk_4Vd8Qm2RtL/transfer \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "organization_id": "org_3Hd8Kw2PqZ", "mode": "copy" }'

Errors

CodeStatusMeaning
provider_key_not_found404The id matches no key in the organization.
field_read_only400A PATCH sends etag, custom, or bedrock.access_key_id, auth_mode or session_token. Rotate or add a key to change credentials.
default_change_required400is_default false on the current default. Make another key default first.
dry_run_unsupported400A rotate or transfer sent dry_run.
missing_idempotency_key400A transfer has no Idempotency-Key.
invalid_organization_id400The transfer destination is the current organization.
account_token_required403A transfer uses a key of a named organization.
transfer_forbidden403The API key's creator is not an admin of both organizations, or the destination is outside the account.
provider_key_disabled409A disabled key cannot be made default.
provider_key_enabled409A delete names an enabled key. Disable it first.
provider_key_in_use409Routers, model groups or a policy use the key. details lists up to 20 routers and model groups.
transfer_conflict409The source changed, became the default, or the transfer conflicted.
concurrent_update409The key changed at the same time. Read it again and retry.

API Reference

Model Groups

Creating, reading, changing and deleting model groups, their members and tags, and finding the routers that use them.

The model groups endpoints manage an organization's model groups, the approved sets of models that Claude Proxies and tools policies use. They need the model_groups:read, model_groups:write or model_groups:delete scope. See Model Group.

Endpoints

MethodPathDescriptionScope
GET/v1/model-groupsList model groups. Paginated.model_groups:read
POST/v1/model-groupsCreate a model group. Returns 201. Accepts an Idempotency-Key.model_groups:write
GET/v1/model-groups/{id}Get one model group.model_groups:read
PATCH/v1/model-groups/{id}Change name and description.model_groups:write
DELETE/v1/model-groups/{id}Delete a group no router or policy uses. Accepts dry_run.model_groups:delete
GET/v1/model-groups/{id}/routersList the routers that use the group.model_groups:read
GET/v1/model-groups/{id}/membersList the members.model_groups:read
POST/v1/model-groups/{id}/membersAdd a member. Returns 201.model_groups:write
PATCH/v1/model-groups/{id}/membersChange the member named by the model query parameter.model_groups:write
DELETE/v1/model-groups/{id}/membersRemove the member named by the model query parameter.model_groups:delete
GET/v1/model-groups/{id}/tagsList the group's tags.model_groups:read
POST/v1/model-groups/{id}/tagsAdd a tag, sent as tag.model_groups:write
DELETE/v1/model-groups/{id}/tags/{key}Remove a tag.model_groups:delete

Member and tag writes are conditional on the group's ETag.

Body

A create has these fields:

FieldType
namestring, required
1 to 80 characters.
descriptionstring or null
Up to 2,000 characters.
membersarray
Up to 100 members, each with provider, model, and optionally variant, region and provider_key_id.
tagsarray
Tag names.

A member PATCH takes provider, variant, region and provider_key_id, with null clearing a field; the member is selected by its full model slug in ?model=, never in the body.

Create a model groupbash
curl -X POST https://api.multi-router.ai/v1/model-groups \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Engineering models",
    "members": [
      { "provider": "anthropic", "model": "anthropic/claude-opus-5" },
      { "provider": "anthropic", "model": "anthropic/claude-sonnet-4.5" }
    ]
  }'
Change one memberbash
curl -X PATCH "https://api.multi-router.ai/v1/model-groups/mg_4kT9wQx2Lp/members?model=anthropic/claude-sonnet-4.5" \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H 'If-Match: "2"' \
  -H "Content-Type: application/json" \
  -d '{ "provider_key_id": "llpk_4Vd8Qm2RtL" }'

Errors

CodeStatusMeaning
invalid_model_group400The body is malformed or a field is invalid.
model_group_not_found404The id matches no model group.
model_not_found404The group has no member with that model.
tag_not_found404The group has no such tag.
model_group_member_exists409The model is already a member.
use_sub_collection400A PATCH sends members or tags, which change through their own endpoints. The valid field of the error lists the endpoint path template, such as /v1/model-groups/{id}/members; {id} is not replaced with the object id.
model_group_referenced409A delete names a group that routers use.
model_group_policy_managed409The group belongs to a tools policy.
model_group_incompatible_with_claude_proxy409The change would leave a Claude Proxy using the group with models it cannot serve.

API Reference

Prompts

The prompts, prompt repos and GitHub connections endpoints.

The prompt endpoints manage inline prompts, GitHub prompts, the prompt repos they come from, and GitHub connections. Each group has its own scopes. Inline prompts, prefixed ip_, are changed through the API; GitHub prompts, prefixed pf_, are read-only and change in their repository. See Managing Prompts.

Prompts

MethodPathDescriptionScope
GET/v1/promptsList prompts. Filter with source, type or prompt_repo_id. Paginated.prompts:read
POST/v1/promptsCreate an inline prompt from name, type and body. Returns 201. Accepts dry_run and an Idempotency-Key.prompts:write
GET/v1/prompts/{id}Get a prompt with its body and variables.prompts:read
PATCH/v1/prompts/{id}Change name or body. A changed body saves a new version. Accepts dry_run.prompts:write
DELETE/v1/prompts/{id}Delete an inline prompt that no router binds. Accepts dry_run.prompts:delete
GET/v1/prompts/{id}/versionsList versions, or commits for a GitHub prompt, newest first. Paginated.prompts:read
GET/v1/prompts/{id}/versions/{version}Get one version by number, or one commit by SHA.prompts:read
POST/v1/prompts/{id}/versions/{version}/restoreMake an earlier inline version current, as a new version. Takes no body. Accepts dry_run.prompts:write
GET/v1/prompts/{id}/routersList the routers bound to the prompt.prompts:read
GET/v1/prompts/{id}/tagsList the prompt's tags.prompts:read
POST/v1/prompts/{id}/tagsAdd a tag, sent as tag.prompts:write
DELETE/v1/prompts/{id}/tags/{key}Remove a tag.prompts:delete

A text prompt's body is a string, and a chat prompt's body is an array of role and content messages. A prompt's type is fixed. Writes are conditional on the prompt's ETag.

Create an inline promptbash
curl -X POST https://api.multi-router.ai/v1/prompts \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Support tone", "type": "text", "body": "You are a support agent for {{product}}." }'
Change its bodybash
curl -X PATCH https://api.multi-router.ai/v1/prompts/ip_6Rt2Kw9QmD \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H 'If-Match: "1"' \
  -H "Content-Type: application/json" \
  -d '{ "body": "You are a friendly support agent for {{product}}." }'

Prompt repos

A prompt repo connects a repository and branch through a connected GitHub account, and tracks files from it. Each tracked .md or .json file becomes a GitHub prompt:

MethodPathDescriptionScope
GET/v1/prompt-reposList prompt repos. Paginated.prompt_repos:read
POST/v1/prompt-reposConnect repository (owner/repo) and branch under a name, tracking up to 100 file_paths. Returns 201. Accepts dry_run and an Idempotency-Key.prompt_repos:write
GET/v1/prompt-repos/{id}Get a prompt repo.prompt_repos:read
PATCH/v1/prompt-repos/{id}Rename it. Accepts dry_run.prompt_repos:write
DELETE/v1/prompt-repos/{id}Disconnect it and remove its prompts. Returns files_deleted. Accepts dry_run.prompt_repos:delete
GET/v1/prompt-repos/{id}/filesList tracked files, each with its prompt id and sync status. Paginated.prompt_repos:read
POST/v1/prompt-repos/{id}/filesTrack one file, sent as path. Accepts dry_run.prompt_repos:write
DELETE/v1/prompt-repos/{id}/filesStop tracking the file named in the path query parameter. Accepts dry_run.prompt_repos:delete
GET/v1/prompt-repos/{id}/commitsList commits on the branch, optionally only those touching file_path. Paginated.prompt_repos:read
GET/v1/prompt-repos/{id}/tagsList the repo's tags.prompt_repos:read
POST/v1/prompt-repos/{id}/tagsAdd a tag, sent as tag. Accepts dry_run.prompt_repos:write
DELETE/v1/prompt-repos/{id}/tags/{key}Remove a tag. Accepts dry_run.prompt_repos:delete
Connect a repositorybash
curl -X POST https://api.multi-router.ai/v1/prompt-repos \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Prompts", "repository": "acme/prompts", "branch": "main", "file_paths": ["prompts/support.md"] }'

GitHub connections

The GitHub connections endpoints read and remove connected GitHub accounts:

MethodPathDescriptionScope
GET/v1/github/connectionsList connected GitHub accounts. Paginated.github:read
GET/v1/github/connections/{login}Get one connection by its GitHub login.github:read
DELETE/v1/github/connections/{login}Disconnect a GitHub account. Accepts dry_run.github:delete

GitHub accounts are connected in the dashboard. See GitHub Prompt.

Errors

CodeStatusMeaning
prompt_not_found404The id matches no prompt.
revision_not_found404The prompt has no such version.
invalid_revision400An inline version is not a positive whole number.
unexpected_body400A restore sent a body.
source_read_only409A write names a GitHub prompt.
prompt_in_use409A delete names a prompt that routers bind.
prompt_repo_not_found404The id matches no prompt repo.
prompt_repo_exists409The repository and branch are already connected.
github_repo_not_found404The connected account cannot find the repository or branch.
file_not_found404The file is not tracked.
invalid_file_path400The path is absolute, climbs out of the repository, is too long, or is not a .md or .json file.
github_not_connected424The repository owner's GitHub account is not connected.
github_error502GitHub returned an error.
connection_not_found404No connected GitHub account has that login.
use_sub_collection400An inline prompt PATCH sends tags, or a prompt repo PATCH sends files or tags. They change through their own endpoints; a GitHub prompt PATCH returns source_read_only first. The valid field of the error lists the endpoint path template, such as /v1/prompts/{id}/tags; {id} is not replaced with the object id.

API Reference

Traffic

Reading request records, metrics, charts, captured payloads and CSV exports.

The traffic endpoints read the organization's request records, the same data the Traffic page shows. They need the traffic:read scope and have no writes. See Traffic and Request Detail.

Endpoints

MethodPathDescriptionScope
GET/v1/traffic/requestsList requests, newest first, with filters. Paginated, up to 200 per page.traffic:read
GET/v1/traffic/requests/{request_id}Get one request record.traffic:read
GET/v1/traffic/requests/{request_id}/payloadGet a request's captured request and response bodies and headers.traffic:read
GET/v1/traffic/metricsList the metrics, stats and dimensions that stats and series accept.traffic:read
GET/v1/traffic/statsAggregate values for the metrics named, as comma-separated metric.stat ids.traffic:read
GET/v1/traffic/seriesChart series for the metrics named, as metric.stat or metric.stat@dimension ids, in buckets of bucket seconds, from 1 to 86400.traffic:read
GET/v1/traffic/exportDownload the matching requests as CSV, newest first, up to 100,000 rows.traffic:read

The requests, stats, series and export endpoints take the same filters: start and end as RFC 3339 or epoch milliseconds, q, router_id, router_kind, model, provider, status (2xx, 4xx or 5xx), error_kind, streaming, session_id, correlation_id, comparison_id, role (primary or shadow), customer_user, and the machine identity issuer, subject and binding. An unknown parameter returns 400 unknown_parameter.

Failed requests on one routerbash
curl "https://api.multi-router.ai/v1/traffic/requests?router_id=rt_9Hk3Ws7PzQ&status=5xx&limit=20" \
  -H "Authorization: Bearer $MR_API_KEY"

Each record has the request's router, model, status, tokens, cost_micros, tool_calls, latency_ms, bytes, caller and prompt, and whether a payload is available. The single-request response holds the record in traffic_request. Payloads can contain prompts, headers and credentials sent with the request.

Errors

CodeStatusMeaning
unknown_parameter400The query has a parameter the endpoint does not accept.
invalid_field400A filter or metric has an invalid value, or stats and series have no metrics.
not_found404No request has that id.
payload_not_captured404No payload was captured for this request.
payload_unavailable404The payload was captured but is no longer stored, or payload storage is off.
payload_not_ready409The payload is still being written. Retry after retry_after_seconds.
export_in_progress429Another export is running.
payload_read_failed500The stored payload could not be read. Retry.
traffic_unavailable503Traffic is unavailable. Retry.

API Reference

Tags

Listing the tag names used in an organization.

Tags label routers, prompts, prompt repos and model groups, and budgets can be scoped to a tag. Each resource adds and removes its own tags through its /tags endpoints; this endpoint lists every tag name in use in the organization.

MethodPathDescriptionScope
GET/v1/tagsList the organization's tag names.tags:read
Response 200json
{
  "organization_id": "org_7Tq2Wd9KxP",
  "data": ["production", "staging", "support"],
  "next_cursor": null,
  "has_more": false
}

API Reference

API Tokens

Reading the metadata of the organization's API keys.

The API tokens endpoints read the organization's API keys: their names, scopes, routers and creators. They never return a key's secret, carry no ETag, and have no writes; keys are created and revoked on the API Keys page. See API Key.

MethodPathDescriptionScope
GET/v1/api-tokensList API keys. Paginated.api_tokens:read
GET/v1/api-tokens/{id}Get one API key.api_tokens:read
FieldType
idstring
The key id, prefixed tok_.
namestring
The key name.
descriptionstring or null
The key description.
key_prefixstring
The start of the key, to recognize it.
enabledboolean
Whether the key works.
expires_atstring or null
When the key expires, or null for never.
scopesarray or null
The scopes, or null for full control.
router_idsarray or null
The routers the key is limited to, or null for every router.
agent_idstring or null
The agent the key belongs to, if any.
created_byobject or null
The person, API key or machine identity that created it.
created_atstring
When the key was created.
CodeStatusMeaning
api_token_not_found404No API key has that id in the organization.

API Reference

BudgetsEnterprise

Creating, reading, changing and deleting budgets, with their model caps, alerts, overrides and requests.

The budgets endpoints manage an organization's budgets: their scope, period and caps, the alerts that notify or block, and the current period's overrides and budget requests. Amounts are integers in millionths of a US dollar, so 5000000000 is $5,000. Writes take the budget's bdg_ id. See Budgets & Spend Control.

Endpoints

MethodPathDescriptionScope
GET/v1/budgetsList budgets. Paginated.budgets:read
POST/v1/budgetsCreate a budget. Returns 201. Accepts dry_run and an Idempotency-Key.budgets:write
GET/v1/budgets/{id}Get one budget with its current period's status.budgets:read
PATCH/v1/budgets/{id}Change name, enabled, scope, period, total_cap_micros and timezone. Accepts dry_run.budgets:write
DELETE/v1/budgets/{id}Delete a budget. Accepts dry_run.budgets:delete
GET/v1/budgets/{id}/model-capsList model caps.budgets:read
POST/v1/budgets/{id}/model-capsAdd a cap for one model. Returns 201.budgets:write
PATCH/v1/budgets/{id}/model-capsChange the cap_micros of the model named in ?model=.budgets:write
DELETE/v1/budgets/{id}/model-capsRemove the cap of the model named in ?model=.budgets:delete
GET/v1/budgets/{id}/alertsList alerts.budgets:read
POST/v1/budgets/{id}/alertsAdd an alert. Returns 201.budgets:write
PATCH/v1/budgets/{id}/alerts/{alert_id}Change the fields sent.budgets:write
DELETE/v1/budgets/{id}/alerts/{alert_id}Remove an alert.budgets:delete
GET/v1/budgets/{id}/alert-eventsList the current period's alert firings.budgets:read
GET/v1/budgets/{id}/overridesList the current period's overrides.budgets:write
POST/v1/budgets/{id}/overridesRaise a cap for the current period: cap_micros, with an optional router_id and model. Returns 201.budgets:write
PATCH/v1/budgets/{id}/overridesChange the cap_micros of the override selected by ?router_id= and ?model=.budgets:write
DELETE/v1/budgets/{id}/overridesRemove the override selected by ?router_id= and ?model=.budgets:delete
GET/v1/budgets/{id}/budget-requestsList the current period's budget requests.budgets:write
GET/v1/budgets/{id}/budget-requests/{request_id}Get one budget request.budgets:write
POST/v1/budgets/{id}/budget-requests/{request_id}/approveApprove a pending request with the cap_micros to grant.budgets:write
POST/v1/budgets/{id}/budget-requests/{request_id}/denyDeny a pending request. The body is {}.budgets:write

Model cap and alert writes are conditional on the budget's ETag and take no dry_run. Override and request writes act on the current period and take no If-Match or dry_run. An override with neither router_id nor model applies to the total cap. Override and approval caps are from 1 to 9223372036854775807.

Creating a budget

A budget on routers tagged productionbash
curl -X POST https://api.multi-router.ai/v1/budgets \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production API",
    "scope": { "tags": [{ "tag": "production", "cap_micros": 2000000000 }] },
    "period": { "kind": "monthly" },
    "total_cap_micros": 5000000000,
    "model_caps": [{ "model": "anthropic/claude-opus-5", "cap_micros": 1000000000 }],
    "alerts": [
      { "threshold_percent": 80, "watches": { "kind": "total" },
        "actions": [{ "kind": "send_email", "recipient": "lead@example.com" }] },
      { "threshold_percent": 100, "watches": { "kind": "per_router" },
        "actions": [{ "kind": "block" }] }
    ]
  }'
FieldType
namestring, required
The budget name.
scopeobject, required
Exactly one of {"organization": true}, {"routers": [{"router_id", "cap_micros"}]} or {"tags": [{"tag", "cap_micros"}]}. Replaced whole by PATCH.
periodobject, required
{"kind": "daily"}, weekly, monthly or lifetime. Replaced whole by PATCH.
total_cap_microsinteger or null
The total cap.
model_capsarray
On create, a model slug and cap_micros for each capped model.
alertsarray
On create, the alerts.
timezonestring
The timezone the period resets in. Defaults to UTC.
enabledboolean
Defaults to true.

An alert has threshold_percent, above 0 and up to 200 with at most two decimal places, watches, an optional projected_within_minutes, and actions. watches is {"kind":"total"}, {"kind":"per_router"}, {"kind":"per_model"} or {"kind":"model","model":"…"}. Actions are {"kind":"block"}, {"kind":"send_email","recipient":"…"} and {"kind":"send_webhook","endpoint_id":"…"}. Each alert gets a bdr_ id.

Raise this period's total capbash
curl -X POST https://api.multi-router.ai/v1/budgets/bdg_8Wq3Ld5TnM/overrides \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "cap_micros": 7500000000 }'

Status

A budget's status describes the current period: spend_micros, cap_micros, remaining_micros, burn_rate_micros_per_min, projected_cap_at, label, what is restricted, and unpriced_share. label is ok, nearing, imminent or capped. Status is null until Multi-Router first computes it, and always null in the current object of a 409 or 412 response.

Errors

CodeStatusMeaning
invalid_budget400A field is missing or invalid. The message names it.
invalid_scope400The scope does not hold exactly one of organization, routers or tags.
invalid_threshold400An alert threshold is out of range or has more than two decimal places.
unknown_model400A model cap names a model absent from the catalog.
budget_not_found404No budget with that id exists in the organization.
budget_item_not_found404The budget has no such model cap or alert.
override_not_found404No override matches the router and model.
budget_request_not_found404No budget request has that id.
budgets_off404Budgets are off for the installation.
budget_policy_managed409The budget belongs to a tools policy.
model_cap_exists409The model already has a cap.
override_exists409An override for that router and model already exists.
request_not_pending409The budget request was already decided.
request_period_closed409The budget request's period has ended.
use_sub_collection400A PATCH sends model_caps, alerts or overrides, which change through their own endpoints. The valid field of the error lists the endpoint path template, such as /v1/budgets/{id}/alerts; {id} is not replaced with the object id.
catalog_unavailable503The model catalog could not be read to check model caps. Retry.

API Reference

AgentsEnterprise

Listing and retiring the agents people set up from the Portal.

An agent is the router and API key a person sets up for an AI tool from the Portal. The agents endpoints list active agents and retire them; agents are created from the Portal, not the API. See Managing Agents.

MethodPathDescriptionScope
GET/v1/agentsList active agents. Filter with person_id and router_id. Paginated.agents:read
GET/v1/agents/{id}Get one agent.agents:read
DELETE/v1/agents/{id}Retire an agent, with its router and API key. Needs a key of the account organization.agents:delete

Each agent has its wl_ id, person_id, router_id, token_id, profile_id and created_at.

Retire an agentbash
curl -X DELETE https://api.multi-router.ai/v1/agents/wl_5Np8Rk2VxT \
  -H "Authorization: Bearer $MR_API_KEY"
CodeStatusMeaning
invalid_filter400A filter value is invalid.
account_token_required403A delete uses a key of a named organization.
router_scoped_token403The key is limited to some routers.
agent_not_found404No active agent has that id.
agent_router_has_aliases409The agent's router has aliases. Detach them first.

API Reference

OrganizationsEnterprise

Listing, creating, renaming and deleting organizations.

The organizations endpoints manage the named organizations in an account. The account itself is listed as an organization with is_account true. Creating and deleting need a key of the account organization, and keys limited to some routers cannot use these endpoints. See Organizations.

MethodPathDescriptionScope
GET/v1/organizationsList the organizations the key can see. Paginated.organizations:read
POST/v1/organizationsCreate an organization with a name of 2 to 80 characters. Returns 201. Accepts an Idempotency-Key.organizations:write
GET/v1/organizations/{id}Get an organization, with counts of its contents.organizations:read
PATCH/v1/organizations/{id}Rename it. Accepts dry_run.organizations:write
DELETE/v1/organizations/{id}Delete an empty organization.organizations:delete

contents counts the organization's routers, API keys, provider keys, prompt repos, prompts and people. An organization is deleted only when every count is zero.

Create an organizationbash
curl -X POST https://api.multi-router.ai/v1/organizations \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Engineering" }'
CodeStatusMeaning
account_token_required403A create or delete uses a key of a named organization.
router_scoped_token403The key is limited to some routers.
organization_not_found404No organization has that id in the account.
organization_not_empty409A delete names an organization that still holds something. contents gives the counts.
account_organization409The account organization cannot be renamed or deleted.
organization_limit_reached409The account has its maximum number of organizations.
organization_changed409The organization changed at the same time. Retry.

API Reference

InvitesEnterprise

Inviting people to the account, and changing, resending and revoking invitations.

The invites endpoints invite people to the account by email, with an account role and access to organizations. They need a key of the account organization, and keys limited to some routers cannot use them. See Inviting People.

MethodPathDescriptionScope
GET/v1/people/invitesList invitations. Filter with status: pending, accepted, declined, revoked or expired. Paginated.invites:read
POST/v1/people/invitesInvite a person. Returns 201. Accepts an Idempotency-Key.invites:write
GET/v1/people/invites/{id}Get one invitation.invites:read
PATCH/v1/people/invites/{id}Change account_role or grants of a pending invitation. Accepts dry_run.invites:write
DELETE/v1/people/invites/{id}Revoke an invitation.invites:delete
POST/v1/people/invites/{id}/resendRenew a pending invitation for seven days and email it again. Takes no body. Accepts an Idempotency-Key.invites:write

A create requires email, account_role (admin, member or null, and present even when null), and grants, each with organization_id, role (admin, member or null), managed and policy_id. In a PATCH, account_role: null clears the account role, while grants: null leaves grants unchanged; grants: [] clears them. Creates and resends share a budget of 20 attempts per minute per account; an idempotent replay of a resend does not count or send again.

Invite a personbash
curl -X POST https://api.multi-router.ai/v1/people/invites \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "priya@example.com",
    "account_role": null,
    "grants": [{ "organization_id": "org_3Hd8Kw2PqZ", "role": "admin", "managed": false, "policy_id": null }]
  }'
CodeStatusMeaning
invalid_account_role400The account role is not admin, member or null.
invalid_grants400A grant is invalid.
account_organization_role_mismatch400The grant for the account organization has a role that differs from account_role.
account_token_required403The key belongs to a named organization.
router_scoped_token403The key is limited to some routers.
invite_not_found404No invitation has that id.
person_exists409The person already belongs to the account.
invite_pending409A pending invitation for the email already exists.
invite_not_pending409A change or resend names an invitation that is not pending.
invite_changed409The invitation changed at the same time. Retry.
rate_limited429Over 20 invite attempts per minute. Retry-After gives the seconds to wait.

API Reference

SSOEnterprise

Configuring, verifying, checking, enforcing and removing the account's single sign-on connection.

The SSO endpoints manage the account's one OpenID Connect sign-on connection. They need a key of the account organization. The client secret is accepted on create and change, and never returned; reads show only whether it is set. See Setting Up SSO.

MethodPathDescriptionScope
GET/v1/ssoGet the SSO connection, with its domain verification record and test sign-in URL.sso:read
POST/v1/ssoCreate the connection from domain, provider and oidc. Returns 201. Accepts an Idempotency-Key.sso:write
PATCH/v1/ssoChange the fields sent, including enabled and enforce_sso. Accepts dry_run.sso:write
DELETE/v1/ssoRemove the connection.sso:delete
POST/v1/sso/verify-domainCheck the domain's DNS TXT record and mark the domain verified.sso:write
POST/v1/sso/checkCheck the issuer's discovery document and signing keys.sso:write
FieldType
domainstring, required on create
The email domain that signs in through SSO.
providerstring, required on create
okta, entra, google, ping, jumpcloud, adfs or generic.
oidcobject, required on create
issuer, client_id and client_secret.
allow_subdomainsboolean
Whether the domain's subdomains also sign in through SSO.
jit_provisioningstring
auto or invite.
default_rolestring
member or admin.
new_user_organization_idstring
The organization new people join.
enabledboolean
Whether SSO sign-in is on. PATCH only.
enforce_ssoboolean
Whether people of the domain must sign in through SSO. PATCH only.

Reads add domain_verified, the domain_verification TXT record to publish, scim_connected, last_test_sign_in_at and test_sign_in_url. Setting enforce_sso to true needs a verified domain, a test sign-in and SSO enabled. The verify and check endpoints are rate limited, and return 429 with Retry-After.

Create the connectionbash
curl -X POST https://api.multi-router.ai/v1/sso \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Content-Type: application/json" \
  --data @sso-create.json
sso-create.jsonjson
{
  "domain": "example.com",
  "provider": "okta",
  "oidc": { "issuer": "https://example.okta.com", "client_id": "0oa8x…", "client_secret": "…" },
  "jit_provisioning": "invite",
  "default_role": "member"
}
CodeStatusMeaning
invalid_value400A field has an invalid value, such as an unsupported provider or an organization outside the account.
account_scope_required403The key belongs to a named organization.
sso_disabled403SSO is not enabled for this installation.
sso_not_configured404The account has no SSO connection.
sso_exists409The account already has an SSO connection.
domain_claimed409Another account has claimed the domain.
domain_not_verified409The DNS TXT record was not found.
enforcement_not_ready409Enforcement needs a verified domain, a test sign-in and SSO enabled. details.missing lists what is missing.
sso_enforced409A domain change or delete while SSO is enforced. Turn enforcement off first.
scim_connected409A directory connection locks the SSO settings. Disconnect SCIM first.
rate_limited429Too many verify or check calls. Retry after 60 seconds.
dns_unavailable502DNS could not be read. Retry.

API Reference

Machine IdentityEnterprise

Registering identity issuers, granting them to organizations, and binding workload subjects to routers and scopes.

The machine identity endpoints manage issuers, the identity providers whose tokens workloads present, and bindings, which let a workload subject from an issuer call chosen routers with chosen scopes. Issuers belong to the account and are granted to organizations; their writes need a key of the account organization. Bindings belong to the organization of the key. See Machine Identity.

MethodPathDescriptionScope
GET/v1/machine-identity/issuersList issuers visible to the organization. Paginated.machine_identity:read
POST/v1/machine-identity/issuersRegister an issuer from name and issuer_url. Returns 201. Accepts dry_run and an Idempotency-Key.machine_identity:write
GET/v1/machine-identity/issuers/{id}Get one issuer, with its key-fetch health.machine_identity:read
PATCH/v1/machine-identity/issuers/{id}Change name, enabled, max_token_lifetime_seconds, on_key_fetch_failure and tenancy. Accepts dry_run.machine_identity:write
DELETE/v1/machine-identity/issuers/{id}Delete an issuer with no bindings or grants. Accepts dry_run.machine_identity:delete
POST/v1/machine-identity/issuers/{id}/organizationsGrant the issuer to the organization_id sent. Accepts dry_run.machine_identity:write
DELETE/v1/machine-identity/issuers/{id}/organizations/{organization_id}Remove a grant. Accepts dry_run.machine_identity:delete
GET/v1/machine-identity/bindingsList the organization's bindings. Paginated.machine_identity:read
POST/v1/machine-identity/bindingsCreate a binding. Returns 201. Accepts dry_run and an Idempotency-Key.machine_identity:write
GET/v1/machine-identity/bindings/{id}Get one binding.machine_identity:read
PATCH/v1/machine-identity/bindings/{id}Change router_ids, scopes and enabled. Accepts dry_run.machine_identity:write
DELETE/v1/machine-identity/bindings/{id}Delete a binding. Accepts dry_run.machine_identity:delete

An issuer create can also set max_token_lifetime_seconds, on_key_fetch_failure (serve_stale or fail_closed), enabled and organization_ids. Multi-Router reads the issuer's discovery document to find its jwks_uri and tenancy; a dry run returns that preview without saving. A binding has issuer_id, subject, subject_type (ci or kubernetes_service_account), subject_match (exact or prefix), router_ids, scopes and an optional enabled.

Bind a GitHub Actions workflow to a routerbash
curl -X POST https://api.multi-router.ai/v1/machine-identity/bindings \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "issuer_id": "mii_2Kd7Wq9LmP",
    "subject": "repo:acme/app:ref:refs/heads/main",
    "subject_type": "ci",
    "subject_match": "exact",
    "router_ids": ["rt_9Hk3Ws7PzQ"],
    "scopes": ["inference"]
  }'
CodeStatusMeaning
invalid_parameter400A field is invalid. The field names it.
issuer_discovery_failed400The issuer's discovery document or keys could not be read.
account_organization_required403An issuer write uses a key of a named organization.
not_found404No issuer or binding has that id.
issuer_not_granted409The issuer is not granted to the organization of the binding.
duplicate_binding409A binding with the same issuer, subject and match type already exists, in this or another organization.
cross_organization_precedence409The binding overlaps another organization's binding for the same issuer.
issuer_in_use409A delete names an issuer that still has grants or bindings.
organization_in_use409A grant removal names an organization that still has bindings to the issuer.
organization_already_granted409The issuer is already granted to the organization.
account_issuer_limit409The account has its maximum number of issuers.
issuer_grant_limit409The issuer has its maximum number of grants.
issuer_binding_limit409The issuer has its maximum number of bindings.
use_sub_collection400An issuer PATCH sends organization_ids. Grants change through /v1/machine-identity/issuers/{id}/organizations.
Was this helpful?