Documentation

Coming from another AI gateway

Your fallback list, preset or config header becomes one named workflow. Your code changes the base URL, the API key and the model value.

ModelRiver speaks the OpenAI Chat Completions protocol, so most code written for another OpenAI-compatible gateway keeps its SDK. What changes is where the routing lives. Other gateways usually take a provider model in model (openai/gpt-4o) and routing rules in the request, a header or a config file. In ModelRiver, model is the name of a workflow, and the workflow holds the primary model, up to two backups, structured output, guardrails, budgets and its release history.

This page is the technical reference. For a side-by-side overview of each platform, see Migrate to ModelRiver.

What changes in your code

Three values change. Everything else in the request (messages, temperature, max_tokens, tools, stream) stays as it is.

SettingBeforeAfter
Base URLYour gateway's URLhttps://api.modelriver.com/v1
API keyYour gateway keyA ModelRiver project key, mr_live_…
modelA provider model, preset or aliasA workflow name, e.g. support_bot

Before you switch, create a workflow and add your provider keys to the project. Creating a workflow publishes its first Live version, so it is callable right away. Workflow names use letters, digits and underscores.

Concept mapping

What you have todayIn ModelRiver
provider/model string in model (openai/gpt-4o)The workflow's primary model. Send the workflow name instead
Fallback list in the request (models: [...], providerOptions.gateway.models)The workflow's Backup 1 and Backup 2. ModelRiver tries the primary, then each backup in order
Saved preset, virtual model or alias (@preset/…, vmc/…, LiteLLM model_name)A workflow. The name you send stays the same while you edit and publish new versions
Routing config in a header (x-portkey-config)Nothing in the request. The workflow holds the routing
Provider keys stored in the gatewayProvider keys stored in your ModelRiver project (BYOK). Providers bill you directly; ModelRiver adds no token markup
Versioned config or preset historyDraft and Live releases with rollback
response.model showing the provider modelresponse.model is the workflow name; the provider and model that answered are in response headers
Load balancing across keys or weighted targetsNo equivalent today. A workflow runs the primary first and moves to a backup only on failure
Prepaid credits or one bill for all providersNo equivalent. Each provider bills your own account
Responses API (/v1/responses)Not supported. Use Chat Completions; see OpenAI compatibility

A workflow holds one primary and up to two backups. If your fallback list is longer, keep the three you rely on most.

Before and after, by gateway

The "before" snippets show each gateway's documented syntax, per their docs as of Nov 2026. Check their current docs for the exact options your setup uses.

OpenRouter

Before: a provider model in model, backups in models.

PYTHON
1from openai import OpenAI
2 
3client = OpenAI(
4 base_url="https://openrouter.ai/api/v1",
5 api_key="OPENROUTER_API_KEY",
6)
7 
8response = client.chat.completions.create(
9 model="openai/gpt-4o",
10 extra_body={"models": ["anthropic/claude-sonnet-4", "google/gemini-2.5-flash"]},
11 messages=[{"role": "user", "content": "Summarize this ticket"}],
12)

After: create a workflow support_bot with primary openai/gpt-4o, Backup 1 anthropic/claude-sonnet-4, Backup 2 google/gemini-2.5-flash. Then:

PYTHON
1from openai import OpenAI
2 
3client = OpenAI(
4 base_url="https://api.modelriver.com/v1",
5 api_key="mr_live_YOUR_API_KEY",
6)
7 
8response = client.chat.completions.create(
9 model="support_bot",
10 messages=[{"role": "user", "content": "Summarize this ticket"}],
11)

If you call a preset (model="@preset/support-bot"), create a workflow with the preset's models and send its name. OpenRouter docs: Quickstart, Model fallbacks.

Vercel AI Gateway

Before: a provider/model string in the AI SDK, with fallback models in providerOptions.gateway.

TYPESCRIPT
1import { generateText } from "ai";
2 
3const { text } = await generateText({
4 model: "openai/gpt-4o",
5 prompt: "Summarize this ticket",
6 providerOptions: {
7 gateway: {
8 models: ["anthropic/claude-sonnet-4", "google/gemini-2.5-flash"],
9 },
10 },
11});

After: the same workflow support_bot, called through the OpenAI provider. Use .chat(): since AI SDK 5 the default model call uses the Responses API, which ModelRiver doesn't support.

TYPESCRIPT
1import { createOpenAI } from "@ai-sdk/openai";
2import { generateText } from "ai";
3 
4const modelriver = createOpenAI({
5 baseURL: "https://api.modelriver.com/v1",
6 apiKey: process.env.MODELRIVER_API_KEY,
7});
8 
9const { text } = await generateText({
10 model: modelriver.chat("support_bot"),
11 prompt: "Summarize this ticket",
12});

A virtual model (vmc/<slug>) maps to a workflow the same way: recreate its models and options in a workflow and send the workflow name. If you call the gateway's OpenAI-compatible endpoint (https://ai-gateway.vercel.sh/v1) instead of the AI SDK, follow the OpenRouter example above. Vercel docs: Provider options, Virtual models.

Portkey

Before: the routing config travels in the x-portkey-config header.

PYTHON
1import json
2from openai import OpenAI
3 
4config = {
5 "strategy": {"mode": "fallback"},
6 "targets": [
7 {"override_params": {"model": "@openai-prod/gpt-4o"}},
8 {"override_params": {"model": "@anthropic-prod/claude-sonnet-4"}},
9 ],
10}
11 
12client = OpenAI(
13 base_url="https://api.portkey.ai/v1",
14 api_key="unused",
15 default_headers={
16 "x-portkey-api-key": "PORTKEY_API_KEY",
17 "x-portkey-config": json.dumps(config),
18 },
19)
20 
21response = client.chat.completions.create(
22 model="gpt-4o",
23 messages=[{"role": "user", "content": "Summarize this ticket"}],
24)

