ModelRiver returns one of two response shapes, depending on which endpoint you call:
| Endpoint | Response shape | Best for |
|---|---|---|
Native API: POST /v1/ai | ModelRiver envelope: message, status, data, customer_data, meta | Full metadata, cache fields, failover details |
OpenAI-compatible: POST /v1/chat/completions (and /v1/completions, /v1/embeddings) | Standard OpenAI response | Drop-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:
1{2 "model": "my_chat_workflow",3 "messages": [4 {"role": "user", "content": "Hello!"}5 ]6}Response:
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": 2123 }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": 045 },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:
| Header | When it is sent |
|---|---|
X-ModelRiver-Provider, X-ModelRiver-Model | Requests that use a structured output (the provider and model that served it) |
X-ModelRiver-Cached | true when the response was served from cache |
X-ModelRiver-Workflow | Responses 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:
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)
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)
1import openai2 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)
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
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 envelope20interface 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
| Aspect | Native API (/v1/ai) | OpenAI-compatible endpoints |
|---|---|---|
| Response body | ModelRiver envelope | Standard OpenAI format |
| Metadata location | meta in the body | Provider/model headers only |
Cache fields (customer_data) | ✅ Yes | ❌ No |
| SDK support | Manual parsing | ✅ OpenAI SDKs |
Best practices
- Use the OpenAI-compatible endpoints when you want existing OpenAI SDK code to work unchanged
- Use the native API when you need
customer_data, failover details, or backend pipelines - Check
statuson native responses before readingdata - Monitor
meta.attemptsto detect fallback patterns - Don't send
format: it has no effect
Next steps
- Error handling: Complete error reference
- Endpoints: Full endpoint specification
- OpenAI compatibility: SDK integration