Documentation

Response formats

The native API returns a consistent envelope with metadata. The OpenAI-compatible endpoints return standard OpenAI responses for drop-in SDK use.

ModelRiver returns one of two response shapes, depending on which endpoint you call:

EndpointResponse shapeBest for
Native API: POST /v1/aiModelRiver envelope: message, status, data, customer_data, metaFull metadata, cache fields, failover details
OpenAI-compatible: POST /v1/chat/completions (and /v1/completions, /v1/embeddings)Standard OpenAI responseDrop-in use with OpenAI SDKs

The format request field ("raw" or "wrapped") is accepted for backward compatibility but has no effect: the shape depends only on the endpoint. Request examples select a workflow with the model alias; native requests can also use workflow, and workflow wins if both are present.

Native API envelope (/v1/ai)

Every synchronous /v1/ai response uses the same envelope.

Request:

JSON
1{
2 "model": "my_chat_workflow",
3 "messages": [
4 {"role": "user", "content": "Hello!"}
5 ]
6}

Response:

JSON
1{
2 "message": "success",
3 "status": "success",
4 "data": {
5 "id": "chatcmpl-123",
6 "object": "chat.completion",
7 "created": 1677652288,
8 "model": "gpt-4o",
9 "choices": [
10 {
11 "index": 0,
12 "message": {
13 "role": "assistant",
14 "content": "Hello! How can I help you today?"
15 },
16 "finish_reason": "stop"
17 }
18 ],
19 "usage": {
20 "prompt_tokens": 9,
21 "completion_tokens": 12,
22 "total_tokens": 21
23 }
24 },
25 "customer_data": {
26 "user_id": "550e8400-e29b-41d4-a716-446655440000"
27 },
28 "meta": {
29 "status": "success",
30 "http_status": 200,
31 "workflow": "my_chat_workflow",
32 "requested_provider": "openai",
33 "requested_model": "gpt-4o",
34 "used_provider": "openai",
35 "used_model": "gpt-4o",
36 "duration_ms": 1250,
37 "usage": {
38 "prompt_tokens": 9,
39 "completion_tokens": 12,
40 "total_tokens": 21,
41 "cache_read_tokens": 0,
42 "cache_write_tokens": 0,
43 "cache_write_1h_tokens": 0,
44 "reasoning_tokens": 0
45 },
46 "structured_output": false,
47 "attempts": [
48 {
49 "provider": "openai",
50 "model": "gpt-4o",
51 "status": "success"
52 }
53 ],
54 "customer_fields": ["user_id"]
55 }
56}

What data contains

  • Chat workflows without a structured output: an OpenAI-style chat completion object. Responses from other providers are converted to this shape, so you always read data.choices[0].message.content.
  • Workflows with a structured output: the parsed JSON object that matched your schema (see Structured outputs).
  • Other request types: the result for that request type (see Request types).

Response headers

Metadata is returned in the body under meta, not in headers. A few headers are added in specific cases:

HeaderWhen it is sent
X-ModelRiver-Provider, X-ModelRiver-ModelRequests that use a structured output (the provider and model that served it)
X-ModelRiver-Cachedtrue when the response was served from cache
X-ModelRiver-WorkflowResponses served from cache, and non-chat request types

When to use the native API

✅ Metadata in every response: provider used, attempts, duration, and token usage ✅ Cache fields: customer_data echoes the fields you configured on the workflow ✅ Failover visibility: meta.attempts lists every provider attempt ✅ Async and backend pipelines: the WebSocket and webhook results use the same data, customer_data, and meta fields


OpenAI-compatible responses

The OpenAI-compatible endpoints return the standard OpenAI response body, so existing OpenAI SDKs work without changes. They do not include customer_data or meta.

Response from POST /v1/chat/completions:

