Documentation

Rouva Docs

Everything you need to connect your app to Rouva — from a quick start to the full API reference.

Updated on September 12, 2026


Quick start

Connect your app to Rouva in three steps. No infrastructure changes required.

  1. Create an account
    Sign up at app.rouva.io and connect one or more AI provider API keys in Settings.
  2. Generate your Rouva gateway key
    Go to Integrations in your dashboard and click Generate API key. Your key starts with rva_.
  3. 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' }],
})
Omit model to let Rouva route automatically to the cheapest capable model. Pass a model to use it as a cost ceiling — Rouva will route cheaper when possible, and savings are tracked against it.

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

API reference

All three endpoints require Authorization: Bearer rva_your_key and Content-Type: application/json.

POST /api/gateway/messages Native gateway — intelligent routing, streaming

Routes your prompt to the cheapest capable model across your connected providers. Streams by default. Omit model to let Rouva auto-route, or pin a model as a cost ceiling.

▸ Request parameters
FieldTypeRequiredDescription
messagesarrayRequiredArray of { role, content } objects. Roles: system, user, assistant, tool. Content must be a string except on tools requests.
modelstringOptionalPin a model. Omit to auto-route. For non-tools requests, Rouva routes cheaper when possible and tracks savings against it. For tools requests, the named model is honored exactly on this endpoint, except when a same-provider routing override applies — in that case the override model is used instead. Workspace members are additionally subject to the administrator’s model allowlist: if the requested model is disallowed, the cheapest permitted model is substituted. Honored exactly for all requests when Intelligent Routing is off.
providerstringOptionalanthropic, openai, gemini, mistral, deepseek, xai, zai, alibaba, moonshot
systemstring | arrayOptionalSystem prompt — plain string or Anthropic-style text blocks.
max_tokensnumberOptionalDefaults to 4096. max_completion_tokens accepted as alias.
temperaturenumberOptional0–1. Omitted for reasoning models.
streambooleanOptionalDefaults to true (SSE). Set false for buffered JSON.
toolsarrayOptionalTool definitions in your provider's format, forwarded verbatim. Requires model.
tool_choicestring | objectOptionalForwarded verbatim. Only valid with tools.
top_pnumberOptionalNucleus sampling. top_p: 1 treated as omitted. Not supported on OpenAI reasoning models.
stopstring | string[]OptionalUp to 4 stop sequences.
seedintegerOptionalOpenAI models only — pins routing to OpenAI.
metadataobjectOptionalFlat string map stored with the request. Up to 10 keys.
Unsupported options are rejected with an explicit 400, never silently ignored: response_format, logit_bias, logprobs, reasoning_effort, parallel_tool_calls, n ≠ 1, non-zero penalties, and the legacy functions/function_call fields.

Response format

Responses use the wire format of the provider Rouva routed to — either OpenAI-format or Anthropic-format. Parse the format that matches the provider, or use the SDK which normalises both.

OpenAI-format stream (SSE)

data: {"id":"chatcmpl-...","choices":[{"delta":{"content":"Hello"},"index":0}]}
data: {"id":"chatcmpl-...","choices":[{"delta":{},"finish_reason":"stop","index":0}]}
data: [DONE]

Anthropic-format stream (SSE)

data: {"type":"content_block_delta","delta":{"type":"text_delta","text":"Hello"}}
data: {"type":"message_delta","delta":{"stop_reason":"end_turn"}}
data: {"type":"message_stop"}

Buffered JSON (stream: false) — OpenAI format

{"id":"chatcmpl-...","choices":[{"message":{"role":"assistant","content":"Hello"},"finish_reason":"stop","index":0}],"usage":{"prompt_tokens":10,"completion_tokens":5,"total_tokens":15}}
POST /v1/chat/completions OpenAI-compatible alias

Drop-in replacement for the OpenAI API. Point any OpenAI SDK or compatible client at https://app.rouva.io/v1 with your rva_ key — no other changes needed.

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://app.rouva.io/v1",
  apiKey: process.env.ROUVA_API_KEY,
});

const completion = await client.chat.completions.create({
  model: "gpt-4o-mini",
  messages: [{ role: "user", content: "Hello" }],
});
▸ Differences from /api/gateway/messages
  • model is required
  • provider field is not accepted — provider is inferred from the model
  • stream defaults to false (OpenAI convention)
  • Restricted to OpenAI-format providers (OpenAI, Gemini, DeepSeek, Mistral, Moonshot, xAI, Z.ai, Alibaba Qwen) unless a Routing Override routes to Anthropic — for non-tools requests only, Rouva translates the response transparently. Tools requests always stay within the same provider (tool schemas are provider-specific). When Intelligent Routing is on, the named model acts as a cost ceiling — Rouva may route to a cheaper capable model within the same provider. The named model is honored exactly only when Intelligent Routing is off. When a same-provider override applies, the override model is used instead of the named model
  • Anthropic-format tool definitions (input_schema) are rejected — use OpenAI format
  • stream_options only accepts omitted, {}, or { include_usage: true } — any other value (e.g. { include_usage: false } or extra keys) returns 400
