Rouva Docs
Everything you need to connect your app to Rouva — from a quick start to the full API reference.
Updated on August 25, 2026
Quick start
Connect your app to Rouva in three steps. No infrastructure changes required.
-
Create an account
Sign up at app.rouva.io and connect one or more AI provider API keys in Settings. -
Generate your Rouva gateway key
Go to Integrations in your dashboard and click Generate API key. Your key starts with rva_. -
Route your first request
Use the SDK or call the gateway directly. Rouva handles the rest.
SDK
The official Node.js SDK is the fastest way to integrate. One client, any connected provider — drop-in compatible with the OpenAI SDK.
Install
npm install @rouvanpm/rouva
Usage
import { Rouva } from '@rouvanpm/rouva'
const rouva = new Rouva({ apiKey: 'rva_your_key' })
const response = await rouva.chat.completions.create({
messages: [{ role: 'user', content: 'Your prompt here' }],
})
Already using OpenAI or Anthropic?
Change two lines. Rouva uses the same message format — your existing code works as-is.
// Before (OpenAI) import OpenAI from 'openai' const openai = new OpenAI({ apiKey: '...' }) const res = await openai.chat.completions.create({ messages, model: 'gpt-4o' }) // Before (Anthropic) import Anthropic from '@anthropic-ai/sdk' const anthropic = new Anthropic({ apiKey: '...' }) const res = await anthropic.messages.create({ messages, model: 'claude-sonnet-4-6', max_tokens: 1024 }) // After — one client, any provider, intelligently routed import { Rouva } from '@rouvanpm/rouva' const rouva = new Rouva({ apiKey: 'rva_...' }) const res = await rouva.chat.completions.create({ messages, model: 'gpt-4o' }) // model optional
Options
| Option | Type | Required | Description |
|---|---|---|---|
| apiKey | string | Required | Your rva_ gateway key from the dashboard |
| baseURL | string | Optional | Override the default gateway URL |
Gateway endpoint
Prefer Python or another language? Call the gateway directly over HTTP — no SDK required.
POST https://app.rouva.io/api/gateway/messages
OpenAI-compatible endpoint
The same handler is also served at POST /v1/chat/completions, so any OpenAI-compatible client — the OpenAI SDKs, LangChain, the Vercel AI SDK — works by pointing baseURL at Rouva with your rva_ key. No other code changes.
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://app.rouva.io/v1",
apiKey: process.env.ROUVA_API_KEY, // rva_...
});
const completion = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "Hello" }],
});
Three things to know about this endpoint: the model you name is honored exactly — never substituted (intelligent routing applies only to the native gateway endpoint below and the SDK). It normally accepts only OpenAI-format models, but if you have a Routing Override configured for the detected task type, the override can route to any connected provider — including Anthropic — and Rouva translates the response to OpenAI format transparently. And stream follows OpenAI's convention: omitted means a buffered JSON completion, stream: true means SSE. Everything else — sampling options, tool use, which unsupported options are rejected — works as described in the reference below.
Headers
| Header | Value |
|---|---|
| Authorization | Bearer rva_your_key |
| Content-Type | application/json |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| messages | array | Required | Array of { role, content } objects. Supported roles: system, user, assistant (plus tool on tools requests). Content must be a string, except on tools requests where tool-call turns and tool-result blocks are forwarded verbatim. |
| model | string | Required on /v1 | Your intended model. On the gateway endpoint it's optional — omit to let Rouva route freely; when provided, Rouva routes cheaper when possible and tracks savings against it. With Intelligent Routing off (dashboard Settings → Gateway), a named model is honored exactly, and omitting it serves your connected provider's default model. On /v1/chat/completions it's required; non-tools requests are always honored exactly. Tools requests on /v1 are routed to the cheapest capable model within the same provider when Intelligent Routing is on (the named model sets the cost ceiling), or honored exactly when it's off. |
| system | string | array | Optional | System prompt — a plain string or Anthropic-style text blocks. Equivalent to a system message at the head of messages. |
| max_tokens | number | Optional | Maximum tokens to generate. Defaults to 4096. max_completion_tokens is accepted as an alias. Values below 1024 steer auto-routing away from reasoning models, which spend output budget on hidden reasoning tokens. |
| temperature | number | Optional | Sampling temperature between 0 and 1. Reasoning models (gpt-5 family) only support their default temperature, so it is omitted when routing to one. |
| top_p | number | Optional | Nucleus sampling, greater than 0 and at most 1. Forwarded to every provider; top_p: 1 (the no-op default) is treated as omitted. OpenAI reasoning models (gpt-5 family, o-series) don't support it: auto-routing avoids them when top_p is set, and pinning one returns a 400. |
| stop | string | string[] | Optional | Up to 4 stop sequences, forwarded to every provider (Anthropic receives them as stop_sequences). |
| seed | integer | Optional | Best-effort deterministic sampling. Only OpenAI honors it, so it requires an OpenAI model and pins the request to that model — no cheaper-model substitution. |
| stream | boolean | Optional | On the gateway endpoint, defaults to true (SSE stream); set stream: false for a buffered JSON response — the upstream provider's completion body, returned verbatim. On /v1/chat/completions the default is false, matching OpenAI's convention. |
| tools | array | Optional | Tool definitions in your target provider's own format, forwarded verbatim. Requires model. On the native gateway endpoint, the named model is always honored exactly. On /v1/chat/completions, tool schemas are provider-specific so routing stays within the same provider — Rouva picks the cheapest capable model in that provider when Intelligent Routing is on, or pins the named model exactly when it's off. |
| tool_choice | string | object | Optional | Forwarded verbatim in your target provider's format. Only valid alongside tools. |
| metadata | object | Optional | Flat key-value map of strings you want stored alongside the request — up to 10 keys, keys up to 64 characters, values up to 256 characters. Saved to the database for every request but not yet surfaced in the dashboard UI. Useful for attaching your own labels (e.g. { "env": "production", "feature": "chat" }) so you can query spend and usage by dimension later. |
Task type & confidence in the dashboard
Every non-tools request — on either endpoint, routed or pinned — is classified with a task type and quality-scored after the response completes. The task type powers dashboard analytics and selects the scoring rubric (a Q&A answer is judged differently than code), and the confidence score is Rouva's quality signal for the response itself — both are independent of which model served the request. Tools requests are classified when routing overrides are configured (so the override can apply to the right task type), and show a task type in the dashboard when classification ran. Confidence score always shows "—" for tools — a tool_calls turn with null content isn't meaningfully scorable, so the judge is skipped. Responses served from the semantic cache keep their task type (classification runs before the cache lookup) but show "—" for confidence — the original response was scored when it was first generated. Tokens and cost are recorded for every request either way.
Agent tools
The gateway passes tool conversations through untouched: tools and tool_choice, OpenAI-style assistant tool_calls and role: "tool" messages, and Anthropic-style tool_use/tool_result content blocks are all forwarded byte-for-byte to the chosen model, and the provider's response streams back unchanged. Write your tools in the schema of the provider that owns your pinned model — OpenAI's format also applies to Gemini, DeepSeek, Mistral, Moonshot, xAI, Z.ai, and Alibaba Qwen (and works on both endpoints); Anthropic-format tools go through this gateway endpoint or the SDK.
Within-provider intelligent routing for agents (/v1 only) — when Intelligent Routing is on (the default), tools requests on /v1/chat/completions are routed to the cheapest capable model within the same provider. The named model sets a cost ceiling: Rouva will never route to anything more expensive. Routing stays within the provider because tool schemas are provider-specific — cross-provider substitution would send the wrong schema upstream. Requests with seed, temperature, top_p, or stop set skip substitution for reasoning models (which don't support those parameters), and a model with an insufficient max_tokens budget for reasoning is rescued by the cheapest non-reasoning alternative. Turn Intelligent Routing off in dashboard Settings → Gateway to always pin the exact model. The native gateway endpoint (/api/gateway/messages) does not perform cost-reduction routing for tools requests, but account-level routing overrides (configured in Settings → Intelligent Routing) still apply when the override targets the same provider as the pinned model — tool schemas are provider-specific, so cross-provider overrides are always skipped. When a cross-provider override is skipped on /v1, within-provider cost-reduction routing still runs and may choose a cheaper model within the same provider. To guarantee exact model pinning on either endpoint, disable Intelligent Routing or remove the matching override in the dashboard. Savings are recorded whenever a cheaper model is substituted.
Tools responses are not quality-scored or served from the semantic cache.
Stop reasons follow the provider's own semantics, relayed verbatim. Notably, OpenAI returns finish_reason: "stop" — not "tool_calls" — when tool_choice forces a specific function. Detect tool invocations from the response content, not the stop reason: for OpenAI-format providers check for tool_calls in the deltas (or on message when stream: false); for Anthropic check for tool_use content blocks. (The Node SDK normalizes both to message.tool_calls for you.)
Session tracking
Tag all turns of an agent run with a shared session ID so you can correlate requests across a multi-step run. Pass x-rouva-session-id as a request header — the ID is stored on each snapshot and visible in the expanded detail row of the request log, making it easy to trace every turn that belonged to the same run.
Using the Node SDK, call startSession() once before the agent loop begins. The SDK auto-generates a unique ID and attaches it to every subsequent request automatically. Call endSession() when the run is complete.
import { Rouva } from '@rouvanpm/rouva'
const rouva = new Rouva({ apiKey: 'rva_...' })
// Start a session — auto-generates an ID, attaches to every request
const sessionId = rouva.startSession()
const tools = [{
type: 'function',
function: { name: 'get_weather', description: 'Get weather for a city',
parameters: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] } }
}]
// All turns carry the same session ID automatically
const turn1 = await rouva.chat.completions.create({
model: 'gpt-5-mini',
messages: [{ role: 'user', content: 'What is the weather in SF?' }],
tools,
})
const turn2 = await rouva.chat.completions.create({
model: 'gpt-5-mini',
messages: [{ role: 'user', content: 'What about NYC?' }],
tools,
})
// End the session when the agent run is complete
rouva.endSession()
console.log(sessionId) // 'rva-sess-abc123xyz'
When calling the gateway directly (without the SDK), pass the header manually on every request in the run:
POST /api/gateway/messages Authorization: Bearer rva_your_key x-rouva-session-id: my-agent-run-001 Content-Type: application/json
Session IDs are optional — requests without one are tracked individually as before. The session ID appears in the expanded detail row of each request in the dashboard for manual correlation across turns.
Agentic run — POST /api/gateway/run
/api/gateway/run hands the entire agent loop to Rouva. You provide tool definitions and handler URLs; the gateway calls the model, dispatches tool calls in parallel batches to your endpoints, appends results, and loops until the model stops or a limit is reached — no client-side loop required.
Every turn is logged as a usage snapshot grouped under a shared session_id, giving you a full session replay in the dashboard with per-turn cost, model used, tool calls, finish reason, and any mid-run model escalations.
Node SDK
import { Rouva } from '@rouvanpm/rouva'
const rouva = new Rouva({ apiKey: 'rva_...' })
const result = await rouva.run({
model: 'claude-haiku-4-5-20251001',
messages: [{ role: 'user', content: 'What is the weather in Paris and London?' }],
tools: [{
name: 'get_weather',
description: 'Get current weather for a city',
input_schema: {
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
},
}],
tool_handlers: {
// Gateway POSTs { name, arguments } as JSON; response body is returned to the model
get_weather: 'https://api.example.com/weather',
},
max_turns: 5,
session_budget_usd: 0.10,
})
console.log(result.content) // final model response
console.log(result.turns) // turns taken
console.log(result.tool_calls_made) // total tool calls dispatched
console.log(result.session_id) // groups all turns in the dashboard
console.log(result.finish_reason) // why the loop exited
console.log(result.session_cost_usd) // total USD spent
Raw HTTP
POST /api/gateway/run
Authorization: Bearer rva_your_key
Content-Type: application/json
{
"model": "claude-haiku-4-5-20251001",
"messages": [{ "role": "user", "content": "What is the weather in Paris?" }],
"tools": [{
"name": "get_weather",
"description": "Get current weather for a city",
"input_schema": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}],
"tool_handlers": {
"get_weather": "https://api.example.com/weather"
},
"max_turns": 5
}
Response:
{
"content": "The weather in Paris is 18°C and partly cloudy.",
"turns": 2,
"tool_calls_made": 1,
"session_id": "rva-sess-abc123xyz",
"finish_reason": "stop",
"session_cost_usd": 0.000041
}
Request parameters
| Parameter | Type | Description |
|---|---|---|
| messages required | array | Conversation seed — usually a single user message |
| tools required | array | Tool definitions — OpenAI or Anthropic format |
| tool_handlers required | object | Tool name → public HTTPS handler URL. Receives POST { name, arguments } |
| model required | string | Model to use — required for all /run requests |
| provider | string | Force a specific provider |
| max_turns | number | Max loop iterations (default: 10) |
| max_tokens | number | Max tokens per turn (default: 4096) |
| temperature | number | Sampling temperature 0–1 |
| session_budget_usd | number | Post-turn stopping threshold — the loop exits after any turn that causes cumulative spend to reach this value; the triggering turn is still billed in full |
Handler URL requirements
- Must be a public HTTPS URL — literal private IPs and localhost are rejected with 400
- Gateway sends POST with Content-Type: application/json and body {"{ \"name\": \"tool_name\", \"arguments\": { ...tool_input } }"}
- The response body (text or JSON) is returned to the model as the tool result
- Tool calls within a turn are dispatched in parallel batches of up to 16
When to use /run vs /api/gateway/messages
| /run | /api/gateway/messages | |
|---|---|---|
| Tool loop | Gateway-managed | Client-managed |
| Session grouping | Automatic | Manual (x-rouva-session-id) |
| Streaming | No | Yes |
| Use when | Fully delegating an agentic task | Building your own loop or need streaming |
Example — Python
import requests
# The gateway responds with an SSE stream (text/event-stream) by default,
# so iterate the lines rather than calling response.json().
# (Pass "stream": False in the body to get a plain JSON completion instead.)
response = requests.post(
"https://app.rouva.io/api/gateway/messages",
headers={
"Authorization": "Bearer rva_your_key",
"Content-Type": "application/json",
},
json={
"messages": [{"role": "user", "content": "Hello!"}],
},
stream=True,
)
for line in response.iter_lines():
if line:
print(line.decode()) # data: {...} SSE events
Routing overrides
Routing overrides let you pin specific models to specific task types at the account level — as an enforced policy, not a hint. When an override is active for a task type, Rouva ignores the model the caller requested and routes to your override model instead, regardless of which endpoint or SDK is used.
How to configure
In your dashboard, go to Settings → Intelligent Routing. For each task type (Creative, Code, Analysis, etc.) you can select any model from your connected providers. Save the override — it takes effect immediately on all subsequent requests.
How it works
Every non-tools request is classified by task type (Creative, Code, Summarize, etc.) before routing. If an override exists for the detected task type and Intelligent Routing is enabled, the override model is selected — even if the caller explicitly requested a different model. The dashboard request log marks these rows with an ⊕ override badge so you can see when policy applied.
Caveats
- Intelligent Routing must be enabled. Overrides are inactive when IR is turned off — the caller's model is used as-is.
- Sampling compatibility. Overrides are skipped when the request includes parameters that would cause an upstream error — a seed with a non-OpenAI override, or top_p/stop with an OpenAI reasoning model. temperature on a reasoning-model override is silently omitted rather than a skip condition.
- Tools overrides stay within the same provider. Tool schemas are provider-specific, so an override only applies when it targets the same provider as the pinned model — for example, a gpt-4o tools request can override to gpt-4.1-mini (both OpenAI), but not to claude-sonnet-5 (Anthropic schemas differ). Cross-provider overrides are silently skipped and the pinned model is used.
- Cross-provider on /v1 is non-streaming only. When a /v1 request routes to an Anthropic override, streaming (stream: true) is not yet translated — /v1 defaults to non-streaming, so this is rarely encountered.
Supported models
Pass any of these model IDs in the model field to override routing. Make sure the relevant provider key is connected in your dashboard Settings.
Anthropic
Complex reasoning, coding, and long-context work.
claude-fable-5
claude-opus-5
claude-opus-4-8
claude-opus-4-7
claude-opus-4-6
claude-sonnet-5
claude-sonnet-4-6
claude-haiku-4-5-20251001
OpenAI
General-purpose, coding, and vision-heavy tasks.
gpt-5-nano
gpt-5-mini
gpt-5.4-mini
gpt-5
gpt-5.6-luna
gpt-5.6-terra
gpt-5.6-sol
gpt-4.1-nano
gpt-4.1-mini
gpt-4.1
gpt-4o
gpt-4o-mini
Google Gemini
Fast multimodal and long-context reasoning.
gemini-3.7-flash
gemini-3.6-flash
gemini-3.5-flash-lite
gemini-2.5-flash
gemini-2.5-pro
xAI Grok
Chat, coding, and knowledge work.
grok-4.6
grok-4.5
grok-4.3
grok-4.20-0309-reasoning
DeepSeek
Strong coding and reasoning with efficient pricing.
deepseek-v4-flash
deepseek-v4-flash-think
deepseek-v4-pro
Mistral
Lightweight European models for deterministic workloads.
mistral-small-latest
mistral-large-latest
Moonshot Kimi
Coding, research, and agent workflows.
kimi-k2.6
kimi-k3
Z.ai
Long-horizon agent workflows and lightweight tasks.
glm-5.3
glm-5.2
glm-4.7-flash
Alibaba Qwen
High-throughput Chinese frontier models at competitive pricing.
qwen3-235b-a22b
qwen3-32b
qwen3-30b-a3b
qwen-plus
qwen-turbo
qwen-max
Ready to get started?
Create your account, connect a provider, and make your first routed request in minutes.
Get started free →