JSON
1{
2 "id": "chatcmpl-123",
3 "object": "chat.completion",
4 "created": 1677652288,
5 "model": "my_chat_workflow",
6 "choices": [
7 {
8 "index": 0,
9 "message": {
10 "role": "assistant",
11 "content": "Hello! How can I help you today?"
12 },
13 "finish_reason": "stop"
14 }
15 ],
16 "usage": {
17 "prompt_tokens": 9,
18 "completion_tokens": 12,
19 "total_tokens": 21,
20 "prompt_tokens_details": { "cached_tokens": 0, "cache_write_tokens": 0 },
21 "completion_tokens_details": { "reasoning_tokens": 0 }
22 }
23}

The provider and model that served the request are returned in the X-ModelRiver-Provider and X-ModelRiver-Model headers. See OpenAI compatibility for details.

When to use the OpenAI-compatible endpoints

✅ Drop-in replacement: Replace OpenAI calls without changing client code ✅ SDK compatibility: Works with existing OpenAI SDKs and frameworks ✅ Standard integration: Apps that expect the standard OpenAI response format


Usage examples

JavaScript (native API)

JAVASCRIPT
1const response = await fetch("https://api.modelriver.com/v1/ai", {
2 method: "POST",
3 headers: {
4 Authorization: "Bearer YOUR_API_KEY",
5 "Content-Type": "application/json",
6 },
7 body: JSON.stringify({
8 model: "my_chat_workflow",
9 messages: [{ role: "user", content: "Hello!" }],
10 }),
11});
12 
13const body = await response.json();
14if (body.status === "success") {
15 console.log(body.data.choices[0].message.content);
16 console.log(`Served by ${body.meta.used_provider} in ${body.meta.duration_ms}ms`);
17} else {
18 console.error(body.error);
19}

Python (OpenAI SDK, OpenAI-compatible endpoint)

PYTHON
1import openai
2 
3client = openai.OpenAI(
4 base_url="https://api.modelriver.com/v1",
5 api_key="YOUR_API_KEY"
6)
7 
8response = client.chat.completions.create(
9 model="my_chat_workflow",
10 messages=[{"role": "user", "content": "Hello!"}]
11)
12 
13print(response.choices[0].message.content)

cURL (native API)

Bash
curl -X POST https://api.modelriver.com/v1/ai \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "my_chat_workflow",
"messages": [
{"role": "user", "content": "Hello!"}
]
}'

TypeScript type definitions

TYPESCRIPT
1// OpenAI-compatible chat completion (also the shape of `data` for chat workflows)
2interface ChatCompletion {
3 id: string;
4 object: string;
5 created: number;
6 model: string;
7 choices: Array<{
8 index: number;
9 message: { role: string; content: string };
10 finish_reason: string;
11 }>;
12 usage: {
13 prompt_tokens: number;
14 completion_tokens: number;
15 total_tokens: number;
16 };
17}
18 
19// Native /v1/ai success envelope
20interface ModelRiverResponse<T = ChatCompletion> {
21 message: string;
22 status: "success";
23 data: T;
24 customer_data: Record<string, any>;
25 meta: {
26 status: string;
27 http_status: number;
28 workflow: string;
29 requested_provider: string;
30 requested_model: string;
31 used_provider: string;
32 used_model: string;
33 duration_ms: number;
34 usage: {
35 prompt_tokens: number;
36 completion_tokens: number;
37 total_tokens: number;
38 };
39 structured_output: boolean;
40 attempts: Array<{
41 provider: string;
42 model: string;
43 status: string;
44 reason?: any;
45 }>;
46 customer_fields: string[];
47 };
48}

Error handling

Native API errors use the same envelope with status: "error" and an error object instead of data. The OpenAI-compatible endpoints return OpenAI-style error bodies. See Error handling for complete details.


Comparison

AspectNative API (/v1/ai)OpenAI-compatible endpoints
Response bodyModelRiver envelopeStandard OpenAI format
Metadata locationmeta in the bodyProvider/model headers only
Cache fields (customer_data)✅ Yes❌ No
SDK supportManual parsing✅ OpenAI SDKs

Best practices

  1. Use the OpenAI-compatible endpoints when you want existing OpenAI SDK code to work unchanged
  2. Use the native API when you need customer_data, failover details, or backend pipelines
  3. Check status on native responses before reading data
  4. Monitor meta.attempts to detect fallback patterns
  5. Don't send format: it has no effect

Next steps