POST /api/gateway/run Agentic loop — server-managed tool dispatch

Hand the entire agent loop to Rouva. Provide tool definitions and handler URLs; the gateway calls the model, dispatches tool calls in parallel to your endpoints, appends results, and loops until the model stops or a limit is reached.

const result = await rouva.run({
  model: 'claude-haiku-4-5-20251001',
  messages: [{ role: 'user', content: 'Weather in Paris and London?' }],
  tools: [{ name: 'get_weather', description: 'Get 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,
  session_budget_usd: 0.10,
})
▸ Request parameters
Parameter Type Description
messages requiredarrayConversation seed — usually a single user message
tools requiredarrayTool definitions — OpenAI or Anthropic format
tool_handlers requiredobjectTool name → public HTTPS handler URL. Receives POST { name, arguments }
model requiredstringModel to use
providerstringForce a specific provider. Required when the model ID is not in the registry and has no known provider prefix.
max_turnsnumberMax loop iterations (default: 10)
max_tokensnumberMax tokens per turn (default: 4096)
temperaturenumberSampling temperature 0–1
session_budget_usdnumberPost-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, so spend can exceed the limit by one turn’s cost

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
}

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. Write your tools in the schema of the provider that owns your pinned model.

Stop reason caveat: When tool_choice forces a specific function on an OpenAI-format provider, the response may return finish_reason: "stop" even though tool_calls are present. 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.

Session tracking

Tag all turns of an agent run with a shared session ID. Pass x-rouva-session-id as a request header, or use rouva.startSession() in the SDK — the ID is stored on each snapshot and visible in the request log. Call rouva.endSession() when the run is complete so subsequent requests are not grouped into the same session.

POST /api/gateway/messages
Authorization: Bearer rva_your_key
x-rouva-session-id: my-agent-run-001
Content-Type: application/json

Cross-provider harness

The agentic /run endpoint includes a canonical translation layer — a provider-neutral representation of messages, tool calls, and tool results. With Intelligent Routing enabled, the gateway selects a model across your connected Anthropic and OpenAI-compatible providers once before the loop starts, then uses that provider for all turns. Automatic routing always picks a model at or below the pinned model's cost; Routing Overrides are applied as policy and are not price-constrained. You can write tools in either OpenAI or Anthropic format — the gateway detects the dialect by shape and re-serializes to whatever the selected model expects.

Cross-dialect routing is conditional on the session being translatable. The restrictions apply to cross-provider candidate selection — the automatic within-provider fallback (substituting a cheaper model on the same provider) is unaffected. Routing Overrides targeting the same provider are also subject to the dialect gate: an override whose target model uses the opposite dialect may be skipped when the session contains opaque content for that dialect. Each set of conditions blocks cross-provider selection targeting a particular dialect while leaving the other eligible:

  • Blocks routing to OpenAI-compatible providers (Anthropic targets remain eligible): Anthropic images, document blocks, thinking blocks, or cache_control on messages, text blocks, tool schemas, or tool results; is_error on tool results; documents or images nested in tool result content; thought_signature or other Anthropic-specific fields on tool calls.
  • Blocks routing to Anthropic (OpenAI-compatible targets remain eligible): developer-role messages; untranslatable OpenAI content parts (file, audio blocks — image_url is translated automatically only in tool-result content; in user or assistant messages it is opaque); reasoning_content, audio, or refusal fields; participant name; strict or other OpenAI-specific fields on tool schemas (top-level or inside function); malformed or non-object tool-call arguments; untranslatable tool-result content parts or tool-result metadata extensions.
  • Never crosses dialect groups: routing between two OpenAI-compatible providers (e.g. OpenAI → DeepSeek, Gemini → Alibaba) is not performed — vendor-specific extensions would not transfer safely across those providers.

Tool schema formats

Both tool schema formats are accepted in the tools array:

OpenAI format (also accepted by /run)
// OpenAI tool format
{
  "type": "function",
  "function": {
    "name": "get_weather",
    "description": "Get weather for a city",
    "parameters": {
      "type": "object",
      "properties": { "city": { "type": "string" } },
      "required": ["city"]
    }
  }
}

// Anthropic tool format — also accepted
{
  "name": "get_weather",
  "description": "Get weather for a city",
  "input_schema": {
    "type": "object",
    "properties": { "city": { "type": "string" } },
    "required": ["city"]
  }
}

What the canonical layer does

On every /run turn, the gateway:

  1. Detects each tool's format by shape — OpenAI (type:"function" or a function key) or Anthropic (input_schema) — regardless of which provider the model is on.
  2. Normalizes all messages into a provider-neutral canonical form, preserving tool call IDs, arguments, names, and result content verbatim.
  3. Selects the target provider once before the loop starts — via Intelligent Routing across all your connected providers, or your pinned model. That provider is used for all turns.
  4. Re-serializes the full conversation history for the target provider's wire format — OpenAI tool_calls/role:"tool" or Anthropic tool_use/tool_result blocks — automatically.
  5. Forwards to the upstream model and merges the response back into the session history.

System messages are hoisted to Anthropic's top-level system parameter when routing to Claude, and inlined as role:"system" for OpenAI — no client changes required.

Same-provider verbatim replay

Tool schemas whose format matches the target provider are replayed verbatim — preserving vendor extensions like strict (OpenAI) or cache_control (Anthropic) exactly as you defined them. Cross-format schemas are normalized through the canonical layer.

Tip: Cross-provider routing requires Intelligent Routing to be enabled. The selected provider is fixed for the entire session — all turns use the same provider. Untranslatable content blocks routing to the incompatible target dialect (see the direction-specific restrictions above) while leaving the other dialect eligible.


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.

Policy wins over the caller. If your app requests gpt-5 and your Creative override is set to claude-sonnet-5, a creative prompt routes to Claude Sonnet 5. The caller's model becomes a no-op for that task type.

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.

Team controls

Rouva supports multi-member workspaces. As the workspace owner you can invite team members, set per-member spend caps, and restrict which models each member can access.

Inviting members

Go to the Team page in your dashboard and enter a colleague's email address. An invite link is sent — invite links expire after 7 days. Once the invitee accepts, they are added as a workspace member and can route requests through your connected provider keys. If the invitee already has an rva_… key, it becomes workspace-aware automatically — no action needed. If they don't yet have a key, they must go to the Gateway page in their dashboard and generate one after accepting.

Spend caps

Click Edit next to any member to open their settings panel. Enter a monthly USD cap in the Spend cap field and click Save cap. Once a member's month-to-date spend reaches the cap, all subsequent gateway requests from their key are rejected with 429 Too Many Requests and a Retry-After header set to the number of seconds until the cap resets at the next UTC month start. The cap resets automatically on the 1st of each month (UTC), or the workspace owner can raise it at any time.

Spend alerts for members

When a member's month-to-date spend crosses 80% or 100% of their cap, Rouva automatically sends an email alert — no configuration required beyond setting the cap. Two emails go out per threshold crossing:

  • Member alert — sent to the member's account email, showing their current spend, cap, and usage percentage.
  • Owner alert — sent to the workspace owner's account email, identifying which member triggered the alert.

Alerts are deduplicated per threshold per day — each threshold (80% and 100%) fires at most once per member per 24-hour window, even if the member makes many requests. If a single gateway request takes the member past both thresholds at once, only the 100% notification is delivered — the 80% alert is skipped. If your workspace has a Slack webhook configured, the owner also receives a Slack notification for each threshold crossing.

Model restrictions

In the same Edit panel, expand providers under Model restrictions and check the models you want to allow. Saving with no boxes checked means all models are available (no restrictions). When a member requests a disallowed model, the gateway transparently reroutes to the permitted model with the lowest input-token price within an eligible set and only returns 403 if none exists. On /api/gateway/messages, tools requests restrict the fallback to models on the same provider (tool schemas are provider-specific); non-tools requests can cross providers freely. On /api/gateway/run, cross-provider fallbacks are allowed between dialect groups (e.g. OpenAI-compatible → Anthropic) but same-dialect cross-provider routing is excluded (e.g. OpenAI → DeepSeek). Two additional direction-specific exclusions apply: Anthropic providers are excluded as fallbacks when the request contains a developer role message, OpenAI-specific tool schema extensions (e.g. strict), or other OpenAI-dialect-only content; and non-Anthropic providers are excluded when the request contains Anthropic-only metadata such as cache_control, thinking blocks, or is_error on a tool result. If the only permitted model sits in the excluded dialect, the request returns 403. Restrictions are scoped to that member and do not affect other members or your own key.

Restriction scope. Model restrictions apply after Intelligent Routing and overrides have been evaluated. If the chosen model is already on the allowlist it is used as-is. Only when the chosen model is disallowed does the gateway substitute the lowest-input-price permitted model within the eligible set. Savings are recorded separately whenever the final model is cheaper than the model the caller explicitly requested — this happens with ordinary Intelligent Routing too and is not specific to allowlist substitution.

Revoking access

Click Remove on any member row to revoke that member's workspace access. Their existing rva_… key is not deleted immediately — it continues to exist but will no longer authenticate as a workspace member. Pending invites can be cancelled from the Pending invites list before they are accepted.


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.

Models not listed here still work when pinned: valid-but-unlisted IDs — new releases, dated snapshots like gpt-4o-mini-2024-07-18, fine-tunes like ft:gpt-4o-mini:… — are matched to their provider by naming convention and forwarded as-is. Until Rouva's registry knows their pricing, the dashboard records their token counts with zero cost.

Anthropic

Complex reasoning, coding, and long-context work.

claude-fable-5-1 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.8-flash 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-flash-4-1 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 →