After: each target becomes a workflow slot (primary openai/gpt-4o, Backup 1 anthropic/claude-sonnet-4), and the headers go away.

PYTHON
1from openai import OpenAI
2 
3client = OpenAI(
4 base_url="https://api.modelriver.com/v1",
5 api_key="mr_live_YOUR_API_KEY",
6)
7 
8response = client.chat.completions.create(
9 model="support_bot",
10 messages=[{"role": "user", "content": "Summarize this ticket"}],
11)

If you reference a saved config by ID, map that config to one workflow. Portkey docs: Fallbacks.

LiteLLM

Before: a self-hosted proxy with aliases and fallbacks in config.yaml.

YAML
1model_list:
2 - model_name: support-bot
3 litellm_params:
4 model: openai/gpt-4o
5 api_key: os.environ/OPENAI_API_KEY
6 - model_name: support-bot-backup
7 litellm_params:
8 model: anthropic/claude-sonnet-4
9 api_key: os.environ/ANTHROPIC_API_KEY
10 
11litellm_settings:
12 fallbacks: [{"support-bot": ["support-bot-backup"]}]
PYTHON
1client = OpenAI(base_url="http://0.0.0.0:4000", api_key="sk-litellm-key")
2response = client.chat.completions.create(model="support-bot", messages=messages)

After: each model_name with its fallbacks becomes one workflow. The provider keys from litellm_params go into your ModelRiver project.

PYTHON
1client = OpenAI(base_url="https://api.modelriver.com/v1", api_key="mr_live_YOUR_API_KEY")
2response = client.chat.completions.create(model="support_bot", messages=messages)

LiteLLM aliases often contain hyphens. support-bot still resolves to a workflow named support_bot, but send the exact name. If you use LiteLLM's Python SDK rather than the proxy, keep the openai/ prefix so it calls an OpenAI-compatible endpoint: model="openai/support_bot" with api_base="https://api.modelriver.com/v1". LiteLLM docs: Proxy reliability and fallbacks.

See which model ran

The response model is always the workflow name. To see which provider and model answered, read the response headers:

HeaderValue
x-modelriver-workflowThe workflow that handled the request
x-modelriver-revisionThe workflow revision that ran
x-modelriver-model-resolved-fromThe name you sent, when it was resolved to a different spelling (e.g. support-bot → support_bot)
x-modelriver-providerThe provider that answered
x-modelriver-modelThe provider model that answered; a backup's model when a backup answered
Bash
curl -si https://api.modelriver.com/v1/chat/completions \
-H "Authorization: Bearer $MODELRIVER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "support_bot", "messages": [{"role": "user", "content": "Hi"}]}' \
| grep -i '^x-modelriver-'

Request Logs show every attempt for each request, including the ones that failed over.

First errors and fixes

A model value that doesn't match a workflow returns 404 with code: "model_not_found" and a modelriver object that names the reason and suggests workflows. The SDK error message carries the same text.

JSON
1{
2 "error": {
3 "message": "'openai/gpt-4o' is a provider model. ModelRiver's model field takes a workflow name; the workflow picks provider, model and fallbacks. Create one in Console → Workflows. List yours: GET /v1/models",
4 "type": "invalid_request_error",
5 "param": "model",
6 "code": "model_not_found",
7 "modelriver": {
8 "reason": "provider_model",
9 "suggestions": [],
10 "docs_url": "https://modelriver.com/docs/api/gateway-migration"
11 }
12 }
13}
reasonExample messageFix
provider_model'gpt-4o' is a provider model. ModelRiver's model field takes a workflow name. Workflow 'support_bot' in this project runs openai/gpt-4o — send model="support_bot".Send the suggested workflow name. With no suggestion, create a workflow with that model
near_missWorkflow 'suport_bot' not found. Did you mean 'support_bot'? Names use letters, digits and underscores.Fix the spelling
near_miss (case)Workflow 'Support_Bot' not found. Names are case-sensitive — did you mean 'support_bot'?Match the case exactly
ambiguous'my-wf' matches more than one workflow: 'my_wf', 'mywf'. Send the exact name.Send one of the listed names
unknownWorkflow 'x' not found. This project has: a, b, c (+N more — GET /v1/models).Check the API key's project, or pick a listed name
missing_liveWorkflow 'support_bot' has no Live version. Publish it in Console → Workflows.Publish the workflow
none_callableNo workflow in this project accepts completion requests. Create a completion workflow in Console → Workflows.Create a workflow with the matching request type
no_workflowsThis project has no workflows yet. In ModelRiver, model is a workflow name, not a provider model. Create a workflow in Console → Workflows, then send its name.Create your first workflow

Two other first-day errors:

  • 401 with {"error": "Invalid or missing API key"}: the request still carries your old gateway key, or the header isn't Authorization: Bearer mr_live_…. This body is a plain string, not the OpenAI error object.
  • 404 on POST /v1/responses: your SDK is calling the Responses API. Switch to Chat Completions (modelriver.chat(…) in the Vercel AI SDK, set_default_openai_api("chat_completions") in the OpenAI Agents SDK).

GET /v1/models lists the names you can send. See Error handling for every other status code.

Switch over safely

  1. Add your provider keys to the ModelRiver project.
  2. Create one workflow per preset, alias or fallback list, and run it in test mode or the Playground.
  3. Change the base URL, API key and model in one code path.
  4. Keep your current gateway configured in your code as a fallback until you're confident, then remove it.
  5. Edit workflows as Drafts and publish them to Live when the change is ready. Roll back from History if needed.

Next steps

OpenRouter, Vercel, Portkey and LiteLLM are trademarks of their respective owners. ModelRiver is not affiliated with, endorsed or sponsored by them.