Your AI needs your data. Not your database.
For developers adding AI to existing apps: multi-step workflows that call your backend at each step, while your app keeps control of the data.
Free plan · No credit card · Test mode runs without provider keys
1Your backend starts the workflow
POST /v1/ai/async
{
"workflow": "support_reply",
"stream": true,
"messages": [
{ "role": "user", "content": "Where is order 1234?" }
]
}2It returns a channel right away (202)
{
"status": "pending",
"channel_id": "3f8b2c71…",
"ws_token": "eyJhbGciOi…",
"websocket_channel": "ai_response:{project_id}:3f8b2c71…"
}3Your frontend streams the answer
client.connect({
channelId: res.channel_id,
wsToken: res.ws_token,
websocketChannel: res.websocket_channel,
});
client.on("stream_delta", (chunk, text) => render(text));Order 1234 shipped on Monday and should arr
From a single AI request to a complete backend pipeline.
Send one request. ModelRiver runs the steps, calls your backend, and streams the result.
ModelRiver remembers the conversation. Send the session ID, not the history.
{
"workflow": "my_chatbot",
"session_id": "9f8c2a1e…",
"messages": [{ "role": "user", "content": "Cancel it please" }]
}
// First turn: omit session_id; it returns in meta.session_id- My order is 1234
- It shipped Monday and arrives Thursday.
- Earlier turns added by ModelRiver
- Cancel it please
- It has shipped. Want me to start a return?
AI does the work, your backend adds the data and rules, your app gets the result.
// ModelRiver calls you when a step needs your data
app.post("/modelriver/webhook", async (req, res) => {
res.sendStatus(200);
const { ai_response, callback_url } = req.body.data;
const data = await db.find(ai_response.data.record_id);
await fetch(callback_url, {
method: "POST", headers, body: JSON.stringify({ data }),
});
});1AI step
- Webhook
2Your data and rules
- Callback
3AI step
- Result delivered to your app
Your users watch the answer appear, token by token.
import { ModelRiverClient } from "@modelriver/client";
const client = new ModelRiverClient();
client.on("stream_delta", (chunk, text) => render(text));
client.connect({ channelId, wsToken, websocketChannel });- Where is order 1234?
- It shipped Monday and arr
Get typed JSON back, checked against your schema.
{
"status": "success",
"data": { "eligible": true, "amount": 49, "reason": "damaged" }
}| eligible | true | boolean | ✓ |
| amount | 49 | number | ✓ |
| reason | "damaged" | string | ✓ |
Already on the OpenAI SDK? Change the base URL and keep your code.
const client = new OpenAI({
baseURL: "https://api.modelriver.com/v1",
apiKey: process.env.MODELRIVER_API_KEY,
});
await client.chat.completions.create({ model: "my_workflow", messages });- openai/gpt-6.1-terraSuccess812ms
- anthropic/claude-sonnet-5-5Backup after an openai errorSuccess1.1s
One infrastructure. Different AI applications.
Same pipeline every time: AI starts, your backend adds data and rules, AI finishes.
Start from a working template.
Import a ready backend pipeline, run it on sample data, then make it yours.
Customer support
Refund request triage
Classifies a refund request, then decides eligibility and amount, with your backend in between.
Sales
Lead qualification
Scores inbound leads, spots the ready-to-buy ones, and drafts a personal follow-up.
HR & hiring
Job application screening
Screens applications, scores the fit, and drafts the next-step email.
Built to fit your application
Your keys, your backend, your frontend. ModelRiver runs the part in between.
Any provider, your keys
13 AI providers, 139 models, your own keys.
Your backend in the loop
Signed webhooks fetch your data. ModelRiver never touches your database.
Provider failover
Up to two backup models when a call fails.
Live to the browser
Token by token over a hosted WebSocket.
Structured outputs
Responses checked against your JSON schema.
Draft, Live, rollback
Publish a draft, roll back from History.
Ready for production
Test, cap spend, and trace every request.
Frequently asked questions
How is this different from calling the OpenAI or Anthropic SDK directly?
The SDKs make one model call. ModelRiver runs the workflow around it: async requests your frontend can follow, a signed webhook to your backend at each backend step, failover to backup models, and logs for every request, webhook, and callback. You still use your own provider keys.
How is this different from an AI gateway?
An AI gateway sits between your app and model providers and routes each call. ModelRiver also connects to providers, but it is built around the workflow: it pauses for your backend, waits for your callback, carries the result to the next step, and sends progress to your users.
Does ModelRiver get access to my database?
No. ModelRiver never connects to your database. Your backend reads it and sends back only the fields the next step needs. Those fields are stored in your request logs and passed to later steps.
Why not give an agent direct access to my systems?
Agents tend to work best with broad access to your systems and data. With ModelRiver, your backend decides exactly what each AI step receives and which actions run.
Which providers and models are supported?
ModelRiver supports 13 AI providers, 139 models in total, including OpenAI, Anthropic, Google, xAI, Mistral AI, Azure OpenAI, and Amazon Bedrock. You connect your own API keys. See the full list.
What does it cost?
The Free plan includes 25,000 requests a month. Paid plans start at $49 a month for 500,000 requests. You pay AI providers directly with your own keys. See pricing.
GitHub Client SDK on npm Changelog Status Security Founders' note