# AUTO-GENERATED — DO NOT EDIT MANUALLY # Source: scripts/docs/generate_llms_txt.py # Regenerated on every deploy # Auriko > Intelligent LLM routing API with OpenAI-compatible interface. Route requests > across multiple AI providers to optimize for cost, latency, and reliability. ## Sections in this document 1. Guides — streaming, tool calling, routing, cost optimization, error handling, prompt caching, budget management, advanced routing 2. Response API — overview, streaming, tool calling, structured output, reasoning, routing and extensions 3. API Reference — endpoints, parameters, schemas, error codes 4. SDKs — Python and TypeScript guides plus full API reference (types, parameters, error classes) 5. Framework Integrations — LangChain, CrewAI, OpenAI Agents SDK, Google ADK, LlamaIndex 6. Platform — rate limits, team management, BYOK 7. Reference — supported parameters, blocked fields, error codes, response metadata, response headers, canonical models 8. Error Code Reference — individual error code documentation (causes, resolution, examples) 9. Changelog — release history and notable changes 10. Integrations === # Auriko Guides ## Page: Streaming Auriko streams chat completions over Server-Sent Events (SSE). Set `stream: true` and iterate over chunks as they arrive. --- ## Page: Streaming > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) --- ## Page: Streaming > Section: Stream responses Stream a chat completion response: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Write a short story"}], stream=True ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Write a short story" }], stream: true, }); let content = ""; for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { content += chunk.choices[0].delta.content; } } console.log(content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Write a short story"}], stream=True ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Write a short story" }], stream: true, }); for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { process.stdout.write(chunk.choices[0].delta.content); } } ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Write a short story"}], "stream": true }' ``` --- ## Page: Streaming > Section: Stream asynchronously Stream with the async client: ```python Python OpenAI import os from openai import AsyncOpenAI import asyncio async def stream_response(): client = AsyncOpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) stream = await client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Write a short story"}], stream=True ) async for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) asyncio.run(stream_response()) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); async function streamChat(userMessage: string): Promise { const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: userMessage }], stream: true, }); let content = ""; for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { content += chunk.choices[0].delta.content; } } return content; } const result = await streamChat("Hello!"); console.log(result); ``` ```python Python Auriko import os from auriko import AsyncClient import asyncio async def stream_response(): client = AsyncClient( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) stream = await client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Write a short story"}], stream=True ) async for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) asyncio.run(stream_response()) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); async function streamResponse() { const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Write a short story" }], stream: true, }); for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { process.stdout.write(chunk.choices[0].delta.content); } } } streamResponse(); ``` --- ## Page: Streaming > Section: Stream events Each chunk contains: ```python # ChatCompletionChunk chunk.id # "chatcmpl-abc123" chunk.model # "gpt-4o" chunk.created # 1234567890 chunk.choices[0].delta.content # Token content (may be None) chunk.choices[0].delta.role # "assistant" (first chunk only) chunk.choices[0].delta.reasoning_content # Reasoning fragment (if model supports it) chunk.choices[0].delta.reasoning_signature # Signature for current thinking block chunk.choices[0].delta.reasoning_redacted_data # Encrypted redacted thinking data chunk.choices[0].finish_reason # None until last chunk ("stop") chunk.choices[0].native_finish_reason # Provider's original value (e.g. "end_turn") ``` --- ## Page: Streaming > Section: Handle final chunks The last content chunk carries `finish_reason`. A trailing chunk carries `usage` on every stream. You don't need to set `stream_options`. ```python Python OpenAI stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], stream=True ) full_content = "" usage = None for chunk in stream: if chunk.choices: if chunk.choices[0].delta.content: full_content += chunk.choices[0].delta.content if chunk.choices[0].finish_reason: print(f"\n\nFinished: {chunk.choices[0].finish_reason}") if chunk.usage: usage = chunk.usage if usage: print(f"Tokens used: {usage.total_tokens}") ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], stream: true, stream_options: { include_usage: true }, }); let content = ""; let usage = null; for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { content += chunk.choices[0].delta.content; } if (chunk.usage) { usage = chunk.usage; } } console.log(content); if (usage) console.log(`Tokens: ${usage.total_tokens}`); ``` ```python Python Auriko stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], stream=True ) full_content = "" usage = None for chunk in stream: if chunk.choices: if chunk.choices[0].delta.content: full_content += chunk.choices[0].delta.content if chunk.choices[0].finish_reason: print(f"\n\nFinished: {chunk.choices[0].finish_reason}") if chunk.usage: usage = chunk.usage if usage: print(f"Tokens used: {usage.total_tokens}") ``` ```typescript TypeScript Auriko const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], stream: true, }); let fullContent = ""; for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { fullContent += chunk.choices[0].delta.content; } if (chunk.choices[0]?.finish_reason) { console.log(`\n\nFinished: ${chunk.choices[0].finish_reason}`); } } if (stream.usage) { console.log(`Tokens used: ${stream.usage.total_tokens}`); } ``` The final streaming chunk always contains token usage. Setting `stream_options.include_usage` explicitly is harmless but unnecessary. --- ## Page: Streaming > Section: Stream properties The stream object exposes usage, routing metadata, and response headers after iteration completes. | Property | Python | TypeScript | Available | |----------|--------|------------|-----------| | Token usage | `stream.usage` | `stream.usage` | After iteration | | Routing info | `stream.routing_metadata` | `stream.routing_metadata` | After iteration | | Response headers | `stream.response_headers` | `stream.responseHeaders` | Immediately | | Close connection | `stream.close()` | `stream.close()` | Any time | Use the stream as a context manager to ensure the connection is released: ```python Python Auriko with client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], stream=True ) as stream: for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) # Available after iteration if stream.usage: print(f"Tokens: {stream.usage.total_tokens}") if stream.routing_metadata: print(f"Provider: {stream.routing_metadata.provider}") ``` ```typescript TypeScript Auriko const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], stream: true, }); for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { process.stdout.write(chunk.choices[0].delta.content); } } // Available after iteration console.log(`Tokens: ${stream.usage?.total_tokens}`); console.log(`Provider: ${stream.routing_metadata?.provider}`); ``` Use a context manager for automatic cleanup: ```python Python OpenAI stream = await client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], stream=True ) try: async for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) finally: await stream.close() ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], stream: true, }); let content = ""; for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { content += chunk.choices[0].delta.content; } } console.log(content); ``` ```python Python Auriko async with await client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], stream=True ) as stream: async for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` ```typescript TypeScript Auriko const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], stream: true, }); try { for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { process.stdout.write(chunk.choices[0].delta.content); } } } finally { stream.close(); } ``` `routing_metadata` and `usage` arrive on separate trailing chunks after all content chunks. Consume the stream to completion to access them. In TypeScript, you can only iterate a stream once. A second attempt throws an error. --- ## Page: Streaming > Section: Stream with tools Reassemble streamed tool call chunks into complete function calls: ```python Python OpenAI stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "What's the weather in Paris?"}], tools=[{ "type": "function", "function": { "name": "get_weather", "description": "Get weather", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}} } }], stream=True ) tool_calls = [] for chunk in stream: if not chunk.choices: continue delta = chunk.choices[0].delta if delta.tool_calls: for tc in delta.tool_calls: if tc.index >= len(tool_calls): tool_calls.append({"id": tc.id, "function": {"name": "", "arguments": ""}}) if tc.function and tc.function.name: tool_calls[tc.index]["function"]["name"] += tc.function.name if tc.function and tc.function.arguments: tool_calls[tc.index]["function"]["arguments"] += tc.function.arguments print(tool_calls) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const tools: OpenAI.ChatCompletionTool[] = [{ type: "function", function: { name: "get_weather", description: "Get weather for a city", parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"], }, }, }]; const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "What's the weather in Paris?" }], tools, stream: true, }); const toolCalls: Record = {}; for await (const chunk of stream) { const delta = chunk.choices[0]?.delta; if (delta?.tool_calls) { for (const tc of delta.tool_calls) { if (!toolCalls[tc.index]) { toolCalls[tc.index] = { name: "", arguments: "" }; } if (tc.function?.name) toolCalls[tc.index].name = tc.function.name; if (tc.function?.arguments) toolCalls[tc.index].arguments += tc.function.arguments; } } } for (const [, tc] of Object.entries(toolCalls)) { console.log(`${tc.name}: ${tc.arguments}`); } ``` ```python Python Auriko stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "What's the weather in Paris?"}], tools=[{ "type": "function", "function": { "name": "get_weather", "description": "Get weather", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}} } }], stream=True ) tool_calls = [] for chunk in stream: if not chunk.choices: continue delta = chunk.choices[0].delta # Handle tool call streaming if delta.tool_calls: for tc in delta.tool_calls: if tc.index >= len(tool_calls): tool_calls.append({"id": tc.id, "function": {"name": "", "arguments": ""}}) if tc.function and tc.function.name: tool_calls[tc.index]["function"]["name"] += tc.function.name if tc.function and tc.function.arguments: tool_calls[tc.index]["function"]["arguments"] += tc.function.arguments print(tool_calls) ``` ```typescript TypeScript Auriko const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "What's the weather in Paris?" }], tools: [{ type: "function", function: { name: "get_weather", description: "Get weather", parameters: { type: "object", properties: { city: { type: "string" } } }, }, }], stream: true, }); const toolCalls: Array<{ id: string; function: { name: string; arguments: string } }> = []; for await (const chunk of stream) { if (!chunk.choices.length) continue; const delta = chunk.choices[0].delta; if (delta.tool_calls) { for (const tc of delta.tool_calls) { if (tc.index >= toolCalls.length) { toolCalls.push({ id: tc.id!, function: { name: "", arguments: "" } }); } if (tc.function?.name) toolCalls[tc.index].function.name += tc.function.name; if (tc.function?.arguments) toolCalls[tc.index].function.arguments += tc.function.arguments; } } } console.log(toolCalls); ``` See [Tool Calling Guide](/guides/tool-calling) for function definitions and multi-turn tool conversations. --- ## Page: Streaming > Section: Stream with routing options Pass routing options to a streaming request: ```python Python OpenAI stream = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], stream=True, extra_body={"gateway": {"routing": { "optimize": "ttft-focus", "max_ttft_ms": 1000, }}} ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const stream = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], stream: true, gateway: { routing: { optimize: "ttft-focus", max_ttft_ms: 1000, } }, } as OpenAI.ChatCompletionCreateParamsStreaming); let content = ""; for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { content += chunk.choices[0].delta.content; } } if (!content) throw new Error("No streamed content"); console.log(content); ``` ```python Python Auriko stream = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], stream=True, gateway={ "routing": { "optimize": "ttft-focus", "max_ttft_ms": 1000, }, } ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` ```typescript TypeScript Auriko const stream = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], stream: true, gateway: { routing: { optimize: "ttft-focus", max_ttft_ms: 1000, }, }, }); for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { process.stdout.write(chunk.choices[0].delta.content); } } ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Hello!"}], "stream": true, "gateway": {"routing": {"optimize": "ttft-focus", "max_ttft_ms": 1000}} }' ``` --- ## Page: Streaming > Section: Handle stream errors Catch errors during streaming: ```python Python OpenAI import os from openai import OpenAI, APIStatusError client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) try: stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], stream=True ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) except APIStatusError as e: if e.status_code == 429: retry_after = e.response.headers.get("retry-after") print(f"Rate limited: retry after {retry_after}s") else: print(f"API error ({e.status_code}): {e.message}") ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); try { const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], stream: true, }); let content = ""; for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { content += chunk.choices[0].delta.content; } } console.log(content); } catch (error) { if (error instanceof OpenAI.APIError) { console.error(`API Error ${error.status}: ${error.message}`); if (error.status === 429) { console.error("Rate limited — retry after backoff"); } } else { throw error; } } ``` ```python Python Auriko import os from auriko import Client, APIStatusError, RateLimitError client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) try: stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], stream=True ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) except RateLimitError as e: print(f"Rate limited: retry after {e.retry_after_seconds}s") except APIStatusError as e: # Mid-stream upstream error (502 upstream_error, 504 upstream_timeout, 503 service_unavailable). print(f"Upstream/api error ({e.status_code}, code={e.code}): {e.message}") ``` ```typescript TypeScript Auriko import { Client, APIStatusError, RateLimitError } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); try { const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], stream: true, }); for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { process.stdout.write(chunk.choices[0].delta.content); } } } catch (e) { if (e instanceof RateLimitError) { console.log(`Rate limited: retry after ${e.retryAfterSeconds}s`); } else if (e instanceof APIStatusError) { console.log(`Upstream/api error (${e.statusCode}, code=${e.code}): ${e.message}`); } } ``` See [Error Handling Guide](/guides/error-handling) for retry strategies and circuit breakers. --- ## Page: Streaming > Section: SSE format Raw SSE events look like this. The stream ends with `usage` and `routing_metadata` events before `[DONE]`. ``` data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1234567890,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]} data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1234567890,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}]} data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1234567890,"model":"gpt-4o","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]} data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1234567890,"model":"gpt-4o","choices":[],"usage":{"prompt_tokens":8,"completion_tokens":2,"total_tokens":10}} data: {"id":"chatcmpl-a1b2c3d4","object":"chat.completion.chunk","created":1234567890,"model":"gpt-4o","choices":[],"routing_metadata":{"provider":"openai","routing_strategy":"balanced","cost":{"usd":0.00015}}} data: [DONE] ``` The trailing events before `[DONE]` carry `usage` and `routing_metadata` with `choices: []`. SDKs expose these as `stream.usage` and `stream.routing_metadata` after iteration. --- ## Page: Tool Calling Pass function schemas in your request and Auriko returns structured tool calls you can execute locally. --- ## Page: Tool Calling > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) --- ## Page: Tool Calling > Section: Define tools Define tools as JSON schemas describing the function signature: ```python tools = [ { "type": "function", "function": { "name": "get_weather", "description": "Get the current weather for a city", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "The city name" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "Temperature unit" } }, "required": ["city"] } } } ] ``` --- ## Page: Tool Calling > Section: Call tools Send a request with tools and check the response: ```python Python OpenAI import os from openai import OpenAI import json client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) tools = [ { "type": "function", "function": { "name": "get_weather", "description": "Get weather for a city", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"] } } } ] response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "What's the weather in Paris?"}], tools=tools ) if response.choices[0].message.tool_calls: tool_call = response.choices[0].message.tool_calls[0] print(f"Function: {tool_call.function.name}") print(f"Arguments: {json.loads(tool_call.function.arguments)}") ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const tools: OpenAI.ChatCompletionTool[] = [ { type: "function", function: { name: "get_weather", description: "Get weather for a city", parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"], }, }, }, ]; const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "What's the weather in Paris?" }], tools, }); if (response.choices[0].message.tool_calls) { const toolCall = response.choices[0].message.tool_calls[0]; if (toolCall.type === "function") { console.log(`Function: ${toolCall.function.name}`); console.log(`Arguments: ${toolCall.function.arguments}`); } } ``` ```python Python Auriko import os from auriko import Client import json client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) tools = [ { "type": "function", "function": { "name": "get_weather", "description": "Get weather for a city", "parameters": { "type": "object", "properties": { "city": {"type": "string"} }, "required": ["city"] } } } ] response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "What's the weather in Paris?"}], tools=tools ) if response.choices[0].message.tool_calls: tool_call = response.choices[0].message.tool_calls[0] print(f"Function: {tool_call.function.name}") print(f"Arguments: {json.loads(tool_call.function.arguments)}") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const tools = [ { type: "function" as const, function: { name: "get_weather", description: "Get weather for a city", parameters: { type: "object", properties: { city: { type: "string" }, }, required: ["city"], }, }, }, ]; const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "What's the weather in Paris?" }], tools, }); if (response.choices[0].message.tool_calls) { const toolCall = response.choices[0].message.tool_calls[0]; console.log(`Function: ${toolCall.function.name}`); console.log(`Arguments: ${toolCall.function.arguments}`); } ``` --- ## Page: Tool Calling > Section: Execute tool calls After receiving tool calls, execute them and send the results back: ```python Python OpenAI import json def get_weather(city: str) -> str: return f"Weather in {city}: 72F, sunny" response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "What's the weather in Paris?"}], tools=tools ) message = response.choices[0].message if message.tool_calls: messages = [ {"role": "user", "content": "What's the weather in Paris?"}, message.model_dump(exclude_none=True), ] for tool_call in message.tool_calls: args = json.loads(tool_call.function.arguments) if tool_call.function.name == "get_weather": result = get_weather(args["city"]) messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result }) final_response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools ) print(final_response.choices[0].message.content) ``` ```typescript TypeScript OpenAI function getWeather(city: string): string { return `Weather in ${city}: 72°F, sunny`; } const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "What's the weather in Paris?" }], tools, }); const message = response.choices[0].message; if (message.tool_calls) { const messages: OpenAI.ChatCompletionMessageParam[] = [ { role: "user", content: "What's the weather in Paris?" }, message, ]; for (const toolCall of message.tool_calls) { if (toolCall.type !== "function") continue; const args = JSON.parse(toolCall.function.arguments); let result = ""; if (toolCall.function.name === "get_weather") { result = getWeather(args.city); } messages.push({ role: "tool", tool_call_id: toolCall.id, content: result }); } const finalResponse = await client.chat.completions.create({ model: "gpt-4o", messages, tools, }); console.log(finalResponse.choices[0].message.content); } ``` ```python Python Auriko import json def get_weather(city: str) -> str: # Your actual implementation here return f"Weather in {city}: 72°F, sunny" # Step 1: Initial request response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "What's the weather in Paris?"}], tools=tools ) # Step 2: Check for tool calls message = response.choices[0].message if message.tool_calls: # Build message history messages = [ {"role": "user", "content": "What's the weather in Paris?"}, message.model_dump(exclude_none=True), # Assistant message with tool_calls ] # Execute each tool call for tool_call in message.tool_calls: args = json.loads(tool_call.function.arguments) if tool_call.function.name == "get_weather": result = get_weather(args["city"]) # Add tool result messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result }) # Step 3: Get final response final_response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools ) print(final_response.choices[0].message.content) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); function getWeather(city: string): string { return `Weather in ${city}: 72°F, sunny`; } // Step 1: Initial request const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "What's the weather in Paris?" }], tools, }); // Step 2: Check for tool calls const message = response.choices[0].message; if (message.tool_calls) { const messages: any[] = [ { role: "user", content: "What's the weather in Paris?" }, message, ]; // Execute each tool call for (const toolCall of message.tool_calls) { const args = JSON.parse(toolCall.function.arguments); let result = ""; if (toolCall.function.name === "get_weather") { result = getWeather(args.city); } messages.push({ role: "tool", tool_call_id: toolCall.id, content: result }); } // Step 3: Get final response const finalResponse = await client.chat.completions.create({ model: "gpt-4o", messages, tools, }); console.log(finalResponse.choices[0].message.content); } ``` `model_dump(exclude_none=True)` preserves all tool call fields while stripping `None` fields that some providers reject. Some providers attach a cryptographic signature to tool calls for multi-turn verification. Using `model_dump(exclude_none=True)` ensures the signature is echoed back correctly. --- ## Page: Tool Calling > Section: Use multiple tools Define multiple tools in the same request: ```python Python OpenAI tools = [ { "type": "function", "function": { "name": "get_weather", "description": "Get weather for a city", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"] } } }, { "type": "function", "function": { "name": "search_web", "description": "Search the web for information", "parameters": { "type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"] } } }, { "type": "function", "function": { "name": "send_email", "description": "Send an email", "parameters": { "type": "object", "properties": { "to": {"type": "string"}, "subject": {"type": "string"}, "body": {"type": "string"} }, "required": ["to", "subject", "body"] } } } ] ``` ```typescript TypeScript OpenAI const tools: OpenAI.ChatCompletionTool[] = [ { type: "function", function: { name: "get_weather", description: "Get weather for a city", parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"], }, }, }, { type: "function", function: { name: "search_web", description: "Search the web for information", parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"], }, }, }, { type: "function", function: { name: "send_email", description: "Send an email", parameters: { type: "object", properties: { to: { type: "string" }, subject: { type: "string" }, body: { type: "string" }, }, required: ["to", "subject", "body"], }, }, }, ]; ``` ```python Python Auriko tools = [ { "type": "function", "function": { "name": "get_weather", "description": "Get weather for a city", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"] } } }, { "type": "function", "function": { "name": "search_web", "description": "Search the web for information", "parameters": { "type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"] } } }, { "type": "function", "function": { "name": "send_email", "description": "Send an email", "parameters": { "type": "object", "properties": { "to": {"type": "string"}, "subject": {"type": "string"}, "body": {"type": "string"} }, "required": ["to", "subject", "body"] } } } ] ``` ```typescript TypeScript Auriko const tools = [ { type: "function" as const, function: { name: "get_weather", description: "Get weather for a city", parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"], }, }, }, { type: "function" as const, function: { name: "search_web", description: "Search the web for information", parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"], }, }, }, { type: "function" as const, function: { name: "send_email", description: "Send an email", parameters: { type: "object", properties: { to: { type: "string" }, subject: { type: "string" }, body: { type: "string" }, }, required: ["to", "subject", "body"], }, }, }, ]; ``` --- ## Page: Tool Calling > Section: Use parallel tool calls Models can request multiple tool calls in parallel: ```python Python OpenAI response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "What's the weather in Paris and Tokyo?"}], tools=tools ) if response.choices[0].message.tool_calls: for tool_call in response.choices[0].message.tool_calls: print(f"{tool_call.function.name}: {tool_call.function.arguments}") ``` ```typescript TypeScript OpenAI const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "What's the weather in Paris and Tokyo?" }], tools, }); if (response.choices[0].message.tool_calls) { for (const toolCall of response.choices[0].message.tool_calls) { if (toolCall.type !== "function") continue; console.log(`${toolCall.function.name}: ${toolCall.function.arguments}`); } } ``` ```python Python Auriko response = client.chat.completions.create( model="gpt-4o", messages=[{ "role": "user", "content": "What's the weather in Paris and Tokyo?" }], tools=tools ) # May return two tool calls if response.choices[0].message.tool_calls: for tool_call in response.choices[0].message.tool_calls: print(f"{tool_call.function.name}: {tool_call.function.arguments}") ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "What's the weather in Paris and Tokyo?" }], tools, }); if (response.choices[0].message.tool_calls) { for (const toolCall of response.choices[0].message.tool_calls) { console.log(`${toolCall.function.name}: ${toolCall.function.arguments}`); } } ``` --- ## Page: Tool Calling > Section: Control tool choice Control which tools the model can use: ```python Python OpenAI # Let model decide response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools, tool_choice="auto" ) # Force tool use response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools, tool_choice="required" ) # Force specific tool response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools, tool_choice={"type": "function", "function": {"name": "get_weather"}} ) # Disable tools response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools, tool_choice="none" ) ``` ```typescript TypeScript OpenAI // Let model decide const auto = await client.chat.completions.create({ model: "gpt-4o", messages, tools, tool_choice: "auto", }); // Force tool use const required = await client.chat.completions.create({ model: "gpt-4o", messages, tools, tool_choice: "required", }); // Force specific tool const specific = await client.chat.completions.create({ model: "gpt-4o", messages, tools, tool_choice: { type: "function", function: { name: "get_weather" } }, }); // Disable tools const none = await client.chat.completions.create({ model: "gpt-4o", messages, tools, tool_choice: "none", }); ``` ```python Python Auriko # Let model decide response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools, tool_choice="auto" # default ) # Force tool use response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools, tool_choice="required" ) # Force specific tool response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools, tool_choice={"type": "function", "function": {"name": "get_weather"}} ) # Disable tools response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools, tool_choice="none" ) ``` ```typescript TypeScript Auriko // Let model decide const auto = await client.chat.completions.create({ model: "gpt-4o", messages, tools, tool_choice: "auto", }); // Force tool use const required = await client.chat.completions.create({ model: "gpt-4o", messages, tools, tool_choice: "required", }); // Force specific tool const specific = await client.chat.completions.create({ model: "gpt-4o", messages, tools, tool_choice: { type: "function", function: { name: "get_weather" } }, }); // Disable tools const none = await client.chat.completions.create({ model: "gpt-4o", messages, tools, tool_choice: "none", }); ``` `tool_choice` controls whether the model calls tools: - **`"auto"`** (default): The model decides whether to call a tool based on the conversation. - **`"required"`**: The model must call at least one tool. Most providers support this; see [below](#provider-specific-behavior) for exceptions. - **`{"type": "function", "function": {"name": "..."}}`**: The model must call the specified tool. - **`"none"`**: The model won't call any tools. Auriko ensures no tool calls are produced, even on providers that don't respect `"none"`. These requests can route to more providers, improving availability. --- ## Page: Tool Calling > Section: Provider-specific behavior ### `tool_choice` with reasoning models Some providers activate reasoning by default for certain models. When reasoning is active, these providers don't fully support `tool_choice="required"` or named `tool_choice`. Auriko handles this automatically: | Provider | Affected models | Behavior | |----------|----------------|----------| | DeepSeek | `deepseek-v4-flash`, `deepseek-v4-pro` | Auriko routes `"required"` requests to other providers that honor the constraint. Use `tool_choice: "auto"` directly to avoid routing constraints. | | Moonshot | `kimi-k2.5`, `kimi-k2.6` | Auriko suppresses default thinking to honor `tool_choice="required"`. If you explicitly enable thinking (`reasoning_effort` > `"off"`), Auriko routes to alternative providers; if none are available, you receive a routing error. | These models still call tools reliably with `tool_choice="auto"`. The constraint only affects forcing tool use. See [Extensions and Thinking](/guides/extensions-and-thinking) for details on `reasoning_effort` and how Auriko translates it per provider. These constraints originate from the providers' APIs. Third-party hosts (e.g., Fireworks) serving the same model weights typically don't have this restriction. ### `tool_choice="required"` provider support Auriko filters out providers known not to honor `tool_choice="required"` when routing these requests. | Provider | Affected models | Reason | |----------|----------------|--------| | DeepSeek | `deepseek-v4-flash`, `deepseek-v4-pro` | Reasoning models reject `"required"` with a 400 error | | MiniMax | All models | Models silently ignore `"required"` and behave as `"auto"` | | SiliconFlow | All models | Models return malformed tool calls or errors with `"required"` on SiliconFlow | | Z.AI | All models | Models ignore the `"required"` constraint and may respond with text | | DeepInfra | `llama-3.1` | Model returns text instead of honoring the `"required"` constraint | | DeepInfra | `qwen-3.5` | Model rejects `"required"` with a 400 error | | DeepInfra | GLM family (`glm-4.6`, `glm-4.7`, `glm-4.7-flash`, `glm-5`, `glm-5.1`) | Models ignore the `"required"` constraint and may respond with text | | Together AI | `glm-5.1` | Model ignores the `"required"` constraint and may respond with text | If no capable provider is available for the requested model, the API returns a `tool_choice_required_not_supported` error. To resolve this: - Use `tool_choice: "auto"`. Models still call tools when prompted appropriately. - Remove the provider constraint to allow routing to a capable provider. With `tool_choice: "auto"`, models still call tools when the prompt makes it appropriate. The model isn't forced to call a tool and may respond with text instead. In practice, well-prompted requests still produce tool calls reliably. Named `tool_choice` (`{type: "function", function: {name: "..."}}`) isn't affected by this filtering. Only the string value `"required"` triggers provider filtering. If you need guaranteed forced tool use, exclude affected providers using [`exclude_providers`](/guides/routing-options#prefer-or-exclude-providers) to route to a provider that fully supports `tool_choice: "required"`. --- ## Page: Tool Calling > Section: Stream tool calls Reassemble streamed tool call chunks into complete function calls: ```python Python OpenAI stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "What's the weather in Paris?"}], tools=tools, stream=True ) tool_calls = {} for chunk in stream: if not chunk.choices: continue delta = chunk.choices[0].delta if delta.tool_calls: for tc in delta.tool_calls: idx = tc.index if idx not in tool_calls: tool_calls[idx] = {"id": tc.id, "function": {"name": "", "arguments": ""}} if tc.function and tc.function.name: tool_calls[idx]["function"]["name"] += tc.function.name if tc.function and tc.function.arguments: tool_calls[idx]["function"]["arguments"] += tc.function.arguments print(list(tool_calls.values())) ``` ```typescript TypeScript OpenAI const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "What's the weather in Paris?" }], tools, stream: true, }); const toolCalls: Record = {}; for await (const chunk of stream) { if (!chunk.choices.length) continue; const delta = chunk.choices[0].delta; if (delta.tool_calls) { for (const tc of delta.tool_calls) { if (!(tc.index in toolCalls)) { toolCalls[tc.index] = { id: tc.id!, function: { name: "", arguments: "" } }; } if (tc.function?.name) toolCalls[tc.index].function.name += tc.function.name; if (tc.function?.arguments) toolCalls[tc.index].function.arguments += tc.function.arguments; } } } console.log(Object.values(toolCalls)); ``` ```python Python Auriko stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "What's the weather in Paris?"}], tools=tools, stream=True ) tool_calls = {} for chunk in stream: if not chunk.choices: continue delta = chunk.choices[0].delta if delta.tool_calls: for tc in delta.tool_calls: idx = tc.index if idx not in tool_calls: tool_calls[idx] = {"id": tc.id, "function": {"name": "", "arguments": ""}} if tc.function and tc.function.name: tool_calls[idx]["function"]["name"] += tc.function.name if tc.function and tc.function.arguments: tool_calls[idx]["function"]["arguments"] += tc.function.arguments print(list(tool_calls.values())) ``` ```typescript TypeScript Auriko const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "What's the weather in Paris?" }], tools, stream: true, }); const toolCalls: Record = {}; for await (const chunk of stream) { if (!chunk.choices.length) continue; const delta = chunk.choices[0].delta; if (delta.tool_calls) { for (const tc of delta.tool_calls) { if (!(tc.index in toolCalls)) { toolCalls[tc.index] = { id: tc.id!, function: { name: "", arguments: "" } }; } if (tc.function?.name) toolCalls[tc.index].function.name += tc.function.name; if (tc.function?.arguments) toolCalls[tc.index].function.arguments += tc.function.arguments; } } } console.log(Object.values(toolCalls)); ``` See [Streaming Guide](/guides/streaming#stream-with-tools) for full streaming patterns including error handling and metadata access. --- ## Page: Tool Calling > Section: Convert legacy functions Auriko auto-converts the deprecated `functions`/`function_call` parameters to the modern `tools`/`tool_choice` format: | Legacy parameter | Converted to | Condition | |-----------------|-------------|-----------| | `functions` | `tools` | Only if `tools` is absent | | `function_call: "auto"` | `tool_choice: "auto"` | Only if `tool_choice` is absent | | `function_call: "none"` | `tool_choice: "none"` | Only if `tool_choice` is absent | | `function_call: {name: "fn"}` | `tool_choice: {type: "function", function: {name: "fn"}}` | Only if `tool_choice` is absent | Conversion only runs when the legacy field is present and the modern field is absent. If both are present, the modern field takes precedence. Use `tools`/`tool_choice` for new code. Auriko supports the legacy format for backward compatibility. Auriko also normalizes legacy message formats in chat history: | Legacy message format | Converted to | Notes | |----------------------|-------------|-------| | `role: "function"` message | `role: "tool"` message | `name` replaced with synthesized `tool_call_id` | | `assistant.function_call` | `assistant.tool_calls` entry | Original `function_call` field removed | Your existing chat histories with legacy function messages work without changes. Most providers support tool calling, but subfeatures like `parallel_tool_calls` vary. Check `/v1/directory/models` for current capability details. --- ## Page: Tool Calling > Section: Best practices Write clear, specific function descriptions so the model knows when to use them. Always validate tool call arguments before executing. Return helpful error messages in tool results when execution fails. Only include relevant tools to reduce confusion and latency. --- ## Page: Structured Output You can force models to return valid JSON, optionally conforming to a specific schema. --- ## Page: Structured Output > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) --- ## Page: Structured Output > Section: Choose a response format Auriko supports three response format types: | Type | Description | Use case | |------|-------------|----------| | `text` | Default. Model returns plain text. | General chat, creative writing | | `json_object` | Model returns valid JSON. No schema enforcement. | Flexible JSON extraction | | `json_schema` | Model returns JSON matching a provided schema. | Typed data extraction, API responses | `json_schema` and `json_object` are separate capabilities. `json_schema` has broader model support. Most **Claude** models support `json_schema` but not `json_object`. Claude Sonnet 4 and Opus 4 don't support either mode. If you request an unsupported mode, Auriko returns a `400` with a suggested alternative. Check per-model support on the [models page](https://www.auriko.ai/models) or via the [Model directory](/api-reference/model-directory). `json_schema` appears as **Structured Output**, `json_object` as **JSON Mode**. --- ## Page: Structured Output > Section: Return JSON To return any JSON output, set `response_format` to `json_object`: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) response = client.chat.completions.create( model="gpt-4o", messages=[ {"role": "system", "content": "Extract the user's name and age as JSON."}, {"role": "user", "content": "I'm Alice and I'm 30 years old."}, ], response_format={"type": "json_object"}, ) print(response.choices[0].message.content) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [ { role: "system", content: "Extract the user's name and age as JSON." }, { role: "user", content: "I'm Alice and I'm 30 years old." }, ], response_format: { type: "json_object" }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-4o", messages=[ {"role": "system", "content": "Extract the user's name and age as JSON."}, {"role": "user", "content": "I'm Alice and I'm 30 years old."} ], response_format={"type": "json_object"} ) print(response.choices[0].message.content) # {"name": "Alice", "age": 30} ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [ { role: "system", content: "Extract the user's name and age as JSON." }, { role: "user", content: "I'm Alice and I'm 30 years old." }, ], response_format: { type: "json_object" }, }); console.log(response.choices[0].message.content); // {"name": "Alice", "age": 30} ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [ {"role": "system", "content": "Extract the user'\''s name and age as JSON."}, {"role": "user", "content": "I'\''m Alice and I'\''m 30 years old."} ], "response_format": {"type": "json_object"} }' ``` The model returns valid JSON, but the structure isn't guaranteed. For strict schema conformance, use `json_schema` instead. When using `json_object` mode, always include the word "JSON" in your system or user message. Some providers require this and return a 400 error without it. Including it is harmless on providers that don't enforce it. The `json_schema` mode does not have this requirement. The examples above include "JSON" in the system message. --- ## Page: Structured Output > Section: Enforce schema You can enforce a specific JSON structure by providing a schema: ```python Python OpenAI import os import json from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) response = client.chat.completions.create( model="gpt-4o", messages=[ {"role": "user", "content": "Extract: Alice is 30, lives in NYC, alice@example.com"}, ], response_format={ "type": "json_schema", "json_schema": { "name": "ContactInfo", "schema": { "type": "object", "properties": { "name": {"type": "string"}, "age": {"type": "integer"}, "city": {"type": "string"}, "email": {"type": "string"}, }, "required": ["name", "age", "city", "email"], }, }, }, ) contact = json.loads(response.choices[0].message.content) print(contact["name"]) print(contact["email"]) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [ { role: "user", content: "Extract: Alice is 30, lives in NYC, alice@example.com" }, ], response_format: { type: "json_schema", json_schema: { name: "ContactInfo", schema: { type: "object", properties: { name: { type: "string" }, age: { type: "integer" }, city: { type: "string" }, email: { type: "string" }, }, required: ["name", "age", "city", "email"], }, }, }, }); const contact = JSON.parse(response.choices[0].message.content!); console.log(contact.name); console.log(contact.email); ``` ```python Python Auriko import os import json from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-4o", messages=[ {"role": "user", "content": "Extract: Alice is 30, lives in NYC, alice@example.com"} ], response_format={ "type": "json_schema", "json_schema": { "name": "ContactInfo", "schema": { "type": "object", "properties": { "name": {"type": "string"}, "age": {"type": "integer"}, "city": {"type": "string"}, "email": {"type": "string"} }, "required": ["name", "age", "city", "email"] } } } ) contact = json.loads(response.choices[0].message.content) print(contact["name"]) # Alice print(contact["email"]) # alice@example.com ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [ { role: "user", content: "Extract: Alice is 30, lives in NYC, alice@example.com" }, ], response_format: { type: "json_schema", json_schema: { name: "ContactInfo", schema: { type: "object", properties: { name: { type: "string" }, age: { type: "integer" }, city: { type: "string" }, email: { type: "string" }, }, required: ["name", "age", "city", "email"], }, }, }, }); const contact = JSON.parse(response.choices[0].message.content!); console.log(contact.name); // Alice console.log(contact.email); // alice@example.com ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [ {"role": "user", "content": "Extract: Alice is 30, lives in NYC, alice@example.com"} ], "response_format": { "type": "json_schema", "json_schema": { "name": "ContactInfo", "schema": { "type": "object", "properties": { "name": {"type": "string"}, "age": {"type": "integer"}, "city": {"type": "string"}, "email": {"type": "string"} }, "required": ["name", "age", "city", "email"] } } } }' ``` The `json_schema` object requires a `name` field. The `schema` field accepts a standard JSON Schema definition. Auriko automatically routes to providers that support your requested format. If no provider supports it, you get a clear error with suggestions. --- ## Page: Structured Output > Section: Resources Call functions from LLM responses Optimize for cost, latency, or throughput Handle errors and retries See which models support structured output and JSON mode --- ## Page: Vision Auriko supports vision through the OpenAI-compatible `image_url` content part. Pass an image URL or a base64 data URL in the `content` array of a user message. --- ## Page: Vision > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) - A vision-capable model (e.g., `gpt-4o`, `claude-sonnet-4-6`, `gemini-flash-latest`) --- ## Page: Vision > Section: Analyze images from URLs Pass an image URL as a content part in the user message: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) response = client.chat.completions.create( model="gpt-4o", messages=[{ "role": "user", "content": [ {"type": "text", "text": "What is in this image?"}, {"type": "image_url", "image_url": { "url": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png" }}, ], }], max_tokens=300, ) print(response.choices[0].message.content) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: [ { type: "text", text: "What is in this image?" }, { type: "image_url", image_url: { url: "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png", } }, ], }], max_tokens: 300, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) response = client.chat.completions.create( model="gpt-4o", messages=[ { "role": "user", "content": [ {"type": "text", "text": "What is in this image?"}, { "type": "image_url", "image_url": { "url": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png" }, }, ], } ], max_tokens=300, ) print(response.choices[0].message.content) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [ { role: "user", content: [ { type: "text", text: "What is in this image?" }, { type: "image_url", image_url: { url: "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png", }, }, ], }, ], max_tokens: 300, }); console.log(response.choices[0].message.content); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{ "role": "user", "content": [ {"type": "text", "text": "What is in this image?"}, {"type": "image_url", "image_url": {"url": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"}} ] }], "max_tokens": 300 }' ``` --- ## Page: Vision > Section: Analyze base64-encoded images For local files or private images, encode the bytes as a data URL: ```python Python OpenAI import base64 import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) with open("chart.png", "rb") as f: b64 = base64.b64encode(f.read()).decode("utf-8") response = client.chat.completions.create( model="gpt-4o", messages=[{ "role": "user", "content": [ {"type": "text", "text": "Summarize the trend in this chart."}, {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}}, ], }], max_tokens=500, ) print(response.choices[0].message.content) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; import fs from "node:fs"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const b64 = fs.readFileSync("chart.png").toString("base64"); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: [ { type: "text", text: "Summarize the trend in this chart." }, { type: "image_url", image_url: { url: `data:image/png;base64,${b64}` } }, ], }], max_tokens: 500, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import base64 import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) with open("chart.png", "rb") as f: b64 = base64.b64encode(f.read()).decode("utf-8") response = client.chat.completions.create( model="gpt-4o", messages=[ { "role": "user", "content": [ {"type": "text", "text": "Summarize the trend in this chart."}, { "type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}, }, ], } ], max_tokens=500, ) print(response.choices[0].message.content) ``` ```typescript TypeScript Auriko import fs from "node:fs"; import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const b64 = fs.readFileSync("chart.png").toString("base64"); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [ { role: "user", content: [ { type: "text", text: "Summarize the trend in this chart." }, { type: "image_url", image_url: { url: `data:image/png;base64,${b64}` }, }, ], }, ], max_tokens: 500, }); console.log(response.choices[0].message.content); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{ "role": "user", "content": [ {"type": "text", "text": "What is in this image?"}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC"}} ] }], "max_tokens": 300 }' ``` --- ## Page: Vision > Section: Send multiple images Send several images in a single request by adding multiple `image_url` content parts: ```python Python OpenAI messages=[{ "role": "user", "content": [ {"type": "text", "text": "Compare these two images."}, {"type": "image_url", "image_url": {"url": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"}}, {"type": "image_url", "image_url": {"url": "https://upload.wikimedia.org/wikipedia/commons/a/a7/Camponotus_flavomarginatus_ant.jpg"}}, ], }] ``` ```typescript TypeScript OpenAI const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: [ { type: "text", text: "Compare these two images." }, { type: "image_url", image_url: { url: "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png" } }, { type: "image_url", image_url: { url: "https://upload.wikimedia.org/wikipedia/commons/a/a7/Camponotus_flavomarginatus_ant.jpg" } }, ], }], max_tokens: 300, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko messages=[ { "role": "user", "content": [ {"type": "text", "text": "Compare these two images."}, {"type": "image_url", "image_url": {"url": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"}}, {"type": "image_url", "image_url": {"url": "https://upload.wikimedia.org/wikipedia/commons/a/a7/Camponotus_flavomarginatus_ant.jpg"}}, ], } ] ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: [ { type: "text", text: "Compare these two images." }, { type: "image_url", image_url: { url: "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png" } }, { type: "image_url", image_url: { url: "https://upload.wikimedia.org/wikipedia/commons/a/a7/Camponotus_flavomarginatus_ant.jpg" } }, ], }], max_tokens: 300, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{ "role": "user", "content": [ {"type": "text", "text": "Compare these two images."}, {"type": "image_url", "image_url": {"url": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"}}, {"type": "image_url", "image_url": {"url": "https://upload.wikimedia.org/wikipedia/commons/a/a7/Camponotus_flavomarginatus_ant.jpg"}} ] }], "max_tokens": 300 }' ``` --- ## Page: Vision > Section: Control image resolution You can set `detail` on the `image_url` content part to control how much resolution the model uses: ```python Python OpenAI { "type": "image_url", "image_url": { "url": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png", "detail": "low", }, } ``` ```typescript TypeScript OpenAI const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: [ { type: "text", text: "Describe this image." }, { type: "image_url", image_url: { url: "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png", detail: "low", }, }, ], }], max_tokens: 300, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko { "type": "image_url", "image_url": { "url": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png", "detail": "low", }, } ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: [ { type: "text", text: "Describe this image." }, { type: "image_url", image_url: { url: "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png", detail: "low", }, }, ], }], max_tokens: 300, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{ "role": "user", "content": [ {"type": "text", "text": "Describe this image."}, {"type": "image_url", "image_url": {"url": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png", "detail": "low"}} ] }], "max_tokens": 300 }' ``` | Value | Behavior | |-------|----------| | `auto` | The model decides based on image size (default) | | `low` | Fixed low-resolution processing, fewer tokens | | `high` | High-resolution processing, more tokens for fine detail | Use `low` for cost-sensitive workloads where fine detail isn't needed. Use `high` when the model needs to read small text or distinguish fine visual features. --- ## Page: Vision > Section: Response shape Vision responses use the standard `ChatCompletionResponse` shape. The model's analysis appears in `choices[0].message.content` as text. --- ## Page: Vision > Section: Errors | Situation | HTTP | SDK error | |---|---|---| | Image too large for the model's context window | `400` | `BadRequestError` | | Model doesn't support vision | `400` | `BadRequestError` | Some models accept image URLs directly. Others require Auriko to process the image first, which adds these constraints: | Situation | HTTP | SDK error | |---|---|---| | Image URL unreachable | `400` | `BadRequestError` | | Total image data exceeds 30 MB | `400` | `BadRequestError` | | More than 1,500 images in one request | `400` | `BadRequestError` | | Image URL isn't HTTPS | `400` | `BadRequestError` | | Unsupported image format | `400` | `BadRequestError` | URL resolution behavior varies by model. For consistent results across models, use base64-encoded images. Check [Supported parameters](/contract/supported-parameters) for the accepted `content` part types and see [Error codes](/contract/error-codes) for the full error taxonomy. --- ## Page: Vision > Section: Related - [Streaming](/guides/streaming) — stream vision responses chunk-by-chunk - [Tool calling](/guides/tool-calling) — combine vision with function calling - [Structured output](/guides/structured-output) — extract structured data from images - [Image generation](/guides/image-generation) — generate images with Gemini models --- ## Page: Image generation Send a text prompt to an image-capable model and receive base64-encoded images in `choices[0].message.images`. --- ## Page: Image generation > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) - An image-capable model: `gemini-2.5-flash-image`, `gemini-3-pro-image`, or `gemini-3.1-flash-image` --- ## Page: Image generation > Section: Generate images Create a chat completion with an image-capable model and save the generated image: ```python Python OpenAI import os import base64 from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) response = client.chat.completions.create( model="gemini-2.5-flash-image", messages=[{ "role": "user", "content": "Draw a cartoon cat wearing a top hat", }], ) image = response.choices[0].message.images[0] image_bytes = base64.b64decode(image["data"]) with open("output.png", "wb") as f: f.write(image_bytes) print(f"Saved {image['mime_type']} image ({len(image_bytes)} bytes)") ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; import { writeFileSync } from "fs"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gemini-2.5-flash-image", messages: [{ role: "user", content: "Draw a cartoon cat wearing a top hat", }], }); const image = response.choices[0].message.images[0]; const imageBytes = Buffer.from(image.data, "base64"); writeFileSync("output.png", imageBytes); console.log(`Saved ${image.mime_type} image (${imageBytes.length} bytes)`); ``` ```python Python Auriko import os import base64 from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) response = client.chat.completions.create( model="gemini-2.5-flash-image", messages=[{ "role": "user", "content": "Draw a cartoon cat wearing a top hat", }], ) image = response.choices[0].message.images[0] image_bytes = base64.b64decode(image["data"]) with open("output.png", "wb") as f: f.write(image_bytes) print(f"Saved {image['mime_type']} image ({len(image_bytes)} bytes)") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; import { writeFileSync } from "fs"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gemini-2.5-flash-image", messages: [{ role: "user", content: "Draw a cartoon cat wearing a top hat", }], }); const image = response.choices[0].message.images[0]; const imageBytes = Buffer.from(image.data, "base64"); writeFileSync("output.png", imageBytes); console.log(`Saved ${image.mime_type} image (${imageBytes.length} bytes)`); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gemini-2.5-flash-image", "messages": [{ "role": "user", "content": "Draw a cartoon cat wearing a top hat" }] }' | jq -r '.choices[0].message.images[0].data' | base64 --decode > output.png ``` `image_tokens` in `usage.completion_tokens_details` reports tokens consumed by generated images. The `images` field is absent when no images are generated. In streaming responses, images arrive complete in a single `delta` chunk. --- ## Page: Image generation > Section: Response shape Each entry in the `images` array is a `GeneratedImage` object: | Field | Type | Description | |-------|------|-------------| | `type` | `string` | Always `"image"` | | `mime_type` | `string` | MIME type (e.g., `image/png`) | | `data` | `string` | Base64-encoded image data | | `thought_signature` | `string?` | Opaque model signature. Response-only | --- ## Page: Image generation > Section: Related - [Vision](/guides/vision) — analyze images in chat completions - [Streaming](/guides/streaming) — stream image responses chunk-by-chunk - [Extensions and thinking](/guides/extensions-and-thinking) — pass provider-specific parameters --- ## Page: Routing Options Auriko selects a provider for each request based on your routing configuration. Pass routing options in the `gateway.routing` object to control strategy, constraints, and provider filtering. --- ## Page: Routing Options > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) --- ## Page: Routing Options > Section: Compare strategies Auriko supports seven optimization strategies: | Strategy | Description | Best For | |----------|-------------|----------| | `cost` | Cost-optimized, well-rounded | Cost-conscious production, budget-sensitive apps | | `cost-focus` (default) | Aggressively minimize cost | Maximum cost savings, no latency requirements | | `ttft` | TTFT-optimized, well-rounded | Streaming UX, interactive apps | | `ttft-focus` | Aggressively minimize time to first token | Real-time applications, chatbots | | `tps` | Throughput-optimized, well-rounded | High-volume processing | | `tps-focus` | Aggressively maximize tokens per second | Maximum throughput, pipeline processing | | `balanced` | All dimensions weighted evenly | General-purpose, mixed workloads | ### Base vs. focus Base strategies (`cost`, `ttft`, `tps`) optimize for the named dimension while still considering other quality factors. Focus strategies (`cost-focus`, `ttft-focus`, `tps-focus`) optimize almost entirely for the named dimension. Other factors have minimal influence. | Type | Behavior | Use when | |------|----------|----------| | Base (`cost`, `ttft`, `tps`) | Favors the named dimension, well-rounded | Production workloads needing reliable performance | | Focus (`cost-focus`, `ttft-focus`, `tps-focus`) | Aggressively optimizes the named dimension | Batch processing, real-time streaming UI, high-throughput pipelines | For custom weight configurations beyond the preset strategies, see [Set custom weights](/guides/advanced-routing#set-custom-weights). --- ## Page: Routing Options > Section: Optimize for cost Auriko computes the expected cost of each request at every available provider and routes to the cheapest one. The cost model accounts for caching and pricing tiers. See [Cost optimization](/guides/cost-optimization) for configuration, code examples, and the full cost model. --- ## Page: Routing Options > Section: Optimize for latency Route requests to low-latency providers: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Quick answer: 2+2?"}], extra_body={"gateway": {"routing": {"optimize": "ttft-focus"}}} ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Quick answer: 2+2?" }], gateway: { routing: { optimize: "ttft-focus" } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Quick answer: 2+2?"}], gateway={ "routing": { "optimize": "ttft-focus", }, } ) print(f"Provider: {response.routing_metadata.provider}") ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Quick answer: 2+2?" }], gateway: { routing: { optimize: "ttft-focus", }, }, }); console.log(`Provider: ${response.routing_metadata?.provider}`); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Quick answer: 2+2?"}], "gateway": {"routing": {"optimize": "ttft-focus"}} }' ``` --- ## Page: Routing Options > Section: Set latency constraints Set maximum time-to-first-token (TTFT): ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": { "optimize": "cost", "max_ttft_ms": 1000, "ttft_percentile": "p50", }}} ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "cost", max_ttft_ms: 1000, ttft_percentile: "p50", } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], gateway={ "routing": { "optimize": "cost", "max_ttft_ms": 1000, # Must start responding within 1s "ttft_percentile": "p50", }, } ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "cost", max_ttft_ms: 1000, // Must start responding within 1s ttft_percentile: "p50", }, }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"optimize": "cost", "max_ttft_ms": 1000, "ttft_percentile": "p50"}} }' ``` If no provider can meet the latency constraint, Auriko returns a 400 error. `max_ttft_ms` evaluates against median (p50) metrics by default. To constrain on worst-case latency, set `ttft_percentile` to `"p95"`. See [Choose metric percentile](/guides/advanced-routing#choose-metric-percentile). --- ## Page: Routing Options > Section: Set cost ceilings Exclude providers that exceed a per-1M-token budget: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": { "optimize": "cost", "max_cost_per_1m": 10.00, }}} ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "cost", max_cost_per_1m: 10.00, } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], gateway={ "routing": { "optimize": "cost", "max_cost_per_1m": 10.00, # Max $10 per 1M tokens (average of input + output) }, } ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "cost", max_cost_per_1m: 10.0, // Max $10 per 1M tokens (average of input + output) }, }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"optimize": "cost", "max_cost_per_1m": 10.00}} }' ``` Auriko calculates cost as the average of input and output price per 1M tokens. Providers exceeding this ceiling are excluded from routing. For fine-grained constraints, see [Advanced routing](/guides/advanced-routing) and [Cost optimization](/guides/cost-optimization#set-cost-ceilings). --- ## Page: Routing Options > Section: Require supported parameters Set `require_parameters` to `true` to only route to providers that accept all optional parameters you sent (like `seed`, `logit_bias`, or `top_logprobs`). Without this flag, Auriko drops unsupported parameters and adds a warning to the response. ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], seed=42, extra_body={"gateway": {"routing": { "optimize": "cost", "require_parameters": True, }}} ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], seed: 42, gateway: { routing: { optimize: "cost", require_parameters: true, } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], seed=42, gateway={ "routing": { "optimize": "cost", "require_parameters": True, }, } ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], seed: 42, gateway: { routing: { optimize: "cost", require_parameters: true, }, }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}], "seed": 42, "gateway": {"routing": {"optimize": "cost", "require_parameters": true}} }' ``` If no provider supports the parameters you sent, Auriko returns a 400 error with code `required_params_not_supported`. See [Filter by parameter support](/guides/advanced-routing#filter-by-parameter-support) for the full list of parameters this applies to. --- ## Page: Routing Options > Section: Prefer or exclude providers Prefer or exclude specific providers: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) # Only consider these providers response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": {"providers": ["openai", "anthropic"]}}} ) # Exclude providers response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": {"exclude_providers": ["deepseek"]}}} ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); // Only consider these providers const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { providers: ["openai", "anthropic"] } }, }); // Exclude providers const response2 = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { exclude_providers: ["deepseek"] } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko # Only consider these providers response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], gateway={ "routing": { "providers": ["openai", "anthropic"], }, } ) # Exclude providers response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], gateway={ "routing": { "exclude_providers": ["deepseek"], }, } ) ``` ```typescript TypeScript Auriko // Only consider these providers const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { providers: ["openai", "anthropic"], }, }, }); // Exclude providers const response2 = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { exclude_providers: ["deepseek"], }, }, }); ``` ```bash cURL # Only consider these providers curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"providers": ["openai", "anthropic"]}} }' # Exclude providers curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"exclude_providers": ["deepseek"]}} }' ``` You can hint at a preferred provider without restricting the candidate pool: ```python Python OpenAI response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": {"prefer": "openai"}}} ) ``` ```typescript TypeScript OpenAI const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { prefer: "openai" } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], gateway={ "routing": { "prefer": "openai", }, } ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { prefer: "openai", }, }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"prefer": "openai"}} }' ``` `prefer` is a soft hint. If the preferred provider is available, Auriko routes to it. If not, routing proceeds normally. Unlike `providers`, a `prefer` miss doesn't fail the request. --- ## Page: Routing Options > Section: Restrict key source Force requests to use only BYOK (bring-your-own-key) or only platform-managed keys: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) # Use only your own provider keys response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": {"only_byok": True}}} ) # Use only Auriko platform keys response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": {"only_platform": True}}} ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); // Use only your own provider keys const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { only_byok: true } }, }); // Use only Auriko platform keys const response2 = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { only_platform: true } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) # Use only your own provider keys response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], gateway={ "routing": { "only_byok": True, }, } ) # Use only Auriko platform keys response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], gateway={ "routing": { "only_platform": True, }, } ) ``` ```typescript TypeScript Auriko // Use only your own provider keys const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { only_byok: true, }, }, }); // Use only Auriko platform keys const response2 = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { only_platform: true, }, }, }); ``` ```bash cURL # Use only your own provider keys curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"only_byok": true}} }' # Use only Auriko platform keys curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"only_platform": true}} }' ``` Both are booleans, default `false`. Setting both to `true` returns a 400 error. They're mutually exclusive. When no key of the requested type is available, the request fails with no fallback. See [Bring Your Own Key](/platform/byok) for BYOK setup. --- ## Page: Routing Options > Section: Opt in to premium tiers Premium-tier offerings are excluded from routing by default to prevent accidental cost escalation. Set `tier` to opt in. | Value | Effect | |-------|--------| | `"priority"` | Includes Anthropic Fast Mode offerings (2.5x speed, 6x cost) | | omitted (default) | Excludes premium-tier offerings | ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="claude-opus-4-6", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": {"tier": "priority"}}} ) ``` ```typescript TypeScript OpenAI const response = await client.chat.completions.create({ model: "claude-opus-4-6", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { tier: "priority" } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="claude-opus-4-6", messages=[{"role": "user", "content": "Hello!"}], gateway={ "routing": { "tier": "priority", }, } ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "claude-opus-4-6", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { tier: "priority", }, }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-opus-4-6", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"tier": "priority"}} }' ``` Auriko's "priority" tier refers to Anthropic Fast Mode, not Anthropic's separate Priority Tier (committed capacity SLA). Without `tier`, requests to models available only under a premium tier return [`tier_opt_in_required`](/errors/tier_opt_in_required). --- ## Page: Routing Options > Section: Read routing metadata Every response carries routing information: ```python Python Auriko response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) metadata = response.routing_metadata print(f"Provider: {metadata.provider}") print(f"Model: {metadata.provider_model_id}") print(f"Strategy: {metadata.routing_strategy}") print(f"Cost: ${metadata.cost.usd:.6f}") ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], }); const metadata = response.routing_metadata; console.log(`Provider: ${metadata?.provider}`); console.log(`Model: ${metadata?.provider_model_id}`); console.log(`Strategy: ${metadata?.routing_strategy}`); console.log(`Cost: $${metadata?.cost?.usd}`); ``` For routing metadata with the OpenAI SDK, see [OpenAI Compatibility](/openai-compatibility#access-routing-metadata). For the complete field reference including fallback chain, warnings, and all optional fields, see [Response Extensions](/api-reference/overview#response-extensions). --- ## Page: Routing Options > Section: Combine routing options ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-5.4", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What's the capital of France?"} ], extra_body={"gateway": {"routing": { "optimize": "ttft-focus", "max_ttft_ms": 1000, }}} ) print(response.choices[0].message.content) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: "What's the capital of France?" }, ], gateway: { routing: { optimize: "ttft-focus", max_ttft_ms: 1000, } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) # For a chatbot: optimize TTFT with cost ceiling response = client.chat.completions.create( model="gpt-5.4", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What's the capital of France?"} ], gateway={ "routing": { "optimize": "ttft-focus", "max_ttft_ms": 1000, }, } ) print(response.choices[0].message.content) print(f"\n--- Routing Info ---") print(f"Provider: {response.routing_metadata.provider}") print(f"Strategy: {response.routing_metadata.routing_strategy}") print(f"Cost: ${response.routing_metadata.cost.usd:.6f}") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: "What's the capital of France?" }, ], gateway: { routing: { optimize: "ttft-focus", max_ttft_ms: 1000, }, }, }); console.log(response.choices[0].message.content); console.log(`\n--- Routing Info ---`); console.log(`Provider: ${response.routing_metadata?.provider}`); console.log(`Strategy: ${response.routing_metadata?.routing_strategy}`); console.log(`Cost: $${response.routing_metadata?.cost?.usd}`); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What'\''s the capital of France?"} ], "gateway": {"routing": {"optimize": "ttft-focus", "max_ttft_ms": 1000}} }' ``` --- ## Page: Cost Optimization Auriko's proprietary cost model computes the *expected cost* (the predicted cost accounting for caching, pricing tiers, and your usage patterns) of each request at every available provider and routes to the cheapest one. --- ## Page: Cost Optimization > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) --- ## Page: Cost Optimization > Section: Enable cost optimization To route by cost, set `optimize` to `"cost"`: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": {"optimize": "cost"}}} ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "cost" } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], gateway={ "routing": { "optimize": "cost", }, } ) # See the actual cost print(f"Cost: ${response.routing_metadata.cost.usd:.6f}") print(f"Provider: {response.routing_metadata.provider}") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "cost", }, }, }); console.log(`Provider: ${response.routing_metadata?.provider}`); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"optimize": "cost"}} }' ``` --- ## Page: Cost Optimization > Section: Understand the cost model Pricing page rates show what a cached token costs if it gets cached. They don't tell you which tokens get cached or under what conditions. Two providers quoting identical rates can produce different bills on the same workload. Auriko maintains a proprietary data pipeline and cost model that tracks provider-side caching mechanics, estimates your usage patterns, and predicts the expected cost of each request at every available provider. ### Provider tracking Auriko's data pipeline tracks each provider's caching mechanics: discount depths, minimum token thresholds, block granularity, write costs, expiration windows, and pricing tiers that shift with context length. This data updates as providers change infrastructure. ### Usage estimation Auriko estimates request-level variables from your usage patterns: prefix length, reuse frequency, request timing, conversation depth, and output volume. This predicts how each provider's caching performs for your specific traffic. Auriko is a zero data retention proxy. Pattern estimation uses usage metadata only. Read the [Privacy Policy](https://www.auriko.ai/privacy) for details. ### Per-request cost prediction For each request, the cost model combines provider data and usage estimates to compute the expected cost at every available provider. It routes to the cheapest one. This is a per-request decision, not a static ranking. A provider with higher list prices can be cheaper over a multi-turn conversation if its caching mechanics produce more cache hits for your workload. Cached tokens cost less than uncached tokens. Cache reads cost less than regular input, but writing to cache can cost more. The cost model accounts for these differences. --- ## Page: Cost Optimization > Section: Set latency constraints To optimize for cost while enforcing a latency ceiling, add `max_ttft_ms`: ```python Python OpenAI response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": { "optimize": "cost", "max_ttft_ms": 1000, }}} ) ``` ```typescript TypeScript OpenAI const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "cost", max_ttft_ms: 1000, } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], gateway={ "routing": { "optimize": "cost", "max_ttft_ms": 1000, # Max 1s to first token }, } ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "cost", max_ttft_ms: 1000, // Max 1s to first token }, }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"optimize": "cost", "max_ttft_ms": 1000}} }' ``` --- ## Page: Cost Optimization > Section: Maximize savings with cost-focus `cost-focus` aggressively minimizes cost with minimal weight on other factors: ```python Python OpenAI response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Summarize this document..."}], extra_body={"gateway": {"routing": {"optimize": "cost-focus"}}} ) ``` ```typescript TypeScript OpenAI const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Summarize this document..." }], gateway: { routing: { optimize: "cost-focus" } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Summarize this document..."}], gateway={ "routing": { "optimize": "cost-focus", }, } ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Summarize this document..." }], gateway: { routing: { optimize: "cost-focus", }, }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Summarize this document..."}], "gateway": {"routing": {"optimize": "cost-focus"}} }' ``` You can also use the suffix shortcut: ```python response = client.chat.completions.create( model="gpt-5.4:cost-focus", messages=[{"role": "user", "content": "Summarize this document..."}] ) ``` | Strategy | Behavior | |----------|----------| | `cost` | Favors cheaper providers while considering performance and latency | | `cost-focus` | Routes to the cheapest provider with minimal weight on other factors | Both strategies account for cache economics. `cost-focus` weights cost more aggressively. For the general base vs. focus explanation, see [Base vs. focus](/guides/routing-options#base-vs-focus). --- ## Page: Cost Optimization > Section: Set cost ceilings To exclude providers above a price threshold, set `max_cost_per_1m`: ```python Python OpenAI response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": { "optimize": "cost", "max_cost_per_1m": 10.00, }}} ) ``` ```typescript TypeScript OpenAI const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "cost", max_cost_per_1m: 10.00, } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], gateway={ "routing": { "optimize": "cost", "max_cost_per_1m": 10.00, # Max $10 per 1M tokens (average of input + output) }, } ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "cost", max_cost_per_1m: 10.0, // Max $10 per 1M tokens (average of input + output) }, }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"optimize": "cost", "max_cost_per_1m": 10.00}} }' ``` Auriko calculates cost as the average of input and output price per 1M tokens. Providers exceeding this ceiling are excluded from routing. For fine-grained quality and cost constraints, see [Advanced routing](/guides/advanced-routing). --- ## Page: Cost Optimization > Section: Restrict key source If you have negotiated provider rates through your own API keys, force requests to use only BYOK keys for cost control: ```python Python OpenAI response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": { "optimize": "cost", "only_byok": True, }}} ) ``` ```typescript TypeScript OpenAI const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "cost", only_byok: true, } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], gateway={ "routing": { "optimize": "cost", "only_byok": True, # Use only your own provider keys }, } ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "cost", only_byok: true, // Use only your own provider keys }, }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"optimize": "cost", "only_byok": true}} }' ``` See [Advanced routing](/guides/advanced-routing#set-quality-constraints) for the full constraint API and [Bring Your Own Key](/platform/byok) for BYOK setup. --- ## Page: Cost Optimization > Section: Track cost and savings Every response includes the billable cost in `cost.usd`. The usage breakdown shows `prompt_tokens`, `cached_tokens`, and `completion_tokens`. ```python Python Auriko cost = response.routing_metadata.cost print(f"Total cost: ${cost.usd:.6f}") # Check cache usage usage = response.usage if hasattr(usage, "prompt_tokens_details") and usage.prompt_tokens_details: cached = getattr(usage.prompt_tokens_details, "cached_tokens", 0) print(f"Cached tokens: {cached}") print(f"Total prompt tokens: {usage.prompt_tokens}") ``` ```typescript TypeScript Auriko const cost = response.routing_metadata?.cost; console.log(`Total cost: $${cost?.usd}`); // Check cache usage const cached = response.usage?.prompt_tokens_details?.cached_tokens ?? 0; console.log(`Cached tokens: ${cached}`); console.log(`Total prompt tokens: ${response.usage?.prompt_tokens}`); ``` Auriko normalizes cache reporting across all providers. Regardless of which provider served your request, you read `cached_tokens` from `usage.prompt_tokens_details`. When cost-optimized routing triggers a failover, Auriko falls back in cost order to the next cheapest eligible provider. The cost and savings data in each response reflect the output of Auriko's cost model, not list-price arithmetic. --- ## Page: Cost Optimization > Section: Optimize your workload Structure your workload to maximize cost savings. - **Long, stable system prompts:** Maximize cache reuse across requests. - **Consistent conversation IDs:** These help providers maintain cache affinity. - **Steady request cadence:** Bursty traffic can defeat cache expiration windows. - **Prompt length:** Prompts below provider minimum token thresholds get zero cache discount. - **Strategy choice:** `cost-focus` aggressively minimizes cost. `cost` adds weight to latency and performance. - **Monitor:** Track cost and savings in the dashboard. --- ## Page: Cost Optimization > Section: Apply to use cases ### Background processing Batch processing with `cost-focus` routing: ```python Python OpenAI for doc in documents: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": f"Summarize: {doc}"}], extra_body={"gateway": {"routing": {"optimize": "cost-focus"}}} ) save_summary(doc.id, response.choices[0].message.content) ``` ```typescript TypeScript OpenAI for (const doc of documents) { const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: `Summarize: ${doc}` }], gateway: { routing: { optimize: "cost-focus" } }, }); saveSummary(doc.id, response.choices[0].message.content); } ``` ```python Python Auriko for doc in documents: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": f"Summarize: {doc}"}], gateway={"routing": {"optimize": "cost-focus"}} ) save_summary(doc.id, response.choices[0].message.content) ``` ```typescript TypeScript Auriko for (const doc of documents) { const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: `Summarize: ${doc}` }], gateway: { routing: { optimize: "cost-focus" } }, }); await saveSummary(doc.id, response.choices[0].message.content); } ``` ### With latency budget Cost routing with a latency constraint: ```python Python OpenAI response = client.chat.completions.create( model="gpt-5.4", messages=conversation, extra_body={"gateway": {"routing": { "optimize": "cost", "max_ttft_ms": 1000, }}} ) ``` ```typescript TypeScript OpenAI const response = await client.chat.completions.create({ model: "gpt-5.4", messages: conversation, gateway: { routing: { optimize: "cost", max_ttft_ms: 1000, } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="gpt-5.4", messages=conversation, gateway={ "routing": { "optimize": "cost", "max_ttft_ms": 1000, }, } ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-5.4", messages: conversation, gateway: { routing: { optimize: "cost", max_ttft_ms: 1000, }, }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"optimize": "cost", "max_ttft_ms": 1000}} }' ``` --- ## Page: Cost Optimization > Section: Monitor costs Track your cost savings in the Auriko dashboard: - Total spend by day/week/month - Cost per model - Cost per provider - Savings vs. single-provider baseline Monitor your usage and costs in real-time --- ## Page: Error Handling Every error response includes a machine-readable `code`, a canonical `type`, and the `request_id` for support correlation. --- ## Page: Error Handling > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) --- ## Page: Error Handling > Section: Error types All Auriko errors extend `AurikoAPIError`. The envelope is canonical (see [Errors](/api-reference/errors)): | Field | Type | Description | |-------|------|-------------| | `message` | `str` | Human-readable error message | | `status_code` | `int` | HTTP status code | | `code` | `str` | Machine-readable error code | | `type` | `str` | Canonical error type (one of six values) | | `param` | `str \| None` | Parameter that caused the error | | `request_id` / `requestId` | `str` | Value of `x-request-id` on the failing response | | `retry_after_seconds` / `retryAfterSeconds` | `int \| None` | `Retry-After` header value (429 / 503 only) | | `doc_url` / `docUrl` | `str \| None` | Link to the error's docs page | | `provider` | `str \| None` | Upstream provider that generated the error, when attributable | The SDK provides these typed exception classes: | Exception | HTTP | `type` | |-----------|------|--------| | `BadRequestError` | 400 / 413 / 422 | `invalid_request_error` | | `AuthenticationError` | 401 | `authentication_error` | | `PermissionDeniedError` | 403 | `permission_error` | | `NotFoundError` | 404 | `not_found_error` | | `ConflictError` | 409 | `invalid_request_error` | | `RateLimitError` | 429 | `rate_limit_error` | | `InternalServerError` | 500 | `api_error` | | `APIStatusError` | 502 / 503 / 504 | `api_error` | | `APIConnectionError` | — | network failure before any response | Dispatch on `type` + HTTP status for exception class, then on `code` for precise handling within a class. Never branch on `message` text; see [Errors](/api-reference/errors#retry-policy) for retry policy by code. See the [Python SDK Reference](/sdk/python-reference#error-classes) or [TypeScript SDK Reference](/sdk/typescript-reference#error-classes) for complete error class fields and hierarchy. --- ## Page: Error Handling > Section: Handle errors Catch typed exceptions: ```python Python OpenAI import os import openai client = openai.OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) try: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], ) print(response.choices[0].message.content) except openai.AuthenticationError as e: print(f"Check your API key (status={e.status_code})") except openai.RateLimitError as e: print(f"Rate limited (status={e.status_code})") except openai.NotFoundError as e: print(f"Not found: {e.message}") except openai.BadRequestError as e: print(f"Bad request: {e.message}") except openai.PermissionDeniedError as e: print(f"Not allowed: {e.message}") except openai.InternalServerError as e: print(f"Server error (status={e.status_code})") except openai.APIStatusError as e: print(f"API error ({e.status_code}): {e.message}") except openai.APIConnectionError as e: print(f"Network error: {e}") ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); try { const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], }); console.log(response.choices[0].message.content); } catch (e) { if (e instanceof OpenAI.AuthenticationError) { console.log(`Check your API key (status=${e.status})`); } else if (e instanceof OpenAI.RateLimitError) { console.log(`Rate limited (status=${e.status})`); } else if (e instanceof OpenAI.NotFoundError) { console.log(`Not found: ${e.message}`); } else if (e instanceof OpenAI.BadRequestError) { console.log(`Bad request: ${e.message}`); } else if (e instanceof OpenAI.PermissionDeniedError) { console.log(`Not allowed: ${e.message}`); } else if (e instanceof OpenAI.InternalServerError) { console.log(`Server error (status=${e.status})`); } else if (e instanceof OpenAI.APIConnectionError) { console.log(`Network error: ${e.message}`); } else if (e instanceof OpenAI.APIError) { console.log(`API error (${e.status}): ${e.message}`); } } ``` ```python Python Auriko import os from auriko import Client from auriko.errors import ( AurikoAPIError, APIConnectionError, AuthenticationError, PermissionDeniedError, BadRequestError, ConflictError, NotFoundError, RateLimitError, InternalServerError, APIStatusError, ) client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) try: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) except AuthenticationError as e: print(f"Check your API key (request_id={e.request_id})") except RateLimitError as e: print(f"Rate limited, retry after {e.retry_after_seconds}s") except NotFoundError as e: print(f"Not found: {e.message} (param={e.param})") except BadRequestError as e: print(f"Bad request: {e.message} (param={e.param})") except PermissionDeniedError as e: print(f"Not allowed: {e.message} (code={e.code})") except ConflictError as e: print(f"Conflict: {e.message} (code={e.code})") except InternalServerError as e: print(f"Server error (request_id={e.request_id})") except APIStatusError as e: print(f"Upstream error ({e.status_code}): {e.message}") except APIConnectionError as e: print(f"Network error: {e.message}") except AurikoAPIError as e: print(f"API error ({e.status_code}): {e.message}") ``` ```typescript TypeScript Auriko import { Client, AurikoAPIError, APIConnectionError, AuthenticationError, PermissionDeniedError, BadRequestError, ConflictError, NotFoundError, RateLimitError, InternalServerError, APIStatusError, } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); try { const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], }); console.log(response.choices[0].message.content); } catch (e) { if (e instanceof AuthenticationError) { console.log(`Check your API key (requestId=${e.requestId})`); } else if (e instanceof RateLimitError) { console.log(`Rate limited, retry after ${e.retryAfterSeconds}s`); } else if (e instanceof NotFoundError) { console.log(`Not found: ${e.message} (param=${e.param})`); } else if (e instanceof BadRequestError) { console.log(`Bad request: ${e.message} (param=${e.param})`); } else if (e instanceof PermissionDeniedError) { console.log(`Not allowed: ${e.message} (code=${e.code})`); } else if (e instanceof ConflictError) { console.log(`Conflict: ${e.message} (code=${e.code})`); } else if (e instanceof InternalServerError) { console.log(`Server error (requestId=${e.requestId})`); } else if (e instanceof APIStatusError) { console.log(`Upstream error (${e.statusCode}): ${e.message}`); } else if (e instanceof APIConnectionError) { console.log(`Network error: ${e.message}`); } else if (e instanceof AurikoAPIError) { console.log(`API error (${e.statusCode}): ${e.message}`); } } ``` --- ## Page: Error Handling > Section: Use built-in retries The SDK automatically retries transient errors with exponential backoff: | Setting | Value | |---------|-------| | Max retries | 2 (default) | | Initial interval | 500ms | | Max interval | 30 seconds | | Backoff | Exponential (1.5 exponent) + random jitter | | Retried status codes | 429, 500, 502, 503, 504 | | Connection/timeout errors | Retried | | `Retry-After` header | Respected (overrides backoff when present) | ```python Python OpenAI import os from openai import OpenAI # Default: 2 retries with exponential backoff client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) # More retries client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", max_retries=5, ) # Disable retries client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", max_retries=0, ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; // Default: 2 retries with exponential backoff const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); // More retries const resilientClient = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", maxRetries: 5, }); // Disable retries const noRetryClient = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", maxRetries: 0, }); ``` ```python Python Auriko import os from auriko import Client # Default: 2 retries with exponential backoff client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) # More retries for resilience client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", max_retries=5 ) # Disable retries entirely client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", max_retries=0 ) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; // Default: 2 retries with exponential backoff const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); // More retries for resilience const resilientClient = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", maxRetries: 5, }); // Disable retries entirely const noRetryClient = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", maxRetries: 0, }); ``` When the server returns a `Retry-After` header (common with 429 responses), the SDK uses that value instead of the calculated backoff interval. --- ## Page: Error Handling > Section: Retry manually For request-level control over backoff or error filtering, implement custom retry logic: ```python Python OpenAI import os import time import openai client = openai.OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", max_retries=0, ) def make_request_with_retry(messages, max_retries=3): last_error = None for attempt in range(max_retries): try: return client.chat.completions.create( model="gpt-4o", messages=messages, ) except openai.RateLimitError as e: last_error = e retry_after = e.response.headers.get("Retry-After") wait_time = float(retry_after) if retry_after else min(2 ** attempt, 60) time.sleep(wait_time) except openai.APIStatusError as e: last_error = e retry_after = e.response.headers.get("Retry-After") wait_time = float(retry_after) if retry_after else 2 ** attempt time.sleep(wait_time) raise last_error # Usage response = make_request_with_retry([{"role": "user", "content": "Hello!"}]) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", maxRetries: 0, }); async function makeRequestWithRetry( messages: Array<{ role: string; content: string }>, maxRetries = 3 ) { let lastError: Error | undefined; for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await client.chat.completions.create({ model: "gpt-4o", messages, }); } catch (e) { lastError = e as Error; if (e instanceof OpenAI.RateLimitError || e instanceof OpenAI.APIError) { const retryAfter = (e as OpenAI.APIError).headers?.["retry-after"]; const waitMs = (retryAfter ? parseFloat(retryAfter) : Math.min(2 ** attempt, 60)) * 1000; await new Promise((r) => setTimeout(r, waitMs)); } else { throw e; } } } throw lastError; } ``` ```python Python Auriko import os import time from auriko import Client from auriko.errors import RateLimitError, APIStatusError client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", max_retries=0 # Disable auto-retry ) def make_request_with_retry(messages, max_retries=3): last_error = None for attempt in range(max_retries): try: return client.chat.completions.create( model="gpt-4o", messages=messages ) except RateLimitError as e: last_error = e wait_time = e.retry_after_seconds or min(2 ** attempt, 60) time.sleep(wait_time) except APIStatusError as e: last_error = e wait_time = e.retry_after_seconds or 2 ** attempt time.sleep(wait_time) raise last_error # Usage response = make_request_with_retry([{"role": "user", "content": "Hello!"}]) ``` ```typescript TypeScript Auriko import { Client, RateLimitError, APIStatusError } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", maxRetries: 0, // Disable auto-retry }); async function makeRequestWithRetry( messages: Array<{ role: string; content: string }>, maxRetries = 3 ) { let lastError: Error | undefined; for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await client.chat.completions.create({ model: "gpt-4o", messages, }); } catch (e) { lastError = e as Error; if (e instanceof RateLimitError || e instanceof APIStatusError) { const waitMs = (e.retryAfterSeconds ?? Math.min(2 ** attempt, 60)) * 1000; await new Promise((r) => setTimeout(r, waitMs)); } else { throw e; } } } throw lastError; } ``` --- ## Page: Error Handling > Section: Retry asynchronously Retry with async/await: ```python Python OpenAI import os import asyncio import openai client = openai.AsyncOpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", max_retries=0, ) async def make_request_with_backoff(messages, max_retries=3): for attempt in range(max_retries): try: return await client.chat.completions.create( model="gpt-4o", messages=messages, ) except (openai.RateLimitError, openai.APIStatusError) as e: if attempt == max_retries - 1: raise retry_after = e.response.headers.get("Retry-After") wait_time = float(retry_after) if retry_after else 2 ** attempt await asyncio.sleep(wait_time) ``` ```python Python Auriko import os import asyncio from auriko import AsyncClient from auriko.errors import RateLimitError, APIStatusError client = AsyncClient( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", max_retries=0 ) async def make_request_with_backoff(messages, max_retries=3): for attempt in range(max_retries): try: return await client.chat.completions.create( model="gpt-4o", messages=messages ) except (RateLimitError, APIStatusError) as e: if attempt == max_retries - 1: raise wait_time = e.retry_after_seconds or 2 ** attempt await asyncio.sleep(wait_time) ``` TypeScript is inherently async. See the TypeScript tabs in [Retry manually](#retry-manually). **Side effects and retries:** When using tools or multi-step workflows, consider whether retries are safe. A retried request that triggers a tool call may execute the tool twice. For idempotency-sensitive operations, either disable automatic retries (`max_retries=0`) or implement your own deduplication logic. --- ## Page: Error Handling > Section: Fall back to another model Catch the error from your primary model and retry with a different one: ```python Python OpenAI import os import openai client = openai.OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) def chat_with_fallback(messages): try: return client.chat.completions.create( model="gpt-4o", messages=messages, extra_body={"gateway": {"routing": {"max_ttft_ms": 1000}}}, ) except openai.APIError as e: print(f"Primary failed ({e}), trying fallback...") return client.chat.completions.create( model="gpt-4o-mini", messages=messages, ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); async function chatWithFallback(messages: any[]) { try { return await client.chat.completions.create({ model: "gpt-4o", messages, gateway: { routing: { max_ttft_ms: 1000 } }, }); } catch (e) { if (e instanceof OpenAI.APIError) { console.log(`Primary failed (${e}), trying fallback...`); return await client.chat.completions.create({ model: "gpt-4o-mini", messages, }); } throw e; } } ``` ```python Python Auriko import os from auriko import Client, AurikoAPIError client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) def chat_with_fallback(messages): try: return client.chat.completions.create( model="gpt-4o", messages=messages, gateway={"routing": {"max_ttft_ms": 1000}} ) except AurikoAPIError as e: print(f"Primary failed ({e}), trying fallback...") return client.chat.completions.create( model="gpt-4o-mini", messages=messages ) ``` ```typescript TypeScript Auriko import { Client, AurikoAPIError } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); async function chatWithFallback(messages: any[]) { try { return await client.chat.completions.create({ model: "gpt-4o", messages, gateway: { routing: { max_ttft_ms: 1000 } }, }); } catch (e) { if (e instanceof AurikoAPIError) { console.log(`Primary failed (${e}), trying fallback...`); return await client.chat.completions.create({ model: "gpt-4o-mini", messages, }); } throw e; } } ``` --- ## Page: Error Handling > Section: Use circuit breakers A circuit breaker stops sending requests after repeated failures and re-tests after a timeout: ```python Python OpenAI import os from datetime import datetime, timedelta, timezone import openai client = openai.OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) class CircuitBreaker: def __init__(self, failure_threshold=5, reset_timeout=60): self.failures = 0 self.failure_threshold = failure_threshold self.reset_timeout = reset_timeout self.last_failure = None self.is_open = False def record_failure(self): self.failures += 1 self.last_failure = datetime.now(timezone.utc) if self.failures >= self.failure_threshold: self.is_open = True def record_success(self): self.failures = 0 self.is_open = False def can_proceed(self): if not self.is_open: return True if datetime.now(timezone.utc) - self.last_failure > timedelta(seconds=self.reset_timeout): self.is_open = False return True return False # Usage breaker = CircuitBreaker() def safe_request(messages): if not breaker.can_proceed(): raise Exception("Circuit breaker open, try later") try: response = client.chat.completions.create( model="gpt-4o", messages=messages, ) breaker.record_success() return response except openai.APIStatusError as e: breaker.record_failure() raise ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); class CircuitBreaker { private failures = 0; private lastFailure: Date | null = null; private isOpen = false; constructor( private failureThreshold = 5, private resetTimeout = 60_000, ) {} recordFailure() { this.failures++; this.lastFailure = new Date(); if (this.failures >= this.failureThreshold) this.isOpen = true; } recordSuccess() { this.failures = 0; this.isOpen = false; } canProceed(): boolean { if (!this.isOpen) return true; if (Date.now() - this.lastFailure!.getTime() > this.resetTimeout) { this.isOpen = false; return true; } return false; } } const breaker = new CircuitBreaker(); async function safeRequest(messages: any[]) { if (!breaker.canProceed()) throw new Error("Circuit breaker open, try later"); try { const response = await client.chat.completions.create({ model: "gpt-4o", messages }); breaker.recordSuccess(); return response; } catch (e) { if (e instanceof OpenAI.APIError && !(e instanceof OpenAI.APIConnectionError)) { breaker.recordFailure(); } throw e; } } ``` ```python Python Auriko import os from datetime import datetime, timedelta, timezone from auriko import Client from auriko.errors import APIStatusError client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) class CircuitBreaker: def __init__(self, failure_threshold=5, reset_timeout=60): self.failures = 0 self.failure_threshold = failure_threshold self.reset_timeout = reset_timeout self.last_failure = None self.is_open = False def record_failure(self): self.failures += 1 self.last_failure = datetime.now(timezone.utc) if self.failures >= self.failure_threshold: self.is_open = True def record_success(self): self.failures = 0 self.is_open = False def can_proceed(self): if not self.is_open: return True if datetime.now(timezone.utc) - self.last_failure > timedelta(seconds=self.reset_timeout): self.is_open = False return True return False # Usage breaker = CircuitBreaker() def safe_request(messages): if not breaker.can_proceed(): raise Exception("Circuit breaker open, try later") try: response = client.chat.completions.create( model="gpt-4o", messages=messages ) breaker.record_success() return response except APIStatusError as e: breaker.record_failure() raise ``` ```typescript TypeScript Auriko import { Client, APIStatusError } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); class CircuitBreaker { private failures = 0; private lastFailure: Date | null = null; private isOpen = false; constructor( private failureThreshold = 5, private resetTimeout = 60_000, ) {} recordFailure() { this.failures++; this.lastFailure = new Date(); if (this.failures >= this.failureThreshold) this.isOpen = true; } recordSuccess() { this.failures = 0; this.isOpen = false; } canProceed(): boolean { if (!this.isOpen) return true; if (Date.now() - this.lastFailure!.getTime() > this.resetTimeout) { this.isOpen = false; return true; } return false; } } const breaker = new CircuitBreaker(); async function safeRequest(messages: any[]) { if (!breaker.canProceed()) throw new Error("Circuit breaker open, try later"); try { const response = await client.chat.completions.create({ model: "gpt-4o", messages }); breaker.recordSuccess(); return response; } catch (e) { if (e instanceof APIStatusError) breaker.recordFailure(); throw e; } } ``` --- ## Page: Error Handling > Section: Set timeouts ```python Python OpenAI import os import openai client = openai.OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", timeout=30.0, ) try: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Write a long essay..."}], ) except openai.APIConnectionError as e: print(f"Connection failed: {e}") ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", timeout: 30000, }); try { const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Write a long essay..." }], }); } catch (e) { if (e instanceof OpenAI.APIConnectionError) { console.log(`Connection failed: ${e.message}`); } } ``` ```python Python Auriko import os from auriko import Client from auriko.errors import APIConnectionError client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", timeout=30.0 # 30 second timeout ) try: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Write a long essay..."}] ) except APIConnectionError as e: print(f"Connection failed: {e.message}") ``` ```typescript TypeScript Auriko import { Client, APIConnectionError } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", timeout: 30000, // 30 second timeout (ms) }); try { const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Write a long essay..." }], }); } catch (e) { if (e instanceof APIConnectionError) { console.log(`Connection failed: ${e.message}`); } } ``` --- ## Page: Error Handling > Section: Log errors Log errors for debugging: ```python Python OpenAI import os import logging import openai logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) client = openai.OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) try: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], ) except openai.APIStatusError as e: logger.exception("Chat completion failed", extra={ "error_type": type(e).__name__, "status_code": e.status_code, "request_id": e.response.headers.get("x-request-id"), "model": "gpt-4o", }) raise ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); try { const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], }); } catch (e) { if (e instanceof OpenAI.APIError) { console.error("Chat completion failed", { error_type: e.constructor.name, status_code: e.status, request_id: e.request_id, model: "gpt-4o", }); } throw e; } ``` ```python Python Auriko import os import logging from auriko import Client, AurikoAPIError logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) try: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) except AurikoAPIError as e: logger.exception("Chat completion failed", extra={ "error_type": type(e).__name__, "status_code": e.status_code, "request_id": e.request_id, "model": "gpt-4o", }) raise ``` ```typescript TypeScript Auriko import { Client, AurikoAPIError } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); try { const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], }); } catch (e) { if (e instanceof AurikoAPIError) { console.error("Chat completion failed", { error_type: e.constructor.name, status_code: e.statusCode, request_id: e.requestId, model: "gpt-4o", }); } throw e; } ``` --- ## Page: Error Handling > Section: Map OpenAI SDK errors If you use the OpenAI SDK directly (with `base_url` pointed at Auriko), you can convert OpenAI errors to typed Auriko errors using `map_openai_error()`: ```python import os import openai from auriko import map_openai_error from auriko.errors import RateLimitError, PermissionDeniedError client = openai.OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) try: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) except openai.APIStatusError as e: auriko_error = map_openai_error(e) if isinstance(auriko_error, RateLimitError): # budget_exhausted and insufficient_quota surface as RateLimitError too; # branch on auriko_error.code for code-level retry decisions. print(f"Rate limited. Retry after: {auriko_error.retry_after_seconds}s (code={auriko_error.code})") elif isinstance(auriko_error, PermissionDeniedError): print(f"Permission denied: {auriko_error.message}") else: raise auriko_error ``` You get typed error fields (`status_code`, `code`, `response_headers`) and fine-grained `isinstance` checks, even when using the OpenAI client. `map_openai_error()` is Python-only. TypeScript users should use the Auriko SDK directly for typed errors. See [OpenAI Compatibility](/openai-compatibility#map-errors) for OpenAI SDK error mapping. --- ## Page: Prompt Caching *Prompt caching* (reusing previously processed prompt tokens instead of reprocessing them) reduces cost and latency on repeated requests. By default, Auriko handles cache optimization automatically. For fine-grained control, you can specify cache control manually. --- ## Page: Prompt Caching > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) --- ## Page: Prompt Caching > Section: How it works Auriko optimizes caching for each provider when your request includes reusable prompt content. On subsequent requests sharing the same prompt prefix, the provider serves cached tokens at reduced cost and lower latency. Auriko accounts for each provider's caching economics (token thresholds, discount depths, and read/write prices) when choosing where to route. Over time, the system learns your usage patterns to improve estimation accuracy. Create separate workspaces for different use cases to get better predictions. Auriko is a zero data retention proxy. Your prompts, responses, and content are never read, logged, or stored. Pattern calibration uses usage metadata only. Read the [Privacy Policy](https://www.auriko.ai/privacy) for details. --- ## Page: Prompt Caching > Section: Send a cached request Send a request with a reusable system prompt: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) response = client.chat.completions.create( model="claude-sonnet-4-20250514", messages=[ {"role": "system", "content": "You are a helpful coding assistant..."}, {"role": "user", "content": "Explain async/await in Python."}, ], ) usage = response.usage if hasattr(usage, "prompt_tokens_details") and usage.prompt_tokens_details: cached = getattr(usage.prompt_tokens_details, "cached_tokens", 0) print(f"Cached tokens: {cached}") print(f"Total prompt tokens: {usage.prompt_tokens}") ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "claude-sonnet-4-20250514", messages: [ { role: "system", content: "You are a helpful coding assistant..." }, { role: "user", content: "Explain async/await in Python." }, ], }); const usage = response.usage; const cached = (usage as any)?.prompt_tokens_details?.cached_tokens ?? 0; console.log(`Cached tokens: ${cached}`); console.log(`Total prompt tokens: ${usage?.prompt_tokens}`); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="claude-sonnet-4-20250514", messages=[ {"role": "system", "content": "You are a helpful coding assistant..."}, {"role": "user", "content": "Explain async/await in Python."} ] ) usage = response.usage if hasattr(usage, "prompt_tokens_details") and usage.prompt_tokens_details: cached = getattr(usage.prompt_tokens_details, "cached_tokens", 0) print(f"Cached tokens: {cached}") print(f"Total prompt tokens: {usage.prompt_tokens}") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "claude-sonnet-4-20250514", messages: [ { role: "system", content: "You are a helpful coding assistant..." }, { role: "user", content: "Explain async/await in Python." }, ], }); const cached = response.usage?.prompt_tokens_details?.cached_tokens ?? 0; console.log(`Cached tokens: ${cached}`); console.log(`Total prompt tokens: ${response.usage?.prompt_tokens}`); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "messages": [ {"role": "system", "content": "You are a helpful coding assistant..."}, {"role": "user", "content": "Explain async/await in Python."} ] }' ``` --- ## Page: Prompt Caching > Section: Override caching per provider Auriko handles caching automatically for supported providers. For explicit control, each provider accepts specific fields. When you supply one, Auriko skips automatic injection and uses your value. | Provider | Field | Effect | |----------|-------|--------| | Anthropic | `cache_control: {"type": "ephemeral"}` on content blocks | Marks specific content for caching | | OpenAI | `prompt_cache_key` (string) | Improves cache hit rate for repeated conversations | | OpenAI | `prompt_cache_retention: "24h"` | Extends cache lifetime to 24 hours | | Fireworks | `user` (string) | Improves cache reuse across conversation turns | When you provide any of these fields, Auriko skips automatic cache injection for that provider. ### Anthropic — `cache_control` Add `cache_control` to content blocks to mark specific content for caching: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) response = client.chat.completions.create( model="claude-sonnet-4-20250514", messages=[ {"role": "system", "content": [ {"type": "text", "text": "You are a helpful coding assistant with deep knowledge of Python, JavaScript, and Rust. You follow best practices and explain your reasoning step by step.", "cache_control": {"type": "ephemeral"}}, ]}, {"role": "user", "content": "Explain async/await in Python."}, ], ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "claude-sonnet-4-20250514", messages: [ { role: "system", content: [ { type: "text", text: "You are a helpful coding assistant with deep knowledge of Python, JavaScript, and Rust. You follow best practices and explain your reasoning step by step.", cache_control: { type: "ephemeral" } }, ]}, { role: "user", content: "Explain async/await in Python." }, ], }); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="claude-sonnet-4-20250514", messages=[ {"role": "system", "content": [ {"type": "text", "text": "You are a helpful coding assistant with deep knowledge of Python, JavaScript, and Rust. You follow best practices and explain your reasoning step by step.", "cache_control": {"type": "ephemeral"}}, ]}, {"role": "user", "content": "Explain async/await in Python."} ] ) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "claude-sonnet-4-20250514", messages: [ { role: "system", content: [ { type: "text", text: "You are a helpful coding assistant with deep knowledge of Python, JavaScript, and Rust. You follow best practices and explain your reasoning step by step.", cache_control: { type: "ephemeral" } }, ]}, { role: "user", content: "Explain async/await in Python." }, ], }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "messages": [ {"role": "system", "content": [ {"type": "text", "text": "You are a helpful coding assistant with deep knowledge of Python, JavaScript, and Rust. You follow best practices and explain your reasoning step by step.", "cache_control": {"type": "ephemeral"}} ]}, {"role": "user", "content": "Explain async/await in Python."} ] }' ``` The only supported type is `"ephemeral"`. This follows the provider's default retention behavior. `cache_control` applies to Anthropic models only. For other providers, automatic optimization handles caching. ### OpenAI — `prompt_cache_key` and `prompt_cache_retention` `prompt_cache_key` improves cache hit rate for repeated conversations. `prompt_cache_retention: "24h"` extends the cache lifetime to 24 hours. `prompt_cache_retention` is supported on gpt-4.1+ and gpt-5+ models only. It isn't compatible with ZDR data policy. Omit it if your workspace uses ZDR. ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) response = client.chat.completions.create( model="gpt-4.1-mini", messages=[ {"role": "system", "content": "You are a helpful coding assistant."}, {"role": "user", "content": "Explain async/await in Python."}, ], extra_body={ "prompt_cache_key": "my-conversation-123", "prompt_cache_retention": "24h", }, ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4.1-mini", messages: [ { role: "system", content: "You are a helpful coding assistant." }, { role: "user", content: "Explain async/await in Python." }, ], prompt_cache_key: "my-conversation-123", prompt_cache_retention: "24h", }); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-4.1-mini", messages=[ {"role": "system", "content": "You are a helpful coding assistant."}, {"role": "user", "content": "Explain async/await in Python."} ], prompt_cache_key="my-conversation-123", extra_body={"prompt_cache_retention": "24h"} ) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4.1-mini", messages: [ { role: "system", content: "You are a helpful coding assistant." }, { role: "user", content: "Explain async/await in Python." }, ], prompt_cache_key: "my-conversation-123", extra_body: { prompt_cache_retention: "24h" }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4.1-mini", "messages": [ {"role": "system", "content": "You are a helpful coding assistant."}, {"role": "user", "content": "Explain async/await in Python."} ], "prompt_cache_key": "my-conversation-123", "prompt_cache_retention": "24h" }' ``` ### Fireworks — `user` On Fireworks, requests with the same `user` value benefit from improved cache reuse across conversation turns. ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) response = client.chat.completions.create( model="gpt-oss-20b", messages=[ {"role": "system", "content": "You are a helpful coding assistant."}, {"role": "user", "content": "Explain async/await in Python."}, ], user="my-conversation-123", ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-oss-20b", messages: [ { role: "system", content: "You are a helpful coding assistant." }, { role: "user", content: "Explain async/await in Python." }, ], user: "my-conversation-123", }); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-oss-20b", messages=[ {"role": "system", "content": "You are a helpful coding assistant."}, {"role": "user", "content": "Explain async/await in Python."} ], user="my-conversation-123" ) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-oss-20b", messages: [ { role: "system", content: "You are a helpful coding assistant." }, { role: "user", content: "Explain async/await in Python." }, ], user: "my-conversation-123", }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-oss-20b", "messages": [ {"role": "system", "content": "You are a helpful coding assistant."}, {"role": "user", "content": "Explain async/await in Python."} ], "user": "my-conversation-123" }' ``` --- ## Page: Prompt Caching > Section: Check cache usage For `/v1/chat/completions` responses, cache hit information appears in `usage.prompt_tokens_details`: ```json { "usage": { "prompt_tokens": 1500, "completion_tokens": 200, "total_tokens": 1700, "prompt_tokens_details": { "cached_tokens": 1200, "cache_write_tokens": 300 } } } ``` `cached_tokens` shows how many prompt tokens were served from cache. Auriko normalizes this field across all providers in the OpenAI-format response. `cache_write_tokens` indicates how many tokens were written to prompt cache on this request. Present when the provider reports cache-write accounting. For `/v1/messages` responses, cache tokens appear as top-level usage fields: ```json { "usage": { "input_tokens": 300, "output_tokens": 200, "cache_read_input_tokens": 1200, "cache_creation_input_tokens": 0 } } ``` `input_tokens` represents only the non-cached portion. Total input tokens = `input_tokens` + `cache_read_input_tokens` + `cache_creation_input_tokens`. For `/v1/responses` requests, cache token counts appear in `usage.input_tokens_details`: ```json { "usage": { "input_tokens": 1500, "output_tokens": 200, "total_tokens": 1700, "input_tokens_details": { "cached_tokens": 1200, "cache_write_tokens": 300 } } } ``` `cache_write_tokens` indicates how many tokens were written to prompt cache. Present when the provider reports cache-write accounting. ### Check cache savings Cache savings appear in `routing_metadata.cost` when savings are greater than zero: ```json { "routing_metadata": { "cost": { "usd": 0.0042, "cache_savings_percent": 47, "cache_savings_usd": 0.0037 } } } ``` `cache_savings_percent` is an integer (0-100) showing the percentage saved compared to uncached cost. `cache_savings_usd` shows the dollar amount saved. ### Check cache usage in streams Cache metrics appear in the final streaming chunk alongside `usage` and `routing_metadata`. See [Streaming](/guides/streaming#handle-final-chunks) for details on consuming trailing chunks. --- ## Page: Prompt Caching > Section: Improve cache hits You can improve cache hit rates by structuring your requests for reuse. - **Long, stable system prompts:** Place reusable instructions in the system message. The prompt prefix is what providers cache. - **Few-shot examples:** Static example blocks are reused across requests. - **Static before dynamic:** Put content that doesn't change before content that does. - **Multi-turn conversations:** Shared prompt prefixes get better cache reuse across requests. - **Steady request cadence:** Providers expire cached tokens after inactivity. Steady flow keeps entries warm. See [Cost optimization](/guides/cost-optimization#optimize-your-workload) for more strategies. --- ## Page: Prompt Caching > Section: Look up cache pricing The model directory exposes cache pricing for every supported provider. Query it to see `cache_read_price`, `cache_write_price`, and `supports_prompt_caching` per model: ```python Python OpenAI import os import httpx response = httpx.get( "https://api.auriko.ai/v1/directory/models", headers={"Authorization": f"Bearer {os.environ['AURIKO_API_KEY']}"}, ) for model_id, model in response.json()["models"].items(): for provider in model.get("providers", []): for tier in provider.get("tiers", []): if tier.get("cache_read_price"): print(f"{model_id} ({provider['provider']}): " f"read=${tier['cache_read_price']}/M, " f"write=${tier.get('cache_write_price', 'N/A')}/M") ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) directory = client.models.list_directory() for model_id, model in directory.models.items(): for provider in model.providers: for tier in provider.tiers: if tier.cache_read_price: print(f"{model_id} ({provider.provider}): " f"read=${tier.cache_read_price}/M, " f"write=${tier.cache_write_price}/M") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const directory = await client.models.listDirectory(); for (const [modelId, model] of Object.entries(directory.models)) { for (const provider of model.providers) { for (const tier of provider.tiers) { if (tier.cache_read_price) { console.log(`${modelId} (${provider.provider}): ` + `read=$${tier.cache_read_price}/M, ` + `write=$${tier.cache_write_price}/M`); } } } } ``` ```bash cURL curl https://api.auriko.ai/v1/directory/models \ -H "Authorization: Bearer $AURIKO_API_KEY" \ | jq '.models | to_entries[] | .value.providers[] | .tiers[] | select(.cache_read_price) | {model: .name, cache_read_price, cache_write_price}' ``` Providers offer discounted rates for cache reads compared to standard input pricing. Some charge a surcharge for cache writes. Check the directory for current prices. --- ## Page: Prompt Caching > Section: Troubleshoot | Symptom | Fix | |---------|-----| | `cached_tokens` always 0 (first request) | The first request creates the cache. Send a follow-up with the same prefix. | | `cached_tokens` always 0 (unsupported model) | Check `supports_prompt_caching` in the [model directory](/api-reference/model-directory). | | `cached_tokens` always 0 (unique prompts) | Caching requires a shared prefix. Add a reusable system prompt. | | `cached_tokens` always 0 (short prompt) | Your prompt may be below the provider's minimum token threshold. Add more reusable content to the system message. | | Lower-than-expected savings | Move static content before dynamic content in messages. | | Lower-than-expected savings (gaps between requests) | Providers expire cached tokens after inactivity. Maintain steady request flow. | | `cache_savings_percent` not in response | The field appears only when savings are greater than zero. | --- ## Page: Prompt Caching > Section: Resources - [Cost optimization](/guides/cost-optimization#understand-the-cost-model) — cache economics in routing - [Streaming](/guides/streaming#handle-final-chunks) — cache metrics in streaming responses - [Model directory](/api-reference/model-directory) — cache pricing and support per model - [Response metadata](/contract/response-metadata) — `routing_metadata.cost` fields --- ## Page: Extensions and Thinking Add `reasoning_effort` to your request. Auriko translates it into each provider's native format. Use `extensions` keyed by provider name to pass through provider-specific parameters like Anthropic's `metadata` or Google's `safety_settings`. --- ## Page: Extensions and Thinking > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) - A model that supports reasoning (see [provider support table](#check-provider-support)) --- ## Page: Extensions and Thinking > Section: Enable thinking Pass `reasoning_effort` in your request to control extended reasoning: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Solve this step by step: what is 23! / 20!?"}], extra_body={"reasoning_effort": "high"} ) print(response.choices[0].message.content) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages: [{ role: "user", content: "Solve this step by step: what is 23! / 20!?" }], reasoning_effort: "high", }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Solve this step by step: what is 23! / 20!?"}], reasoning_effort="high" ) print(response.choices[0].message.content) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages: [{ role: "user", content: "Solve this step by step: what is 23! / 20!?" }], reasoning_effort: "high", }); console.log(response.choices[0].message.content); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "messages": [{"role": "user", "content": "Solve this step by step: what is 23! / 20!?"}], "reasoning_effort": "high" }' ``` --- ## Page: Extensions and Thinking > Section: Check provider support Auriko translates `reasoning_effort` for each provider: | Provider | Models | Behavior | |----------|--------|----------| | Anthropic | Claude 4.6 (Opus, Sonnet) | Adaptive thinking with effort control | | Anthropic | Claude 4.5 Opus | Thinking budget + effort control | | Anthropic | Claude 4.5 Sonnet/Haiku | Thinking budget derived from effort level | | OpenAI | o3, o4-mini, GPT-5 | Native `reasoning_effort` (dropped when `tools` present on GPT-5.4+) | | Google | Gemini 3.x | Thinking level (`low`/`medium`/`high`) | | Google | Gemini 2.5 Flash/Pro | Thinking budget derived from effort level | | DeepSeek | V4 Flash, V4 Pro | Thinking budget derived from effort level | | xAI | Grok 3 mini, Grok 4.3 | Native `reasoning_effort` (`low`/`high` on Grok 3 mini, `low`/`medium`/`high` on Grok 4.3) | | MiniMax | M2 series | Built-in reasoning; `reasoning_effort` dropped | | Moonshot | Kimi K2.5, Kimi K2.6 | Native `reasoning_effort` | Non-reasoning models (e.g. GPT-4o, GPT-4.1, Llama) reject `reasoning_effort` with 400 `reasoning_not_supported`. The one exception is GPT-5.4+ with `tools`: Auriko drops `reasoning_effort` to prevent an upstream 400. `deepseek-chat` and `deepseek-reasoner` are aliases of `deepseek-v4-flash` — they select the same model, not a thinking vs. non-thinking mode. Through Auriko, thinking behavior follows the serving provider's default and may differ from DeepSeek's direct API. Set `reasoning_effort` to control it explicitly — use `"off"` for non-thinking output. --- ## Page: Extensions and Thinking > Section: Read thinking output Some providers surface the model's reasoning in the `reasoning_content` field on the response message: ```python Python OpenAI response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Solve step by step: what is 23! / 20!?"}], extra_body={"reasoning_effort": "high"} ) msg = response.choices[0].message reasoning = getattr(msg, "reasoning_content", None) if reasoning: print(f"Reasoning: {reasoning}") print(f"Answer: {msg.content}") ``` ```typescript TypeScript OpenAI const response = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages: [{ role: "user", content: "Solve step by step: what is 23! / 20!?" }], reasoning_effort: "high", }); const msg = response.choices[0].message; const reasoning = (msg as any).reasoning_content; if (reasoning) { console.log(`Reasoning: ${reasoning}`); } console.log(`Answer: ${msg.content}`); ``` ```python Python Auriko response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Solve step by step: what is 23! / 20!?"}], reasoning_effort="high" ) # Access the reasoning (if the model returns it) if response.choices[0].message.reasoning_content: print(f"Reasoning: {response.choices[0].message.reasoning_content}") print(f"Answer: {response.choices[0].message.content}") ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages: [{ role: "user", content: "Solve step by step: what is 23! / 20!?" }], reasoning_effort: "high", }); if (response.choices[0].message.reasoning_content) { console.log(`Reasoning: ${response.choices[0].message.reasoning_content}`); } console.log(`Answer: ${response.choices[0].message.content}`); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "messages": [{"role": "user", "content": "Solve step by step: what is 23! / 20!?"}], "reasoning_effort": "high" }' ``` Not all reasoning models populate `reasoning_content`, so check before accessing. OpenAI keeps reasoning internal, and other providers vary by model. --- ## Page: Extensions and Thinking > Section: Preserve reasoning across turns Some providers return reasoning context you echo back for multi-turn continuity. Anthropic and Google use structured `reasoning` blocks with cryptographic signatures, while DeepSeek uses a plain-text `reasoning_content` field. Include the relevant fields from the assistant response in your next request to preserve context. ### Read structured reasoning ```python Python OpenAI response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Analyze this problem step by step."}], extra_body={"reasoning_effort": "high"} ) msg = response.choices[0].message reasoning = getattr(msg, "reasoning", None) if reasoning: for block in reasoning: if block.get("type") == "thinking": print(f"Thinking: {block['thinking'][:80]}...") print(f"Signature: {block['signature'][:20]}...") elif block.get("type") == "redacted": print("Redacted block (encrypted)") ``` ```typescript TypeScript OpenAI const response = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages: [{ role: "user", content: "Analyze this problem step by step." }], reasoning_effort: "high", }); const msg = response.choices[0].message; const reasoning = (msg as any).reasoning; if (reasoning) { for (const block of reasoning) { if (block.type === "thinking") { console.log(`Thinking: ${block.thinking.slice(0, 80)}...`); console.log(`Signature: ${block.signature.slice(0, 20)}...`); } else if (block.type === "redacted") { console.log("Redacted block (encrypted)"); } } } ``` ```python Python Auriko response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Analyze this problem step by step."}], reasoning_effort="high" ) msg = response.choices[0].message if msg.reasoning: for block in msg.reasoning: if block.type == "thinking": print(f"Thinking: {block.thinking[:80]}...") print(f"Signature: {block.signature[:20]}...") elif block.type == "redacted": print(f"Redacted block (encrypted)") ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages: [{ role: "user", content: "Analyze this problem step by step." }], reasoning_effort: "high", }); const msg = response.choices[0].message; if (msg.reasoning) { for (const block of msg.reasoning) { if (block.type === "thinking") { console.log(`Thinking: ${block.thinking.slice(0, 80)}...`); console.log(`Signature: ${block.signature.slice(0, 20)}...`); } else if (block.type === "redacted") { console.log("Redacted block (encrypted)"); } } } ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "messages": [{"role": "user", "content": "Analyze this problem step by step."}], "reasoning_effort": "high" }' ``` Each block has a `type`: - `thinking`: contains `thinking` (the reasoning text) and `signature` (cryptographic signature) - `redacted`: contains `data` (encrypted, opaque to the client) ### Round-trip reasoning To continue a multi-turn conversation with reasoning context, include the full assistant message (with `reasoning`) in your next request: ```python Python OpenAI messages = [ {"role": "user", "content": "What are the trade-offs of microservices vs monoliths?"}, ] first = client.chat.completions.create( model="claude-sonnet-4-6", messages=messages, extra_body={"reasoning_effort": "high"} ) assistant_msg = first.choices[0].message messages.append(assistant_msg.model_dump(exclude_none=True)) messages.append({"role": "user", "content": "Now apply that analysis to a 5-person startup."}) second = client.chat.completions.create( model="claude-sonnet-4-6", messages=messages, extra_body={"reasoning_effort": "high"} ) ``` ```typescript TypeScript OpenAI const messages = [ { role: "user" as const, content: "What are the trade-offs of microservices vs monoliths?" }, ]; const first = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages, reasoning_effort: "high", }); const assistantMsg = first.choices[0].message; messages.push(assistantMsg); messages.push({ role: "user" as const, content: "Now apply that analysis to a 5-person startup." }); const second = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages, reasoning_effort: "high", }); ``` ```python Python Auriko messages = [ {"role": "user", "content": "What are the trade-offs of microservices vs monoliths?"}, ] first = client.chat.completions.create( model="claude-sonnet-4-6", messages=messages, reasoning_effort="high" ) assistant_msg = first.choices[0].message messages.append(assistant_msg.model_dump(exclude_none=True)) messages.append({"role": "user", "content": "Now apply that analysis to a 5-person startup."}) second = client.chat.completions.create( model="claude-sonnet-4-6", messages=messages, reasoning_effort="high" ) ``` ```typescript TypeScript Auriko const messages = [ { role: "user" as const, content: "What are the trade-offs of microservices vs monoliths?" }, ]; const first = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages, reasoning_effort: "high", }); const assistantMsg = first.choices[0].message; messages.push(assistantMsg); messages.push({ role: "user" as const, content: "Now apply that analysis to a 5-person startup." }); const second = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages, reasoning_effort: "high", }); ``` ### DeepSeek reasoning content DeepSeek models return reasoning as a plain `reasoning_content` string instead of structured `reasoning` blocks. For multi-turn conversations with DeepSeek, include `reasoning_content` on assistant messages you send back. To preserve it, serialize the full message object: ```python Python OpenAI first = client.chat.completions.create( model="deepseek-v4-flash", messages=[{"role": "user", "content": "Explain quantum entanglement step by step."}], extra_body={"reasoning_effort": "high"} ) msg = first.choices[0].message messages = [ {"role": "user", "content": "Explain quantum entanglement step by step."}, msg.model_dump(exclude_none=True), # preserves reasoning_content {"role": "user", "content": "Now explain it to a five-year-old."}, ] second = client.chat.completions.create( model="deepseek-v4-flash", messages=messages, extra_body={"reasoning_effort": "high"} ) ``` ```typescript TypeScript OpenAI const first = await client.chat.completions.create({ model: "deepseek-v4-flash", messages: [{ role: "user", content: "Explain quantum entanglement step by step." }], reasoning_effort: "high", }); const msg = first.choices[0].message; const messages = [ { role: "user" as const, content: "Explain quantum entanglement step by step." }, msg, // preserves reasoning_content { role: "user" as const, content: "Now explain it to a five-year-old." }, ]; const second = await client.chat.completions.create({ model: "deepseek-v4-flash", messages, reasoning_effort: "high", }); ``` ```python Python Auriko first = client.chat.completions.create( model="deepseek-v4-flash", messages=[{"role": "user", "content": "Explain quantum entanglement step by step."}], reasoning_effort="high" ) msg = first.choices[0].message messages = [ {"role": "user", "content": "Explain quantum entanglement step by step."}, msg.model_dump(exclude_none=True), # preserves reasoning_content {"role": "user", "content": "Now explain it to a five-year-old."}, ] second = client.chat.completions.create( model="deepseek-v4-flash", messages=messages, reasoning_effort="high" ) ``` ```typescript TypeScript Auriko const first = await client.chat.completions.create({ model: "deepseek-v4-flash", messages: [{ role: "user", content: "Explain quantum entanglement step by step." }], reasoning_effort: "high", }); const msg = first.choices[0].message; const messages = [ { role: "user" as const, content: "Explain quantum entanglement step by step." }, msg, // preserves reasoning_content { role: "user" as const, content: "Now explain it to a five-year-old." }, ]; const second = await client.chat.completions.create({ model: "deepseek-v4-flash", messages, reasoning_effort: "high", }); ``` If you construct assistant messages manually and omit `reasoning_content`, Auriko sets it to an empty string. Echo back the original value from the response. ### Stream reasoning fields When streaming with extended thinking, two additional delta fields carry reasoning block data: - `delta.reasoning_signature`: cryptographic signature for the current thinking block - `delta.reasoning_redacted_data`: encrypted data for a redacted thinking block (complete in one event) These appear alongside `delta.reasoning_content` (the incremental reasoning text). --- ## Page: Extensions and Thinking > Section: Use provider passthrough For provider-specific features beyond reasoning effort, use provider-keyed extensions. Auriko forwards these to the provider: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Hello!"}], extra_body={ "reasoning_effort": "high", "extensions": { "anthropic": { "metadata": {"user_id": "user-123"} } } } ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages: [{ role: "user", content: "Hello!" }], reasoning_effort: "high", extensions: { anthropic: { metadata: { user_id: "user-123" }, }, }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Hello!"}], reasoning_effort="high", extensions={ "anthropic": { "metadata": {"user_id": "user-123"} } } ) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages: [{ role: "user", content: "Hello!" }], reasoning_effort: "high", extensions: { anthropic: { metadata: { user_id: "user-123" }, }, }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "messages": [{"role": "user", "content": "Hello!"}], "reasoning_effort": "high", "extensions": {"anthropic": {"metadata": {"user_id": "user-123"}}} }' ``` Auriko normalizes provider aliases. `google`, `google_ai`, `googleai`, and `gemini` are interchangeable. ### Transform-controlled fields If you set `reasoning_effort`, Auriko controls each provider's thinking budget. Thinking-budget parameters in `extensions` are overwritten. If you don't set `reasoning_effort`, your passthrough values are preserved. ### Passthrough fields Fields that aren't transform-controlled pass through to the provider unchanged. Examples: - Anthropic: `metadata` - OpenAI: `store`, `metadata` - Google Gemini: `safety_settings` --- ## Page: Extensions and Thinking > Section: Handle sampling constraints On Anthropic models, `temperature`, `top_p`, and `top_k` are incompatible with active thinking. If you send `reasoning_effort` alongside these parameters, Auriko drops the incompatible values and returns a warning in `routing_metadata.warnings`: ```json { "type": "unsupported_parameter", "code": "temperature", "message": "temperature dropped — incompatible with thinking on anthropic (must be exactly 1 or unset)" } ``` Anthropic's constraints when thinking is active: | Parameter | Constraint | |-----------|-----------| | `temperature` | Must be exactly `1`, or omitted | | `top_p` | Must be `>= 0.95`, or omitted | | `top_k` | Must be omitted | Values within these bounds pass through unchanged. Other providers don't enforce these constraints. --- ## Page: Extensions and Thinking > Section: Check effort normalization Some models support only a subset of `reasoning_effort` levels. If you request a level above the model's maximum, Auriko normalizes it to the highest supported value and includes a warning in `routing_metadata.warnings`: ```json { "type": "unsupported_parameter", "code": "reasoning_effort", "message": "reasoning_effort adjusted to 'high' — exceeds model maximum on openai" } ``` | Provider | Models affected | `xhigh`/`max` normalized to | |----------|----------------|----------------------------| | OpenAI | GPT-5, GPT-5 mini, o3-pro | `high` | | Anthropic | Claude Opus 4.5 | `high` | | xAI | Grok 4.3 | `high` | | xAI | Grok 3 mini | `high` | | Google | Gemini 3.x | `high` | Models not listed above accept `xhigh` and `max` without a warning. For the full provider support table, see [Check provider support](#check-provider-support). --- ## Page: Extensions and Thinking > Section: Handle `max_tokens` constraints Anthropic models that use thinking budgets require `max_tokens` above 1024. If you send `reasoning_effort` with `max_tokens` at or below 1024, Auriko skips thinking and returns a warning in `routing_metadata.warnings`: ```json { "type": "unsupported_parameter", "code": "reasoning_effort", "message": "reasoning_effort dropped — max_tokens (200) is below the 1025 minimum required for thinking on anthropic" } ``` Claude 4.6+ models use adaptive thinking rather than thinking budgets. For the full model list, see [Check provider support](#check-provider-support). --- ## Page: Extensions and Thinking > Section: Estimate cost and latency The `reasoning_effort` level (low/medium/high/xhigh/max) determines the thinking budget per provider. Exact token budgets aren't guaranteed; `reasoning_effort="off"` disables thinking on supported models. See [Check reasoning token availability](#check-reasoning-token-availability) for which providers report a breakdown. --- ## Page: Extensions and Thinking > Section: Check reasoning token availability The `completion_tokens_details.reasoning_tokens` field reports how many tokens the model spent on reasoning. Auriko passes through what the upstream provider reports. | Provider | Model examples | `reasoning_tokens` reported? | Notes | |----------|---------------|----------------------------|-------| | OpenAI | o1, o3, o4-mini | Yes | Native field | | DeepSeek | deepseek-v4-flash, deepseek-v4-pro | Yes | Native field | | xAI | grok-4-fast-reasoning | Yes | Native field | | Google | Gemini 2.5 Flash | Yes | Derived from provider token counts | | Anthropic | All Claude models | No | Reports combined output tokens only | | Moonshot | kimi-k2-thinking, kimi-k2-thinking-turbo | No | Token breakdown not reported | | Fireworks | deepseek-v3.2 | No | Token breakdown not reported for hosted models | When the provider doesn't report a reasoning token breakdown, Auriko doesn't include `completion_tokens_details` in the response. Check for the field before accessing it: ```python Python OpenAI if response.usage.completion_tokens_details: print(f"Reasoning: {response.usage.completion_tokens_details.reasoning_tokens}") ``` ```typescript TypeScript OpenAI if (response.usage?.completion_tokens_details) { console.log(`Reasoning: ${(response.usage.completion_tokens_details as any).reasoning_tokens}`); } ``` ```python Python Auriko if response.usage.completion_tokens_details: print(f"Reasoning: {response.usage.completion_tokens_details.reasoning_tokens}") ``` ```typescript TypeScript Auriko if (response.usage?.completion_tokens_details) { console.log(`Reasoning: ${response.usage.completion_tokens_details.reasoning_tokens}`); } ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "deepseek-v4-flash", "messages": [{"role": "user", "content": "Think about this."}], "reasoning_effort": "high" }' ``` When `completion_tokens_details` isn't available, `completion_tokens` reflects the combined total of reasoning and content tokens. You can still use it for cost tracking. --- ## Page: Advanced Routing Fine-tune routing with suffix shortcuts, multi-model requests, quality constraints, and data policies. For basic routing, see [Routing options](/guides/routing-options). --- ## Page: Advanced Routing > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) - Familiarity with [Routing options](/guides/routing-options) --- ## Page: Advanced Routing > Section: How routing works When you send a request, Auriko's router: 1. **Enumerates candidates** — finds all providers offering the requested model(s) 2. **Filters by constraints** — removes providers that violate your routing options (data policy, Bring Your Own Key (BYOK) requirement, performance constraints, excluded providers) 3. **Scores by strategy** — ranks remaining candidates using your `optimize` strategy: - `cost`: Cost-optimized, well-rounded - `cost-focus`: Aggressively minimize cost (default) - `ttft`: TTFT-optimized, well-rounded - `ttft-focus`: Aggressively minimize time to first token - `tps`: Throughput-optimized, well-rounded - `tps-focus`: Aggressively maximize tokens per second - `balanced`: All dimensions weighted evenly 4. **Selects and routes** — selects from the ranked list, favoring higher-scored providers 5. **Falls back if needed** — if the provider fails and `allow_fallbacks` is true, retries with the next candidate (up to `max_fallback_attempts`) See [Python SDK](/sdk/python#with-routing-options) or [TypeScript SDK](/sdk/typescript#with-routing-options) for routing code examples. --- ## Page: Advanced Routing > Section: Use suffix shortcuts Append a suffix to any model name for quick routing configuration: | Suffix | Strategy | Description | |--------|----------|-------------| | `:cost-focus` | `cost-focus` | Aggressively minimize cost | | `:cost` | `cost` | Cost-optimized, well-rounded | | `:ttft-focus` | `ttft-focus` | Aggressively minimize time to first token | | `:ttft` | `ttft` | TTFT-optimized, well-rounded | | `:tps-focus` | `tps-focus` | Aggressively maximize tokens per second | | `:tps` | `tps` | Throughput-optimized, well-rounded | | `:balanced` | `balanced` | All dimensions weighted evenly | ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-4o:cost-focus", messages=[{"role": "user", "content": "Hello!"}] ) response = client.chat.completions.create( model="claude-sonnet-4-20250514:ttft", messages=[{"role": "user", "content": "Hello!"}] ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o:cost", messages: [{ role: "user", content: "Hello!" }], }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) # Cost-focused provider for gpt-4o response = client.chat.completions.create( model="gpt-4o:cost-focus", messages=[{"role": "user", "content": "Hello!"}] ) # Fastest time to first token response = client.chat.completions.create( model="claude-sonnet-4-20250514:ttft", messages=[{"role": "user", "content": "Hello!"}] ) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); // Cost-focused provider for gpt-4o const response = await client.chat.completions.create({ model: "gpt-4o:cost-focus", messages: [{ role: "user", content: "Hello!" }], }); // Fastest time to first token const fast = await client.chat.completions.create({ model: "claude-sonnet-4-20250514:ttft", messages: [{ role: "user", content: "Hello!" }], }); ``` Suffixes work with any HTTP client: ```bash curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o:cost-focus", "messages": [{"role": "user", "content": "Hello!"}] }' ``` The router parses suffixes only when the model ID contains exactly one colon. Fine-tuned models with multiple colons (for example, `ft:gpt-4o:org:custom`) pass through unchanged. --- ## Page: Advanced Routing > Section: Route across models Pass `gateway.models` instead of `model` to route across multiple models (mutually exclusive with `model`, max 10): ```python Python OpenAI response = client.chat.completions.create( model="", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": { "models": ["gpt-4o", "claude-sonnet-4-20250514", "gemini-flash-latest"], "routing": {"mode": "pool"} }} ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "", messages: [{ role: "user", content: "Hello!" }], gateway: { models: ["gpt-4o", "claude-sonnet-4-20250514", "gemini-flash-latest"], routing: { mode: "pool" }, }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) # Pool mode (default): best provider across all models response = client.chat.completions.create( messages=[{"role": "user", "content": "Hello!"}], gateway={"models": ["gpt-4o", "claude-sonnet-4-20250514", "gemini-flash-latest"], "routing": {"mode": "pool"}} ) # Fallback mode: try models in order response = client.chat.completions.create( messages=[{"role": "user", "content": "Hello!"}], gateway={"models": ["gpt-4o", "claude-sonnet-4-20250514"], "routing": {"mode": "fallback"}} ) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); // Pool mode (default): best provider across all models const response = await client.chat.completions.create({ messages: [{ role: "user", content: "Hello!" }], gateway: { models: ["gpt-4o", "claude-sonnet-4-20250514", "gemini-flash-latest"], routing: { mode: "pool" } }, }); // Fallback mode: try models in order const fallback = await client.chat.completions.create({ messages: [{ role: "user", content: "Hello!" }], gateway: { models: ["gpt-4o", "claude-sonnet-4-20250514"], routing: { mode: "fallback" } }, }); ``` ```bash cURL # Pool mode (default): best provider across all models curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"models": ["gpt-4o", "claude-sonnet-4-20250514", "gemini-flash-latest"], "routing": {"mode": "pool"}} }' # Fallback mode: try models in order curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"models": ["gpt-4o", "claude-sonnet-4-20250514"], "routing": {"mode": "fallback"}} }' ``` | Mode | Behavior | |------|----------| | `pool` (default) | Select the best-scoring provider across all requested models | | `fallback` | Try all providers for the first model, then the second model, and so on | --- ## Page: Advanced Routing > Section: Set quality constraints Filter providers by performance requirements. Pass constraint ceilings under `gateway.routing`: | Field | Type | Description | |-------|------|-------------| | `max_cost_per_1m` | number | Maximum cost per 1 million tokens (USD) | | `max_ttft_ms` | integer | Maximum time to first token in milliseconds | | `min_throughput_tps` | number | Minimum throughput in tokens per second | ```python Python OpenAI response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": { "optimize": "balanced", "min_throughput_tps": 30, "weights": {"cost": 0.6, "ttft": 0.4} }}} ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "balanced", min_throughput_tps: 30, weights: { cost: 0.6, ttft: 0.4 }, } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], gateway={"routing": { "optimize": "balanced", "min_throughput_tps": 30, "weights": {"cost": 0.6, "ttft": 0.4} }} ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "balanced", min_throughput_tps: 30, weights: { cost: 0.6, ttft: 0.4 }, } }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"optimize": "balanced", "min_throughput_tps": 30, "weights": {"cost": 0.6, "ttft": 0.4}}} }' ``` Constraint ceilings (`max_ttft_ms`, `min_throughput_tps`, `max_cost_per_1m`) evaluate against median (p50) metrics. To rank providers by worst-case (p95) TTFT or throughput for scoring, set `ttft_percentile` or `throughput_percentile` — see [Choose metric percentile](#choose-metric-percentile). --- ## Page: Advanced Routing > Section: Filter by parameter support Not all providers support every optional parameter. By default, Auriko drops unsupported parameters and adds a warning to the response. Set `require_parameters` to `true` to only route to providers that accept the optional parameters you sent: ```python Python OpenAI response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], seed=42, extra_body={"gateway": {"routing": { "optimize": "cost", "require_parameters": True, }}} ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], top_p: 0.9, gateway: { routing: { optimize: "cost", require_parameters: true, } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], seed=42, gateway={ "routing": { "optimize": "cost", "require_parameters": True, }, } ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], seed: 42, gateway: { routing: { optimize: "cost", require_parameters: true, }, }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}], "seed": 42, "gateway": {"routing": {"optimize": "cost", "require_parameters": true}} }' ``` The following parameters have per-provider support. When you set `require_parameters` to `true`, Auriko checks that your provider supports each one you sent: `temperature`, `top_p`, `seed`, `logit_bias`, `logprobs`, `top_logprobs`, `n`, `presence_penalty`, `frequency_penalty`, `user`, `parallel_tool_calls`, `web_search_options`, `verbosity`, `prompt_cache_key`, `safety_identifier`. `require_parameters` composes with other constraints. A provider must pass all filters to be eligible. You can check which parameters each provider supports via the model directory endpoint, where each provider entry includes `accepted_params` and `supported_parameters` fields. Providers without parameter support data are excluded when `require_parameters` is `true`. This is fail-closed by design. --- ## Page: Advanced Routing > Section: Set custom weights You can override preset strategies with custom weights across three dimensions. | Dimension | Field | What it controls | |-----------|-------|------------------| | Cost | `cost` | Favor lower-cost providers | | Latency | `ttft` | Favor lower time-to-first-token | | Throughput | `throughput` | Favor higher tokens-per-second | Pass `routing.weights` with your desired dimensions: ```python Python OpenAI # Only cost matters response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": {"weights": {"cost": 1}}}} ) # Mostly cost, some latency response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": {"weights": {"cost": 0.7, "ttft": 0.3}}}} ) # All three dimensions response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": {"weights": {"cost": 0.85, "ttft": 0.5, "throughput": 0.5}}}} ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); // Only cost matters const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { weights: { cost: 1 } } }, }); // Mostly cost, some latency const response2 = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { weights: { cost: 0.7, ttft: 0.3 } } }, }); // All three dimensions const response3 = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { weights: { cost: 0.85, ttft: 0.5, throughput: 0.5 } } }, }); console.log(response3.choices[0].message.content); ``` ```python Python Auriko # Only cost matters response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], gateway={"routing": {"weights": {"cost": 1}}} ) # Mostly cost, some latency response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], gateway={"routing": {"weights": {"cost": 0.7, "ttft": 0.3}}} ) # All three dimensions response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], gateway={"routing": { "weights": { "cost": 0.85, "ttft": 0.5, "throughput": 0.5 } }} ) ``` ```typescript TypeScript Auriko // Only cost matters const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { weights: { cost: 1 } } }, }); // Mostly cost, some latency const response2 = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { weights: { cost: 0.7, ttft: 0.3 } } }, }); // All three dimensions const response3 = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { weights: { cost: 0.85, ttft: 0.5, throughput: 0.5, }, } }, }); ``` ```bash curl # Only cost matters curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"weights": {"cost": 1}}} }' # Mostly cost, some latency curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"weights": {"cost": 0.7, "ttft": 0.3}}} }' # All three dimensions curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": { "weights": { "cost": 0.85, "ttft": 0.5, "throughput": 0.5 } }} }' ``` - The server accepts any non-negative numbers and normalizes them proportionally. - Omitted dimensions default to 0. - At least one dimension must be greater than 0. - `weights` overrides the `optimize` preset, and the response metadata contains `routing_strategy: "custom"`. - To score using worst-case metrics, set `ttft_percentile` and/or `throughput_percentile` to `"p95"`. See [Choose metric percentile](#choose-metric-percentile). Providers approaching their rate limits are automatically deprioritized. --- ## Page: Advanced Routing > Section: Choose metric percentile By default, Auriko scores providers using median (p50) metrics. You can switch to 95th-percentile (worst-case) independently for TTFT and throughput: | Field | Controls | Default | |-------|----------|---------| | `ttft_percentile` | TTFT **scoring** (which providers rank higher) | `p50` | | `throughput_percentile` | Throughput **scoring** (which providers rank higher) | `p50` | Both scoring fields accept `"p50"` (median) or `"p95"` (worst-case). They work with presets and custom weights — no `weights` required. Example — rank providers by worst-case (p95) TTFT instead of median: ```python Python OpenAI response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": { "optimize": "ttft", "ttft_percentile": "p95" }}} ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "ttft", ttft_percentile: "p95", } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], gateway={"routing": { "optimize": "ttft", "ttft_percentile": "p95" }} ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "ttft", ttft_percentile: "p95", } }, }); ``` ```bash curl curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": { "optimize": "ttft", "ttft_percentile": "p95" }} }' ``` --- ## Page: Advanced Routing > Section: Data policy Control how providers handle your data: Auriko doesn't store prompts or responses. Set `data_policy: "zdr"` to route only to providers that satisfy zero data retention. | Policy | Description | |--------|-------------| | `none` (default) | No restrictions | | `no_training` | Provider must not use data for training | | `zdr` | Zero data retention — strictest policy | The hierarchy is `zdr` > `no_training` > `none`. When a per-request policy intersects with an account-level policy, the most restrictive one wins. ```python Python OpenAI response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Sensitive financial data..."}], extra_body={"gateway": {"routing": {"data_policy": "zdr"}}} ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { data_policy: "zdr" } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Sensitive financial data..."}], gateway={"routing": {"data_policy": "zdr"}} ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages: [{ role: "user", content: "Sensitive financial data..." }], gateway: { routing: { data_policy: "zdr" } }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "messages": [{"role": "user", "content": "Sensitive financial data..."}], "gateway": {"routing": {"data_policy": "zdr"}} }' ``` --- ## Page: Advanced Routing > Section: Opt in to premium tiers Premium-tier offerings are excluded from routing by default. Set `tier` to opt in: | Value | Effect | |-------|--------| | `"priority"` | Includes Anthropic Fast Mode offerings (2.5x speed, 6x cost) | | omitted (default) | Excludes premium-tier offerings | ```python Python OpenAI response = client.chat.completions.create( model="claude-opus-4-6", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": {"tier": "priority"}}} ) ``` ```typescript TypeScript OpenAI const response = await client.chat.completions.create({ model: "claude-opus-4-6", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { tier: "priority" } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="claude-opus-4-6", messages=[{"role": "user", "content": "Hello!"}], gateway={"routing": {"tier": "priority"}} ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "claude-opus-4-6", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { tier: "priority" } }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-opus-4-6", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"tier": "priority"}} }' ``` Auriko's "priority" tier refers to Anthropic Fast Mode, not Anthropic's separate Priority Tier (committed capacity SLA). See [Routing options](/guides/routing-options#opt-in-to-premium-tiers) for details. --- ## Page: Advanced Routing > Section: Provider alias normalization Provider names in `providers` and `exclude_providers` are case-insensitive and support aliases: | Alias | Canonical name | |-------|----------------| | `google`, `google_ai`, `googleai`, `gemini` | `google_ai_studio` | | `fireworks` | `fireworks_ai` | | `together` | `together_ai` | Unrecognized names pass through as-is (lowercased). --- ## Page: Advanced Routing > Section: Configure fallbacks By default, Auriko retries with alternative providers on 429 (rate limit), 5xx (server error), and timeout responses. | Setting | Default | Description | |---------|---------|-------------| | `allow_fallbacks` | `true` | Enable automatic fallback to alternative providers | | `max_fallback_attempts` | 19 | Safety ceiling on fallback attempts beyond the primary, range 1-19 (chain length 20 total) | | `timeout_ms` | `120000` (streaming) / `300000` (non-streaming) | Per-attempt timeout in milliseconds. For streaming: time to first byte. For non-streaming: time to complete response | | `deadline_ms` | None (streaming) / `1080000` (non-streaming) | Hard wall-clock cap across all fallback attempts. Non-streaming requests default to 18 minutes; streaming has no default (connections are long-lived). Set explicitly to override | You can configure per-attempt timeouts with `timeout_ms`. To set a hard wall-clock cap across all fallback attempts, use `deadline_ms`. Non-streaming requests have an 18-minute default deadline; streaming requests have no deadline (opt-in only). If the deadline is exceeded, the request fails with a timeout error. ```python Python OpenAI response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": { "allow_fallbacks": True, "max_fallback_attempts": 5 }}} ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { allow_fallbacks: true, max_fallback_attempts: 5, } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], gateway={"routing": { "allow_fallbacks": True, "max_fallback_attempts": 5 }} ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { allow_fallbacks: true, max_fallback_attempts: 5, } }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"allow_fallbacks": true, "max_fallback_attempts": 5}} }' ``` === # Auriko Response API ## Page: Overview `POST /v1/responses` accepts a string or structured input and returns typed output items instead of a messages array. Auriko routes these requests across providers, and routing features (multi-model, cost optimization, and extensions) work with both endpoints. If you're building with the OpenAI SDK's `client.responses.create()`, use this endpoint. The Response API is in preview. The interface may change before GA. --- ## Page: Overview > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the Auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) --- ## Page: Overview > Section: Send requests Send a request and read the output text: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="gpt-4o", input="What is the capital of France?" ) print(response.output_text) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "gpt-4o", input: "What is the capital of France?", }); console.log(response.output_text); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="gpt-4o", input="What is the capital of France?" ) print(response.output_text) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "gpt-4o", input: "What is the capital of France?", }); console.log(response.output_text); ``` ```bash cURL curl https://api.auriko.ai/v1/responses \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "input": "What is the capital of France?" }' ``` --- ## Page: Overview > Section: Check model support Chat models work with both `/v1/chat/completions` and `/v1/responses`. Some models are only available via the Response API — OpenAI pro-tier models such as `gpt-5.5-pro` and `o3-pro` — and chat-format requests to them return [`response_api_only`](/errors/response_api_only). To check endpoint support for a model, read `supported_endpoints` on each provider entry in the [model directory](/api-reference/model-directory), or list every Response-API-callable model directly: ```bash curl "https://api.auriko.ai/v1/models?endpoint=responses" \ -H "Authorization: Bearer $AURIKO_API_KEY" ``` --- ## Page: Overview > Section: Map parameters If you're migrating existing Chat Completions code, use this table to translate parameter names: | Concept | Chat Completions | Response API | |---------|-----------------|-------------| | Input | `messages` array | `input` (string or items) | | System prompt | `messages[0].role: "system"` | `instructions` parameter | | Output | `choices[].message` | `output` items array | | Output text | `choices[0].message.content` | `output_text` | | Structured output | `response_format` | `text.format` | | Reasoning | `reasoning_effort` top-level | `reasoning.effort` nested | | Tool definition | `{type: "function", function: {name, parameters}}` | `{type: "function", name, parameters}` | | Tool results | `{role: "tool", tool_call_id, content}` | `{type: "function_call_output", call_id, output}` | | Usage fields | `prompt_tokens` / `completion_tokens` | `input_tokens` / `output_tokens` | --- ## Page: Overview > Section: Check feature support | Feature | Status | |---------|--------| | Text completions | Supported | | Streaming | Supported (18 SSE event types) | | Tool calling | Supported (function tools) | | Structured output | Supported (`json_schema`, `json_object`) | | Vision | Supported (image URLs in input) | | Multi-model routing | Supported (`gateway.models`) | | Prompt caching | Supported (`prompt_cache_key`) | | Token logprobs | Supported (`top_logprobs`) | | Reasoning | Supported (`reasoning.effort`); [summary availability varies by model](/response-api/reasoning#access-reasoning-summaries) | | `store` | Returns 400 `operation_not_allowed` | | `previous_response_id` | Returns 400 `operation_not_allowed` | | File/audio inputs | Returns 400 `operation_not_allowed` | | Background execution | Returns 400 `operation_not_allowed` | --- ## Page: Overview > Section: Resources Stream events as they're generated Call functions with the input/output item format Constrain output to a JSON Schema Control reasoning effort and access summaries Multi-model routing and provider extensions Full endpoint specification Python SDK reference for responses TypeScript SDK reference for responses --- ## Page: Streaming Each streaming event carries a `type` field like `response.output_text.delta` or `response.completed`. The stream ends with a terminal event (`response.completed`, `response.incomplete`, or `response.failed`) instead of a `data: [DONE]` sentinel. --- ## Page: Streaming > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the Auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) --- ## Page: Streaming > Section: Stream text Stream a response and print each text token: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) stream = client.responses.create( model="gpt-4o", input="Count from 1 to 10", stream=True ) for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="", flush=True) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const stream = await client.responses.create({ model: "gpt-4o", input: "Count from 1 to 10", stream: true, }); for await (const event of stream) { if (event.type === "response.output_text.delta") { process.stdout.write(event.delta); } } ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) stream = client.responses.create( model="gpt-4o", input="Count from 1 to 10", stream=True ) for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="", flush=True) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const stream = await client.responses.create({ model: "gpt-4o", input: "Count from 1 to 10", stream: true, }); for await (const event of stream) { if (event.type === "response.output_text.delta") { process.stdout.write(event.delta); } } ``` ```bash cURL curl --no-buffer https://api.auriko.ai/v1/responses \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "input": "Count from 1 to 10", "stream": true }' ``` --- ## Page: Streaming > Section: Handle event types A basic text response emits events in this order: `response.created` → `response.in_progress` → `response.output_item.added` → `response.content_part.added` → `response.output_text.delta` (repeated) → `response.output_text.done` → `response.content_part.done` → `response.output_item.done` → `response.completed` ### Lifecycle events | Event | Description | |-------|-------------| | `response.created` | Response object created, status is `in_progress` | | `response.in_progress` | Processing has started | | `response.completed` | Response finished, includes final `response` object | | `response.incomplete` | Response stopped early (token limit, content filter) | | `response.failed` | Response failed, includes error details | ### Content events | Event | Description | |-------|-------------| | `response.output_item.added` | New output item started (text, function call, or reasoning) | | `response.output_item.done` | Output item finished | | `response.content_part.added` | New content part within an output item | | `response.content_part.done` | Content part finished | | `response.output_text.delta` | Text chunk, access via `event.delta` | | `response.output_text.done` | Text output complete, access full text via `event.text` | ### Reasoning events | Event | Description | |-------|-------------| | `response.reasoning_summary_part.added` | Reasoning summary part started | | `response.reasoning_summary_part.done` | Reasoning summary part finished | | `response.reasoning_summary_text.delta` | Reasoning summary text chunk | | `response.reasoning_summary_text.done` | Reasoning summary text complete | ### Tool call events | Event | Description | |-------|-------------| | `response.function_call_arguments.delta` | Function call arguments chunk | | `response.function_call_arguments.done` | Function call arguments complete | ### Error event | Event | Description | |-------|-------------| | `error` | Stream-level error | Terminal events (`response.completed`, `response.incomplete`, `response.failed`) carry the final `response` object with `usage` and `routing_metadata`. --- ## Page: Streaming > Section: Access completed response You can read `response_headers` before iterating. After iteration, the stream exposes the terminal event's full response object. ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) stream = client.responses.create( model="gpt-4o", input="What is 2 + 2?", stream=True ) completed = None for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="", flush=True) elif event.type == "response.completed": completed = event.response print(f"\nModel: {completed.model}") print(f"Usage: {completed.usage.input_tokens} in, {completed.usage.output_tokens} out") ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const stream = await client.responses.create({ model: "gpt-4o", input: "What is 2 + 2?", stream: true, }); let completed: any = null; for await (const event of stream) { if (event.type === "response.output_text.delta") { process.stdout.write(event.delta); } else if (event.type === "response.completed") { completed = event.response; } } console.log(`\nModel: ${completed?.model}`); console.log(`Usage: ${completed?.usage?.input_tokens} in, ${completed?.usage?.output_tokens} out`); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) stream = client.responses.create( model="gpt-4o", input="What is 2 + 2?", stream=True ) for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="", flush=True) final = stream.completed_response print(f"\nModel: {final.model}") print(f"Tokens: {final.usage.total_tokens}") print(f"Provider: {final.routing_metadata.provider}") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const stream = await client.responses.create({ model: "gpt-4o", input: "What is 2 + 2?", stream: true, }); for await (const event of stream) { if (event.type === "response.output_text.delta") { process.stdout.write(event.delta); } } const final = stream.completedResponse; console.log(`\nModel: ${final?.model}`); console.log(`Tokens: ${final?.usage?.total_tokens}`); console.log(`Provider: ${final?.routing_metadata?.provider}`); ``` cURL streams raw SSE events. See [Read raw SSE](#read-raw-sse) for parsing terminal events. For routing metadata with the OpenAI SDK, see [OpenAI Compatibility](/openai-compatibility#access-routing-metadata). --- ## Page: Streaming > Section: Stream asynchronously Stream with the async client: ```python Python OpenAI import os import asyncio from openai import AsyncOpenAI async def stream_response(): client = AsyncOpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) stream = await client.responses.create( model="gpt-4o", input="Write a haiku about code", stream=True ) async for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="", flush=True) asyncio.run(stream_response()) ``` ```python Python Auriko import os import asyncio from auriko import AsyncClient async def stream_response(): client = AsyncClient( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) stream = await client.responses.create( model="gpt-4o", input="Write a haiku about code", stream=True ) async for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="", flush=True) asyncio.run(stream_response()) ``` TypeScript's SDK is inherently async. See the [Stream text](#stream-text) example above. --- ## Page: Streaming > Section: Read raw SSE The raw wire format uses `event:` and `data:` lines. A basic text response looks like this: ``` event: response.created data: {"type":"response.created","response":{"id":"resp_abc123","object":"response","status":"in_progress",...}} event: response.in_progress data: {"type":"response.in_progress","response":{"id":"resp_abc123","object":"response","status":"in_progress",...}} event: response.output_item.added data: {"type":"response.output_item.added","output_index":0,"item":{"type":"message","role":"assistant","content":[]}} event: response.output_text.delta data: {"type":"response.output_text.delta","output_index":0,"content_index":0,"delta":"The"} event: response.output_text.delta data: {"type":"response.output_text.delta","output_index":0,"content_index":0,"delta":" capital"} event: response.completed data: {"type":"response.completed","response":{"id":"resp_abc123","object":"response","status":"completed","output":[...],"usage":{...},"routing_metadata":{...}}} ``` See [Chat Completions streaming](/guides/streaming) for the `data: [DONE]` format used by the other endpoint. See [Error Handling](/guides/error-handling) for error recovery patterns. --- ## Page: Tool Calling Tool definitions use a flat schema (`{type, name, parameters}`) and tool results are `function_call_output` input items with a `call_id` and `output` string. --- ## Page: Tool Calling > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the Auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) --- ## Page: Tool Calling > Section: Define tools Define a tool with the flat schema format: ```python Python OpenAI tools = [ { "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } } ] ``` ```typescript TypeScript OpenAI const tools = [ { type: "function" as const, name: "get_weather", description: "Get current weather for a location", parameters: { type: "object", properties: { location: { type: "string", description: "City name" }, }, required: ["location"], }, }, ]; ``` ```python Python Auriko tools = [ { "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } } ] ``` ```typescript TypeScript Auriko const tools = [ { type: "function" as const, name: "get_weather", description: "Get current weather for a location", parameters: { type: "object", properties: { location: { type: "string", description: "City name" }, }, required: ["location"], }, }, ]; ``` Chat Completions wraps these fields under a nested `function` key. --- ## Page: Tool Calling > Section: Call tools Send a request with tools and inspect the `function_call` output item: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="gpt-4o", input="What's the weather in Tokyo?", tools=[{ "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } }] ) for item in response.output: if item.type == "function_call": print(f"Function: {item.name}") print(f"Arguments: {item.arguments}") print(f"Call ID: {item.call_id}") ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "gpt-4o", input: "What's the weather in Tokyo?", tools: [{ type: "function", name: "get_weather", description: "Get current weather for a location", parameters: { type: "object", properties: { location: { type: "string", description: "City name" }, }, required: ["location"], }, }], }); for (const item of response.output) { if (item.type === "function_call") { console.log(`Function: ${item.name}`); console.log(`Arguments: ${item.arguments}`); console.log(`Call ID: ${item.call_id}`); } } ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="gpt-4o", input="What's the weather in Tokyo?", tools=[{ "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } }] ) for item in response.output: if item.type == "function_call": print(f"Function: {item.name}") print(f"Arguments: {item.arguments}") print(f"Call ID: {item.call_id}") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "gpt-4o", input: "What's the weather in Tokyo?", tools: [{ type: "function", name: "get_weather", description: "Get current weather for a location", parameters: { type: "object", properties: { location: { type: "string", description: "City name" }, }, required: ["location"], }, }], }); for (const item of response.output) { if (item.type === "function_call") { console.log(`Function: ${item.name}`); console.log(`Arguments: ${item.arguments}`); console.log(`Call ID: ${item.call_id}`); } } ``` ```bash cURL curl https://api.auriko.ai/v1/responses \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "input": "What'\''s the weather in Tokyo?", "tools": [{ "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } }] }' ``` --- ## Page: Tool Calling > Section: Submit tool results Match the `call_id` from the function call output to link your result back to the original request: ```python Python OpenAI import os import json from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) tools = [{ "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } }] response = client.responses.create( model="gpt-4o", input="What's the weather in Tokyo?", tools=tools ) tool_call = next(item for item in response.output if item.type == "function_call") final = client.responses.create( model="gpt-4o", input=[ {"type": "message", "role": "user", "content": "What's the weather in Tokyo?"}, {"type": "function_call", "name": tool_call.name, "call_id": tool_call.call_id, "arguments": tool_call.arguments}, {"type": "function_call_output", "call_id": tool_call.call_id, "output": json.dumps({"temperature": "22°C", "condition": "Sunny"})} ], tools=tools ) print(final.output_text) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const tools = [{ type: "function" as const, name: "get_weather", description: "Get current weather for a location", parameters: { type: "object", properties: { location: { type: "string", description: "City name" }, }, required: ["location"], }, }]; const response = await client.responses.create({ model: "gpt-4o", input: "What's the weather in Tokyo?", tools, }); const toolCall = response.output.find((item) => item.type === "function_call"); const final = await client.responses.create({ model: "gpt-4o", input: [ { type: "message", role: "user", content: "What's the weather in Tokyo?" }, { type: "function_call", name: toolCall.name, call_id: toolCall.call_id, arguments: toolCall.arguments }, { type: "function_call_output", call_id: toolCall.call_id, output: JSON.stringify({ temperature: "22°C", condition: "Sunny" }) }, ], tools, }); console.log(final.output_text); ``` ```python Python Auriko import os import json from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) tools = [{ "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } }] response = client.responses.create( model="gpt-4o", input="What's the weather in Tokyo?", tools=tools ) tool_call = next(item for item in response.output if item.type == "function_call") final = client.responses.create( model="gpt-4o", input=[ {"type": "message", "role": "user", "content": "What's the weather in Tokyo?"}, {"type": "function_call", "name": tool_call.name, "call_id": tool_call.call_id, "arguments": tool_call.arguments}, {"type": "function_call_output", "call_id": tool_call.call_id, "output": json.dumps({"temperature": "22°C", "condition": "Sunny"})} ], tools=tools ) print(final.output_text) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const tools = [{ type: "function" as const, name: "get_weather", description: "Get current weather for a location", parameters: { type: "object", properties: { location: { type: "string", description: "City name" }, }, required: ["location"], }, }]; const response = await client.responses.create({ model: "gpt-4o", input: "What's the weather in Tokyo?", tools, }); const toolCall = response.output.find((item) => item.type === "function_call"); const final = await client.responses.create({ model: "gpt-4o", input: [ { type: "message", role: "user", content: "What's the weather in Tokyo?" }, { type: "function_call", name: toolCall.name, call_id: toolCall.call_id, arguments: toolCall.arguments }, { type: "function_call_output", call_id: toolCall.call_id, output: JSON.stringify({ temperature: "22°C", condition: "Sunny" }) }, ], tools, }); console.log(final.output_text); ``` Submitting tool results requires state from a previous response. For single-request examples, see [Call tools](#call-tools). --- ## Page: Tool Calling > Section: Use parallel tool calls Set `parallel_tool_calls: true` to let the model call multiple functions in one response: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="gpt-4o", input="What's the weather in Tokyo and London?", tools=[{ "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } }], parallel_tool_calls=True ) for item in response.output: if item.type == "function_call": print(f"{item.name}({item.arguments})") ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "gpt-4o", input: "What's the weather in Tokyo and London?", tools: [{ type: "function", name: "get_weather", description: "Get current weather for a location", parameters: { type: "object", properties: { location: { type: "string", description: "City name" }, }, required: ["location"], }, }], parallel_tool_calls: true, }); for (const item of response.output) { if (item.type === "function_call") { console.log(`${item.name}(${item.arguments})`); } } ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="gpt-4o", input="What's the weather in Tokyo and London?", tools=[{ "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } }], parallel_tool_calls=True ) for item in response.output: if item.type == "function_call": print(f"{item.name}({item.arguments})") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "gpt-4o", input: "What's the weather in Tokyo and London?", tools: [{ type: "function", name: "get_weather", description: "Get current weather for a location", parameters: { type: "object", properties: { location: { type: "string", description: "City name" }, }, required: ["location"], }, }], parallel_tool_calls: true, }); for (const item of response.output) { if (item.type === "function_call") { console.log(`${item.name}(${item.arguments})`); } } ``` ```bash cURL curl https://api.auriko.ai/v1/responses \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "input": "What'\''s the weather in Tokyo and London?", "tools": [{ "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } }], "parallel_tool_calls": true }' ``` --- ## Page: Tool Calling > Section: Control tool choice Set `tool_choice` to control when the model calls tools: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="gpt-4o", input="What's the weather in Tokyo?", tools=[{ "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } }], tool_choice="required" ) print(response.output[0].type) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "gpt-4o", input: "What's the weather in Tokyo?", tools: [{ type: "function", name: "get_weather", description: "Get current weather for a location", parameters: { type: "object", properties: { location: { type: "string", description: "City name" }, }, required: ["location"], }, }], tool_choice: "required", }); console.log(response.output[0].type); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="gpt-4o", input="What's the weather in Tokyo?", tools=[{ "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } }], tool_choice="required" ) print(response.output[0].type) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "gpt-4o", input: "What's the weather in Tokyo?", tools: [{ type: "function", name: "get_weather", description: "Get current weather for a location", parameters: { type: "object", properties: { location: { type: "string", description: "City name" }, }, required: ["location"], }, }], tool_choice: "required", }); console.log(response.output[0].type); ``` ```bash cURL curl https://api.auriko.ai/v1/responses \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "input": "What'\''s the weather in Tokyo?", "tools": [{ "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } }], "tool_choice": "required" }' ``` `"auto"` (default) lets the model decide. `"required"` forces a tool call. `"none"` prevents tool calls. `{"type": "function", "name": "get_weather"}` forces a specific function. See [Tool Calling guide](/guides/tool-calling) for provider-specific behavior. --- ## Page: Tool Calling > Section: Stream tool calls Stream function call arguments as they're generated: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) stream = client.responses.create( model="gpt-4o", input="What's the weather in Tokyo?", tools=[{ "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } }], stream=True ) for event in stream: if event.type == "response.function_call_arguments.delta": print(event.delta, end="", flush=True) elif event.type == "response.function_call_arguments.done": print(f"\nComplete: {event.arguments}") ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const stream = await client.responses.create({ model: "gpt-4o", input: "What's the weather in Tokyo?", tools: [{ type: "function", name: "get_weather", description: "Get current weather for a location", parameters: { type: "object", properties: { location: { type: "string", description: "City name" }, }, required: ["location"], }, }], stream: true, }); for await (const event of stream) { if (event.type === "response.function_call_arguments.delta") { process.stdout.write(event.delta); } else if (event.type === "response.function_call_arguments.done") { console.log(`\nComplete: ${event.arguments}`); } } ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) stream = client.responses.create( model="gpt-4o", input="What's the weather in Tokyo?", tools=[{ "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } }], stream=True ) for event in stream: if event.type == "response.function_call_arguments.delta": print(event.delta, end="", flush=True) elif event.type == "response.function_call_arguments.done": print(f"\nComplete: {event.arguments}") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const stream = await client.responses.create({ model: "gpt-4o", input: "What's the weather in Tokyo?", tools: [{ type: "function", name: "get_weather", description: "Get current weather for a location", parameters: { type: "object", properties: { location: { type: "string", description: "City name" }, }, required: ["location"], }, }], stream: true, }); for await (const event of stream) { if (event.type === "response.function_call_arguments.delta") { process.stdout.write(event.delta); } else if (event.type === "response.function_call_arguments.done") { console.log(`\nComplete: ${event.arguments}`); } } ``` ```bash cURL curl --no-buffer https://api.auriko.ai/v1/responses \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "input": "What'\''s the weather in Tokyo?", "tools": [{ "type": "function", "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } }], "stream": true }' ``` The `.done` event includes the complete `arguments` string, so you don't need to reassemble delta chunks. --- ## Page: Structured Output Pass a JSON Schema in `text.format` to constrain the model's output to a specific structure. The schema definition matches Chat Completions; the parameter path differs. --- ## Page: Structured Output > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the Auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) --- ## Page: Structured Output > Section: Return JSON Constrain the output to valid JSON: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="gpt-4o", input="List 3 programming languages and their main use cases. Respond in JSON.", text={"format": {"type": "json_object"}} ) print(response.output_text) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "gpt-4o", input: "List 3 programming languages and their main use cases. Respond in JSON.", text: { format: { type: "json_object" } }, }); console.log(response.output_text); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="gpt-4o", input="List 3 programming languages and their main use cases. Respond in JSON.", text={"format": {"type": "json_object"}} ) print(response.output_text) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "gpt-4o", input: "List 3 programming languages and their main use cases. Respond in JSON.", text: { format: { type: "json_object" } }, }); console.log(response.output_text); ``` ```bash cURL curl https://api.auriko.ai/v1/responses \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "input": "List 3 programming languages and their main use cases. Respond in JSON.", "text": {"format": {"type": "json_object"}} }' ``` Include the word "JSON" in your prompt or instructions. Some models return non-JSON output without it, matching the Chat Completions `json_object` constraint. --- ## Page: Structured Output > Section: Enforce schema Constrain the output to a specific JSON Schema: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="gpt-4o", input="Extract contact info: John Doe, john@example.com, 555-0123", text={ "format": { "type": "json_schema", "name": "contact_info", "strict": True, "schema": { "type": "object", "properties": { "name": {"type": "string"}, "email": {"type": "string"}, "phone": {"type": "string"} }, "required": ["name", "email", "phone"], "additionalProperties": False } } } ) print(response.output_text) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "gpt-4o", input: "Extract contact info: John Doe, john@example.com, 555-0123", text: { format: { type: "json_schema", name: "contact_info", strict: true, schema: { type: "object", properties: { name: { type: "string" }, email: { type: "string" }, phone: { type: "string" }, }, required: ["name", "email", "phone"], additionalProperties: false, }, }, }, }); console.log(response.output_text); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="gpt-4o", input="Extract contact info: John Doe, john@example.com, 555-0123", text={ "format": { "type": "json_schema", "name": "contact_info", "strict": True, "schema": { "type": "object", "properties": { "name": {"type": "string"}, "email": {"type": "string"}, "phone": {"type": "string"} }, "required": ["name", "email", "phone"], "additionalProperties": False } } } ) print(response.output_text) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "gpt-4o", input: "Extract contact info: John Doe, john@example.com, 555-0123", text: { format: { type: "json_schema", name: "contact_info", strict: true, schema: { type: "object", properties: { name: { type: "string" }, email: { type: "string" }, phone: { type: "string" }, }, required: ["name", "email", "phone"], additionalProperties: false, }, }, }, }); console.log(response.output_text); ``` ```bash cURL curl https://api.auriko.ai/v1/responses \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "input": "Extract contact info: John Doe, john@example.com, 555-0123", "text": { "format": { "type": "json_schema", "name": "contact_info", "strict": true, "schema": { "type": "object", "properties": { "name": {"type": "string"}, "email": {"type": "string"}, "phone": {"type": "string"} }, "required": ["name", "email", "phone"], "additionalProperties": false } } } }' ``` --- ## Page: Structured Output > Section: Map parameters | Chat Completions | Response API | |-----------------|-------------| | `response_format: {type: "json_object"}` | `text: {format: {type: "json_object"}}` | | `response_format: {type: "json_schema", json_schema: {name, schema}}` | `text: {format: {type: "json_schema", name, schema}}` | In Chat Completions, the schema lives under `json_schema.schema`. In the Response API, it's under `format.schema` (one less level of nesting). See [Structured Output guide](/guides/structured-output) for model support and `strict` parameter behavior. --- ## Page: Reasoning The `reasoning` parameter accepts `effort` and `summary` fields. Reasoning output appears as dedicated `reasoning` output items separate from text content. --- ## Page: Reasoning > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the Auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) --- ## Page: Reasoning > Section: Set reasoning effort Set the reasoning effort level: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="claude-sonnet-4-6", input="What is the derivative of x^3 + 2x^2 - 5x + 3?", reasoning={"effort": "high"} ) print(response.output_text) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "claude-sonnet-4-6", input: "What is the derivative of x^3 + 2x^2 - 5x + 3?", reasoning: { effort: "high" }, }); console.log(response.output_text); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="claude-sonnet-4-6", input="What is the derivative of x^3 + 2x^2 - 5x + 3?", reasoning={"effort": "high"} ) print(response.output_text) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "claude-sonnet-4-6", input: "What is the derivative of x^3 + 2x^2 - 5x + 3?", reasoning: { effort: "high" }, }); console.log(response.output_text); ``` ```bash cURL curl https://api.auriko.ai/v1/responses \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "input": "What is the derivative of x^3 + 2x^2 - 5x + 3?", "reasoning": {"effort": "high"} }' ``` Effort levels: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, `off`. Auriko normalizes these levels across providers. See [Extensions and Thinking](/guides/extensions-and-thinking#check-provider-support) for the provider support table. --- ## Page: Reasoning > Section: Access reasoning summaries Request reasoning summaries with the `summary` field (or its alias `generate_summary`): ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="o3-mini", input="Explain why the sky is blue", reasoning={"effort": "high", "summary": "detailed"} ) for item in response.output: if item.type == "reasoning": for block in item.summary: print(f"Reasoning: {block.text}") elif item.type == "message": for part in item.content: print(f"Answer: {part.text}") ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "o3-mini", input: "Explain why the sky is blue", reasoning: { effort: "high", summary: "detailed" }, }); for (const item of response.output) { if (item.type === "reasoning") { for (const block of item.summary) { console.log(`Reasoning: ${block.text}`); } } else if (item.type === "message") { for (const part of item.content) { console.log(`Answer: ${part.text}`); } } } ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="o3-mini", input="Explain why the sky is blue", reasoning={"effort": "high", "summary": "detailed"} ) for item in response.output: if item.type == "reasoning": for block in item.summary: print(f"Reasoning: {block.text}") elif item.type == "message": for part in item.content: print(f"Answer: {part.text}") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "o3-mini", input: "Explain why the sky is blue", reasoning: { effort: "high", summary: "detailed" }, }); for (const item of response.output) { if (item.type === "reasoning") { for (const block of item.summary) { console.log(`Reasoning: ${block.text}`); } } else if (item.type === "message") { for (const part of item.content) { console.log(`Answer: ${part.text}`); } } } ``` ```bash cURL curl https://api.auriko.ai/v1/responses \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "o3-mini", "input": "Explain why the sky is blue", "reasoning": {"effort": "high", "summary": "detailed"} }' ``` Not all models provide reasoning summaries. Check the `summary` array on the `reasoning` output item in the response. See [Extensions and Thinking](/guides/extensions-and-thinking#check-provider-support) for which models support reasoning summaries. --- ## Page: Reasoning > Section: Stream reasoning Stream reasoning summary events: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) stream = client.responses.create( model="o3-mini", input="What is 25 * 37?", reasoning={"effort": "high", "summary": "detailed"}, stream=True ) for event in stream: if event.type == "response.reasoning_summary_text.delta": print(f"[reasoning] {event.delta}", end="", flush=True) elif event.type == "response.output_text.delta": print(event.delta, end="", flush=True) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const stream = await client.responses.create({ model: "o3-mini", input: "What is 25 * 37?", reasoning: { effort: "high", summary: "detailed" }, stream: true, }); for await (const event of stream) { if (event.type === "response.reasoning_summary_text.delta") { process.stdout.write(`[reasoning] ${event.delta}`); } else if (event.type === "response.output_text.delta") { process.stdout.write(event.delta); } } ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) stream = client.responses.create( model="o3-mini", input="What is 25 * 37?", reasoning={"effort": "high", "summary": "detailed"}, stream=True ) for event in stream: if event.type == "response.reasoning_summary_text.delta": print(f"[reasoning] {event.delta}", end="", flush=True) elif event.type == "response.output_text.delta": print(event.delta, end="", flush=True) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const stream = await client.responses.create({ model: "o3-mini", input: "What is 25 * 37?", reasoning: { effort: "high", summary: "detailed" }, stream: true, }); for await (const event of stream) { if (event.type === "response.reasoning_summary_text.delta") { process.stdout.write(`[reasoning] ${event.delta}`); } else if (event.type === "response.output_text.delta") { process.stdout.write(event.delta); } } ``` ```bash cURL curl --no-buffer https://api.auriko.ai/v1/responses \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "o3-mini", "input": "What is 25 * 37?", "reasoning": {"effort": "high", "summary": "detailed"}, "stream": true }' ``` --- ## Page: Reasoning > Section: Map parameters | Chat Completions | Response API | |-----------------|-------------| | `reasoning_effort: "high"` | `reasoning: {effort: "high"}` | | `choices[0].message.reasoning_content` | `output[].type: "reasoning"` with `summary` blocks | | `reasoning_effort` in `extra_body` (OpenAI SDK) | `reasoning` is a native parameter (OpenAI SDK) | The OpenAI SDK supports `reasoning` as a top-level parameter on `client.responses.create()`. You don't need `extra_body` for reasoning in the Response API. --- ## Page: Reasoning > Section: Preserve reasoning across turns Include the full `reasoning` output item (with `encrypted_content` if present) in subsequent `input`. Models use the encrypted content to maintain reasoning context. Omitting it starts reasoning from scratch. ```python final = client.responses.create( model="o3-mini", input=[ {"type": "message", "role": "user", "content": "Solve: x^2 - 5x + 6 = 0"}, *[item.model_dump(exclude={"status"}) for item in response.output], {"type": "message", "role": "user", "content": "Now verify the answer by substitution"} ], reasoning={"effort": "high", "summary": "detailed"} ) ``` The same pattern applies in TypeScript with `await client.responses.create()` and spreading `response.output`. See [Extensions and Thinking](/guides/extensions-and-thinking) for effort normalization, provider support, and sampling constraints. --- ## Page: Routing and Extensions Auriko's `gateway` and `extensions` parameters use identical structure in Chat Completions and the Response API. Pass them at the top level of the request body. --- ## Page: Routing and Extensions > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Python 3.10+ with the OpenAI SDK (`pip install openai`) or the Auriko SDK (`pip install auriko`) - OR Node.js 18+ with the OpenAI SDK (`npm install openai`) or `@auriko/sdk` (`npm install @auriko/sdk`) --- ## Page: Routing and Extensions > Section: Route across models Route a request across multiple models: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( input="What is the capital of France?", extra_body={ "gateway": { "models": ["gpt-4o", "claude-sonnet-4-20250514"], "routing": {"optimize": "cost"} } } ) print(response.output_text) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ input: "What is the capital of France?", // @ts-expect-error Auriko extension gateway: { models: ["gpt-4o", "claude-sonnet-4-20250514"], routing: { optimize: "cost" }, }, }); console.log(response.output_text); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( input="What is the capital of France?", gateway={ "models": ["gpt-4o", "claude-sonnet-4-20250514"], "routing": {"optimize": "cost"} } ) print(response.output_text) print(f"Provider: {response.routing_metadata.provider}") print(f"Cost: ${response.routing_metadata.cost.usd}") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ input: "What is the capital of France?", gateway: { models: ["gpt-4o", "claude-sonnet-4-20250514"], routing: { optimize: "cost" }, }, }); console.log(response.output_text); console.log(`Provider: ${response.routing_metadata?.provider}`); console.log(`Cost: $${response.routing_metadata?.cost?.usd}`); ``` ```bash cURL curl https://api.auriko.ai/v1/responses \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": "What is the capital of France?", "gateway": { "models": ["gpt-4o", "claude-sonnet-4-20250514"], "routing": {"optimize": "cost"} } }' ``` --- ## Page: Routing and Extensions > Section: Set routing options Control routing strategy with `gateway.routing`: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( input="Summarize the benefits of solar energy", extra_body={ "gateway": { "models": ["gpt-4o", "claude-sonnet-4-20250514", "gemini-2.5-flash"], "routing": { "optimize": "cost", "max_cost_per_1m": 5.0, "max_ttft_ms": 2000 } } } ) print(response.output_text) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ input: "Summarize the benefits of solar energy", // @ts-expect-error Auriko extension gateway: { models: ["gpt-4o", "claude-sonnet-4-20250514", "gemini-2.5-flash"], routing: { optimize: "cost", max_cost_per_1m: 5.0, max_ttft_ms: 2000, }, }, }); console.log(response.output_text); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( input="Summarize the benefits of solar energy", gateway={ "models": ["gpt-4o", "claude-sonnet-4-20250514", "gemini-2.5-flash"], "routing": { "optimize": "cost", "max_cost_per_1m": 5.0, "max_ttft_ms": 2000 } } ) print(response.output_text) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ input: "Summarize the benefits of solar energy", gateway: { models: ["gpt-4o", "claude-sonnet-4-20250514", "gemini-2.5-flash"], routing: { optimize: "cost", max_cost_per_1m: 5.0, max_ttft_ms: 2000, }, }, }); console.log(response.output_text); ``` ```bash cURL curl https://api.auriko.ai/v1/responses \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": "Summarize the benefits of solar energy", "gateway": { "models": ["gpt-4o", "claude-sonnet-4-20250514", "gemini-2.5-flash"], "routing": { "optimize": "cost", "max_cost_per_1m": 5.0, "max_ttft_ms": 2000 } } }' ``` See [Routing Options](/guides/routing-options) for all strategies and [Advanced Routing](/guides/advanced-routing) for constraint combinations. --- ## Page: Routing and Extensions > Section: Pass provider extensions Pass provider-specific parameters with `extensions`: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="claude-sonnet-4-20250514", input="Write a haiku about programming", extra_body={ "extensions": { "anthropic": { "metadata": {"user_id": "user-123"} } } } ) print(response.output_text) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "claude-sonnet-4-20250514", input: "Write a haiku about programming", // @ts-expect-error Auriko extension extensions: { anthropic: { metadata: { user_id: "user-123" }, }, }, }); console.log(response.output_text); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="claude-sonnet-4-20250514", input="Write a haiku about programming", extensions={ "anthropic": { "metadata": {"user_id": "user-123"} } } ) print(response.output_text) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "claude-sonnet-4-20250514", input: "Write a haiku about programming", extensions: { anthropic: { metadata: { user_id: "user-123" }, }, }, }); console.log(response.output_text); ``` ```bash cURL curl https://api.auriko.ai/v1/responses \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "input": "Write a haiku about programming", "extensions": { "anthropic": { "metadata": {"user_id": "user-123"} } } }' ``` See [Extensions and Thinking](/guides/extensions-and-thinking#use-provider-passthrough) for all provider extension fields. --- ## Page: Routing and Extensions > Section: Access routing metadata Every Auriko response includes routing metadata with provider, cost, and latency details. With the Auriko SDK ([Python](/sdk/python), [TypeScript](/sdk/typescript)): ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.responses.create( model="gpt-4o", input="Hello!" ) meta = response.routing_metadata print(f"Provider: {meta.provider}") print(f"Model: {meta.provider_model_id}") print(f"Strategy: {meta.routing_strategy}") print(f"TTFT: {meta.ttft_ms}ms") print(f"Throughput: {meta.throughput_tps} tps") if meta.cost: print(f"Cost: ${meta.cost.usd}") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.responses.create({ model: "gpt-4o", input: "Hello!", }); const meta = response.routing_metadata; console.log(`Provider: ${meta?.provider}`); console.log(`Model: ${meta?.provider_model_id}`); console.log(`Strategy: ${meta?.routing_strategy}`); console.log(`TTFT: ${meta?.ttft_ms}ms`); console.log(`Throughput: ${meta?.throughput_tps} tps`); console.log(`Cost: $${meta?.cost?.usd}`); ``` For routing metadata with the OpenAI SDK, see [OpenAI Compatibility](/openai-compatibility#access-routing-metadata). For streaming, access routing metadata from the completed response: ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) stream = client.responses.create( model="gpt-4o", input="Hello!", stream=True ) for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="", flush=True) meta = stream.completed_response.routing_metadata print(f"\nProvider: {meta.provider}, Cost: ${meta.cost.usd}") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const stream = await client.responses.create({ model: "gpt-4o", input: "Hello!", stream: true, }); for await (const event of stream) { if (event.type === "response.output_text.delta") { process.stdout.write(event.delta); } } const meta = stream.completedResponse?.routing_metadata; console.log(`\nProvider: ${meta?.provider}, Cost: $${meta?.cost?.usd}`); ``` For routing metadata with the OpenAI SDK, see [OpenAI Compatibility](/openai-compatibility#access-routing-metadata). Fields: `provider`, `provider_model_id`, `model_canonical`, `routing_strategy`, `ttft_ms`, `throughput_tps`, `cost.usd`. See [Routing Options](/guides/routing-options), [Advanced Routing](/guides/advanced-routing), [Response Metadata](/contract/response-metadata), and [Response Headers](/contract/response-headers) for details. === # Auriko API Reference --- ## Page: Introduction Auriko is an LLM routing layer that applies quantitative trading methodology to inference cost optimization. You can access models across providers through a single API, define your own routing strategy, and switch models through configuration. Auriko charges zero price markup. You pay the price that providers charge. Send your first request in minutes Complete API documentation Native Python SDK with OpenAI compatibility Native TypeScript SDK with full typing OpenAI Response API format with multi-model routing --- ## Page: Introduction > Section: What Auriko Provides - **[Routing and arbitrage](/guides/routing-options)** — Cost, latency, and quality optimization across models and providers. Auriko runs deep [prompt-caching optimization](/guides/prompt-caching) and [cost optimization](/guides/cost-optimization). - **[Automatic failover](/guides/error-handling)** — Redundancy and provider-aware rate limit management. - **Budget controls** — Spending limits at the workspace or API key level. See [Error codes](/contract/error-codes) for budget enforcement behavior. - **[BYOK](/platform/byok)** — Use your own provider keys, platform keys, or both. Auriko provides native SDKs for [Python](/sdk/python) and [TypeScript](/sdk/typescript). It's OpenAI-compatible: both [Chat Completions](/api-reference/chat-completions) and the [Response API](/api-reference/create-response) (preview) work through Auriko. If you already use an OpenAI client or framework, you can point it at Auriko with [minimal changes](/openai-compatibility). --- ## Page: Introduction > Section: Resources - [Available Models](https://www.auriko.ai/models) — Supported models and providers. Fetch model directory data with the [Model directory API](/api-reference/model-directory). - [Pricing](https://www.auriko.ai/pricing) — Pricing information - [Status page](https://status.auriko.ai/) — Current system health and incident history - **[Support page](https://www.auriko.ai/support)** — Or email [support@auriko.ai](mailto:support@auriko.ai) for questions, bug reports, or help - **Security** — Report vulnerabilities to [security@auriko.ai](mailto:security@auriko.ai) (see [security.txt](https://auriko.ai/.well-known/security.txt)) --- ## Page: Introduction > Section: Machine-readable sources You can access Auriko's documentation in machine-readable formats for AI agents and programmatic use. - [llms.txt](/llms.txt) — Index of all documentation sections in plaintext, following the [llms.txt standard](https://llmstxt.org/) - [llms-full.txt](/llms-full.txt) — Complete documentation in a single file - [OpenAPI spec](/openapi.yaml) — OpenAPI 3.1 specification for all API endpoints --- ## Page: Quickstart Install an SDK, set your API key, and make a chat completion call. --- ## Page: Quickstart > Section: Prerequisites - An [Auriko account](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) with an API key --- ## Page: Quickstart > Section: 1. Get an API Key Create your account and get an API key from the dashboard **Base URL:** Use `https://api.auriko.ai/v1` as your base URL with the OpenAI SDK, Auriko SDK, or cURL. The Anthropic SDK and [Claude Code](/integrations/claude-code) append `/v1` on their own, so use `https://api.auriko.ai` instead. --- ## Page: Quickstart > Section: 2. Install ```bash Python OpenAI pip install openai ``` ```bash TypeScript OpenAI npm install openai ``` ```bash Python Auriko pip install auriko ``` ```bash TypeScript Auriko npm install @auriko/sdk ``` --- ## Page: Quickstart > Section: 3. Make Your First Request ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) if response.routing_metadata: print(f"Provider: {response.routing_metadata.provider}") if response.routing_metadata.cost: print(f"Cost: ${response.routing_metadata.cost.usd:.6f}") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], }); console.log(response.choices[0].message.content); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Hello!"}] }' ``` --- ## Page: Quickstart > Section: 4. Enable Routing Features (Optional) ```python Python OpenAI response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": { "optimize": "cost-focus", # Optimize for cost "max_ttft_ms": 1000, # Max 1s to first token "ttft_percentile": "p50", }}} ) ``` ```typescript TypeScript OpenAI const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "cost-focus", max_ttft_ms: 1000, ttft_percentile: "p50", } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], gateway={"routing": { "optimize": "cost-focus", # Optimize for cost "max_ttft_ms": 1000, # Max 1s to first token "ttft_percentile": "p50", }} ) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "cost-focus", // Optimize for cost max_ttft_ms: 1000, // Max 1s to first token ttft_percentile: "p50", }}, }); ``` ```bash cURL curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"optimize": "cost-focus", "max_ttft_ms": 1000, "ttft_percentile": "p50"}} }' ``` --- ## Page: Quickstart > Section: Next Steps Full API documentation Configure cost/latency optimization Real-time streaming responses Let LLMs call your functions Analyze images with chat completions Get JSON responses matching a schema Use with LangChain --- ## Page: Quickstart > Section: Machine-readable sources You can access Auriko's documentation in machine-readable formats for AI agents and programmatic use. - [llms.txt](/llms.txt) — Index of all documentation sections in plaintext, following the [llms.txt standard](https://llmstxt.org/) - [llms-full.txt](/llms-full.txt) — Complete documentation in a single file - [OpenAPI spec](/openapi.yaml) — OpenAPI 3.1 specification for all API endpoints --- ## Page: OpenAI Compatibility Auriko exposes an OpenAI-compatible API. If you already use the OpenAI SDK, change your client initialization to point at Auriko and every call works the same way. --- ## Page: OpenAI Compatibility > Section: Update client initialization Change the client initialization to point at Auriko: ### Python ```python # With OpenAI SDK from openai import OpenAI client = OpenAI(api_key="sk-...") # With Auriko — just change the init import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) # Everything else stays the same response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) ``` ### TypeScript ```typescript // With OpenAI SDK import OpenAI from "openai"; const client = new OpenAI({ apiKey: "sk-..." }); // With Auriko — just change the init import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); // Everything else stays the same const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], }); ``` --- ## Page: OpenAI Compatibility > Section: Check compatibility Standard OpenAI API features work through Auriko, including chat completions, the Response API, streaming, tool calling, structured output (`json_schema`), error classes, async clients, and the models list endpoint. Some features have special handling: | Feature | Behavior | |---------|----------| | Response API | Preview. See [Response API overview](/response-api/overview) and [API reference](/api-reference/create-response). | | Legacy `functions`/`function_call` | Auriko auto-converts to `tools`/`tool_choice` | | Structured output (`json_object`) | Model-dependent. See [Structured output](/guides/structured-output). | --- ## Page: OpenAI Compatibility > Section: Use additional features Auriko adds capabilities on top of the OpenAI-compatible interface: - **[Routing options](/guides/routing-options):** Optimize for cost, latency, or throughput across providers. - **[Cost optimization](/guides/cost-optimization):** Auriko computes the expected cost of each request at every available provider and routes to the cheapest one. - **[Prompt caching](/guides/prompt-caching):** Auriko optimizes prompt caching automatically, reducing cost and latency on repeated requests. - **Budget management:** Set spending limits per workspace, API key, or BYOK provider. See [Error codes](/contract/error-codes) for the `budget_exhausted` code. - **Response headers:** Every response includes `request_id`, rate limit headers, and credit usage. See [Python SDK](/sdk/python#read-response-headers) or [TypeScript SDK](/sdk/typescript#read-response-headers). --- ## Page: OpenAI Compatibility > Section: Use OpenAI SDK directly You can use the OpenAI SDK with a `base_url` override instead of the Auriko package: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="claude-sonnet-4-20250514", messages=[{"role": "user", "content": "Hello from Claude via Auriko!"}] ) print(response.choices[0].message.content) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "claude-sonnet-4-20250514", messages: [{ role: "user", content: "Hello from Claude via Auriko!" }], }); console.log(response.choices[0].message.content); ``` This gives you access to models from multiple providers (Anthropic, Google, Meta, and others) through the OpenAI client. For typed routing metadata and error mapping, use the Auriko SDK ([Python](/sdk/python), [TypeScript](/sdk/typescript)) instead. --- ## Page: OpenAI Compatibility > Section: Map errors If you use the OpenAI SDK directly, Auriko errors arrive as generic `openai.APIStatusError`. You can convert them to typed Auriko errors with `map_openai_error()` to branch on specific error codes like `budget_exhausted` or `rate_limit_error`: ```python import os import openai from auriko import map_openai_error, RateLimitError, PermissionDeniedError client = openai.OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) try: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}] ) except openai.APIStatusError as e: auriko_error = map_openai_error(e) if isinstance(auriko_error, RateLimitError): # budget_exhausted is a 429 rate_limit_error; branch on .code if needed if auriko_error.code == "budget_exhausted": print(f"Budget exhausted: {auriko_error.message}") else: print(f"Rate limited. Retry after: {auriko_error.retry_after_seconds}s") elif isinstance(auriko_error, PermissionDeniedError): print(f"Permission denied (code={auriko_error.code}): {auriko_error.message}") else: raise auriko_error ``` `map_openai_error()` is Python-only. TypeScript users get typed errors automatically with the [Auriko SDK](/sdk/typescript). See [Error Handling](/guides/error-handling) for the full guide. --- ## Page: OpenAI Compatibility > Section: Access routing metadata Every Auriko response includes routing metadata: which provider handled the request, the cost, and latency. How you access it depends on which SDK you use. ### With the Auriko SDK Both the Python and TypeScript SDKs expose `routing_metadata` as a typed property on each response: ```python Python from auriko import Client client = Client() response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], ) print(response.routing_metadata.provider) ``` ```typescript TypeScript import { Client } from "@auriko/sdk"; const client = new Client(); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], }); console.log(response.routing_metadata?.provider); ``` See the [Python SDK](/sdk/python) or [TypeScript SDK](/sdk/typescript) for the full client reference. ### With the OpenAI SDK Auriko includes routing metadata in every response. If you use the OpenAI SDK, the `auriko` Python package provides two helpers to extract it with full typing: **`parse_routing_metadata()`** extracts routing metadata from an OpenAI SDK response: ```python from auriko.route_types import parse_routing_metadata response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], ) metadata = parse_routing_metadata(response) if metadata: print(f"Provider: {metadata.provider}") if metadata.cost: print(f"Cost: ${metadata.cost.usd}") ``` **`AurikoAsyncOpenAI`** (experimental) is a drop-in `AsyncOpenAI` subclass that captures routing metadata on every response. Use it when a framework (OpenAI Agents SDK, LangChain, LlamaIndex) requires an `AsyncOpenAI` instance: ```python import asyncio from auriko import AurikoAsyncOpenAI async def main(): client = AurikoAsyncOpenAI() response = await client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], ) print(client.last_routing_metadata.provider) asyncio.run(main()) ``` Install with `pip install "auriko[openai-compat]"`. See [`AurikoAsyncOpenAI`](/sdk/python#use-with-openai-compatible-frameworks) for framework wiring details. These helpers are Python-only. For typed routing metadata in TypeScript, use the [Auriko SDK](/sdk/typescript) or [`@auriko/ai-sdk-provider`](/frameworks/vercel-ai-sdk) with the Vercel AI SDK. --- ## Page: OpenAI Compatibility > Section: Resources - [Python SDK](/sdk/python) — full native client reference - [TypeScript SDK](/sdk/typescript) — TypeScript client reference - [Error handling](/guides/error-handling) — complete error handling guide --- ## Page: API Overview The Auriko API is OpenAI-compatible. You can use the OpenAI SDK with a base URL change. --- ## Page: API Overview > Section: Base URL ``` https://api.auriko.ai/v1 ``` --- ## Page: API Overview > Section: Make a request Send a chat completion request: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "What is the capital of France?"}] ) print(response.choices[0].message.content) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "What is the capital of France?" }], }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "What is the capital of France?"}] ) print(response.choices[0].message.content) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "What is the capital of France?" }], }); console.log(response.choices[0].message.content); ``` ```bash cURL curl -X POST https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "What is the capital of France?"}] }' ``` To stream the response, set `stream` to `true`: ```python Python OpenAI stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Explain quantum computing in one paragraph."}], stream=True ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` ```typescript TypeScript OpenAI const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Explain quantum computing in one paragraph." }], stream: true, }); for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { process.stdout.write(chunk.choices[0].delta.content); } } ``` ```python Python Auriko stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Explain quantum computing in one paragraph."}], stream=True ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` ```typescript TypeScript Auriko const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Explain quantum computing in one paragraph." }], stream: true, }); for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { process.stdout.write(chunk.choices[0].delta.content); } } ``` ```bash cURL curl -X POST https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Explain quantum computing in one paragraph."}], "stream": true }' ``` To optimize for cost, add routing options: ```python Python OpenAI response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Summarize the benefits of cloud computing."}], extra_body={"gateway": {"routing": {"optimize": "cost"}}} ) print(response.choices[0].message.content) ``` ```typescript TypeScript OpenAI const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Summarize the benefits of cloud computing." }], gateway: { routing: { optimize: "cost" } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Summarize the benefits of cloud computing."}], gateway={"routing": {"optimize": "cost"}} ) print(response.choices[0].message.content) ``` ```typescript TypeScript Auriko const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Summarize the benefits of cloud computing." }], gateway: { routing: { optimize: "cost" } }, }); console.log(response.choices[0].message.content); ``` ```bash cURL curl -X POST https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Summarize the benefits of cloud computing."}], "gateway": {"routing": {"optimize": "cost"}} }' ``` --- ## Page: API Overview > Section: Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | [`/v1/chat/completions`](/api-reference/chat-completions) | POST | Create a chat completion | | [`/v1/responses`](/api-reference/create-response) | POST | Create a response (Response API) | | [`/v1/models`](/api-reference/list-callable-models) | GET | List callable models | | [`/v1/models/{model_id}`](/api-reference/retrieve-callable-model) | GET | Retrieve callable model | | [`/v1/me`](/api-reference/get-api-key-identity) | GET | Get API key identity | | [`/v1/registry/providers`](/api-reference/provider-catalog) | GET | Provider catalog | | [`/v1/registry/models`](/api-reference/canonical-model-records) | GET | Canonical model records | | [`/v1/directory/models`](/api-reference/model-directory) | GET | Model directory | | [`/v1/byok/providers`](/api-reference/list-byok-providers) | GET | BYOK provider discovery | | [`/v1/byok/providers/{provider}/tiers`](/api-reference/get-byok-provider-tiers) | GET | BYOK provider account tiers | | [`/v1/workspaces/{workspace_id}/api-keys`](/api-reference/list-api-keys) | GET | List API keys | | [`/v1/workspaces/{workspace_id}/api-keys/{api_key_id}`](/api-reference/get-api-key) | GET | Get API key | | [`/v1/workspaces/{workspace_id}/api-keys/{api_key_id}/usage`](/api-reference/get-api-key-usage) | GET | Get API key usage | | [`/v1/workspaces/{workspace_id}/api-keys`](/api-reference/create-api-key) | POST | Create API key | | [`/v1/workspaces/{workspace_id}/api-keys/{api_key_id}`](/api-reference/update-api-key) | PATCH | Update API key | | [`/v1/workspaces/{workspace_id}/api-keys/{api_key_id}`](/api-reference/delete-api-key) | DELETE | Revoke API key | | [`/v1/workspaces/{workspace_id}/byok-keys`](/api-reference/list-byok-keys) | GET | List BYOK keys | | [`/v1/workspaces/{workspace_id}/byok-keys/{byok_key_id}`](/api-reference/get-byok-key) | GET | Get BYOK key | | [`/v1/workspaces/{workspace_id}/byok-keys`](/api-reference/create-byok-key) | POST | Create BYOK key | | [`/v1/workspaces/{workspace_id}/byok-keys/{byok_key_id}`](/api-reference/update-byok-key) | PATCH | Update BYOK key | | [`/v1/workspaces/{workspace_id}/byok-keys/{byok_key_id}`](/api-reference/delete-byok-key) | DELETE | Delete BYOK key | | [`/v1/workspaces/{workspace_id}/billing/balance`](/api-reference/get-credit-balance) | GET | Credit balance | --- ## Page: API Overview > Section: OpenAI compatibility Auriko supports the same request/response format as OpenAI. Switch to Auriko by changing two values: 1. **Base URL:** `https://api.openai.com/v1` → `https://api.auriko.ai/v1` 2. **API Key:** Use your Auriko API key (starts with `ak_`) --- ## Page: API Overview > Section: Auriko extensions Auriko responses carry additional fields beyond the OpenAI format. ### Response extensions Every chat completion response includes a `routing_metadata` object with routing and cost details: ```json { "routing_metadata": { "provider": "openai", "provider_model_id": "gpt-4o-2024-08-06", "model_canonical": "gpt-4o", "routing_strategy": "balanced", "ttft_ms": 312, "cost": { "usd": 0.00015 }, "warnings": [] } } ``` When the gateway had to modify or ignore part of the request, `warnings` carries one or more structured entries: ```json { "routing_metadata": { "provider": "openai", "provider_model_id": "gpt-4o-2024-08-06", "model_canonical": "gpt-4o", "routing_strategy": "balanced", "warnings": [ { "type": "unsupported_parameter", "code": "seed", "message": "Parameter 'seed' is not supported by the selected provider." } ] } } ``` See [Response metadata](/contract/response-metadata) for the full field reference. ### Request extensions Pass routing options to optimize your requests via the `gateway` field: ```json { "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}], "gateway": { "routing": { "optimize": "cost", "max_ttft_ms": 1000 } } } ``` ### Request metadata Attach custom metadata to requests for tracking and observability via `gateway.metadata`. Auriko strips this metadata before forwarding to the provider. ```json { "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}], "gateway": { "metadata": { "tags": ["production", "chatbot"], "user_id": "user_abc123", "trace_id": "trace_xyz789", "custom_fields": { "environment": "production", "feature": "customer-support" } } } } ``` | Field | Type | Limits | |-------|------|--------| | `tags` | `string[]` | Max 100 tags, each max 50 chars | | `user_id` | `string` | Max 255 chars | | `trace_id` | `string` | Max 255 chars | | `custom_fields` | `Record` | Max 10 fields, keys max 50 chars, values max 200 chars | --- ## Page: API Overview > Section: Response headers Every response carries custom headers. See [Response headers](/contract/response-headers) for the full reference. Learn how to authenticate your requests --- ## Page: Authentication All API requests require authentication using a Bearer token. --- ## Page: Authentication > Section: API Keys API keys are prefixed with `ak_` and can be created in your [dashboard](https://auriko.ai/dashboard?tab=api-keys). Keep your API key secret. Do not share it or commit it to version control. --- ## Page: Authentication > Section: Use your API key Include your API key in the `Authorization` header: ```bash curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}' ``` --- ## Page: Authentication > Section: SDK Authentication ```python Python Auriko import os from auriko import Client # Option 1: Pass via environment variable (recommended) client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) # Option 2: Auto-detect from AURIKO_API_KEY env var client = Client(base_url="https://api.auriko.ai/v1") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; // Option 1: Pass via environment variable (recommended) const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); // Option 2: Auto-detect from AURIKO_API_KEY env var const client = new Client({ baseUrl: "https://api.auriko.ai/v1", }); ``` --- ## Page: Authentication > Section: Environment Variables Set your API key as an environment variable for security: ```bash export AURIKO_API_KEY=ak_your_api_key_here ``` Then use the SDK without passing the key directly: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); ``` ```python Python Auriko from auriko import Client client = Client(base_url="https://api.auriko.ai/v1") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ baseUrl: "https://api.auriko.ai/v1" }); ``` --- ## Page: Authentication > Section: Error Responses | Status | Code | Description | |--------|------|-------------| | 401 | `invalid_api_key` | API key is invalid or missing | ```json 401 Response { "error": { "message": "API key is invalid.", "type": "authentication_error", "param": null, "code": "invalid_api_key", "doc_url": "https://docs.auriko.ai/errors/invalid_api_key" } } ``` --- ## Page: Authentication > Section: Authentication summary | Category | Auth method | Token prefix | |----------|-------------|-------------| | API key endpoints (`/v1/chat/completions`, `/v1/responses`, `/v1/messages`, `/v1/models`, `/v1/me`) | API key | `ak_` | | Workspace management (`/v1/workspaces/{workspace_id}/*`) | API key + scopes | `ak_` | | Public catalog (`/v1/registry/*`, `/v1/directory/*`, `/v1/byok/providers*`) | None required | — | --- ## Page: Errors Auriko returns errors in the format matching the endpoint's API family. OpenAI-compatible endpoints (`/v1/chat/completions`, `/v1/models`, etc.) use the OpenAI error envelope. Anthropic-compatible endpoints (`/v1/messages`, `/v1/messages/count_tokens`) use the Anthropic error envelope. --- ## Page: Errors > Section: Error envelope ### OpenAI error envelope Every non-2xx response on OpenAI-compatible endpoints uses this shape: ```json { "error": { "message": "Model 'gpt-5-turbo' is not in the catalog. See https://api.auriko.ai/v1/directory/models for available models.", "type": "not_found_error", "code": "model_not_found", "param": "model", "doc_url": "https://docs.auriko.ai/errors/model_not_found" } } ``` | Field | Type | Required | Purpose | |-------|------|----------|---------| | `message` | string | yes | Human-readable, actionable. Don't branch on text. | | `type` | string | yes | One of six categories (see below). Drives SDK class. | | `code` | string \| null | yes | Stable machine identifier. Branch on this. | | `param` | string \| null | yes | Offending field name. `null` when not attributable. | | `doc_url` | string | recommended | Link to the error's docs page. | | `provider` | string \| null | no | Upstream provider that generated the error. Null for non-provider errors. | | `suggestion` | string | no | Actionable fix hint for routing, capability, or model-not-found errors. Absent for other error types. | Notes: - The envelope is flat under `error`. There is no `details[]` array and no nested error objects. - `Content-Type` is `application/json; charset=utf-8` on every error response. - The response body is never empty; even 401 and 404 carry the envelope. ### Anthropic error envelope Anthropic-compatible endpoints (`/v1/messages` and `/v1/messages/count_tokens`) return errors in the Anthropic Messages API format: ```json { "type": "error", "error": { "type": "invalid_request_error", "message": "Request body must be a JSON object." } } ``` | Field | Type | Required | Purpose | |-------|------|----------|---------| | `type` (top-level) | string | yes | Always `"error"`. | | `error.type` | string | yes | One of nine categories (see table below). | | `error.message` | string | yes | Human-readable description. Don't branch on text. | | `error.suggestion` | string | no | Actionable fix hint for routing, capability, or model-not-found errors. Absent for other error types. | The Anthropic envelope doesn't carry `code`, `param`, or `doc_url`. Routing, capability, and model-not-found errors include an optional `suggestion` with an actionable fix hint. Branch on `error.type` and HTTP status instead. | `error.type` | When | |--------------|------| | `invalid_request_error` | 400 — malformed or invalid request | | `authentication_error` | 401 — missing or invalid API key | | `permission_error` | 403 — authenticated but not allowed | | `not_found_error` | 404 — resource doesn't exist | | `request_too_large` | 413 — payload exceeds size limit | | `rate_limit_error` | 429 — rate or quota limit hit | | `billing_error` | 402 — billing issue | | `api_error` | 500 / 502 / 503 / 504 — server-side fault | | `overloaded_error` | 529 — temporarily overloaded | --- ## Page: Errors > Section: Required headers Every response — success and error — carries: - **`x-request-id`** — unique per request. Copy this when opening a support ticket. SDKs expose it as `request_id` (Python) / `requestId` (TypeScript) on every raised exception. Error responses also carry, when applicable: - **`Retry-After`** — integer seconds. Present on 429 and 503. SDKs read this for automatic backoff. --- ## Page: Errors > Section: Error types (OpenAI envelope) The OpenAI envelope's `type` field uses a closed set of six values: | `type` | When | SDK class | |--------|------|-----------| | `invalid_request_error` | 400 / 405 / 409 / 413 / 422 — malformed or semantically invalid request | `BadRequestError` (or `ConflictError` on 409) | | `authentication_error` | 401 — missing, malformed, or invalid API key | `AuthenticationError` | | `permission_error` | 403 — authenticated but not allowed | `PermissionDeniedError` | | `not_found_error` | 404 — resource doesn't exist or isn't visible | `NotFoundError` | | `rate_limit_error` | 429 — rate or quota limit hit | `RateLimitError` | | `api_error` | 5xx — server-side fault or upstream failure | `InternalServerError` (500) / `APIStatusError` (502 / 503 / 504) | --- ## Page: Errors > Section: Error codes These codes are the canonical set, grouped by category. A published code's meaning never changes. See the [error-code reference](/contract/error-codes) for each code's status and description. ### Authentication and authorization | Code | HTTP | `type` | |------|------|--------| | `invalid_api_key` | 401 | `authentication_error` | | `expired_api_key` | 401 | `authentication_error` | | `insufficient_permissions` | 403 | `permission_error` | | `feature_disabled` | 403 | `permission_error` | | `mfa_required` | 403 | `permission_error` | | `invalid_recovery_code` | 401 | `authentication_error` | | `mfa_verification_failed` | 401 | `authentication_error` | ### Request validation | Code | HTTP | `type` | |------|------|--------| | `invalid_request` | 400 | `invalid_request_error` | | `missing_required_parameter` | 400 | `invalid_request_error` | | `invalid_parameter_value` | 400 | `invalid_request_error` | | `payload_too_large` | 413 | `invalid_request_error` | | `context_length_exceeded` | 400 | `invalid_request_error` | | `content_filtered` | 400 | `invalid_request_error` | | `idempotency_conflict` | 409 | `invalid_request_error` | | `idempotency_replay_unavailable` | 409 | `invalid_request_error` | | `field_immutable` | 400 | `invalid_request_error` | | `operation_not_allowed` | 400 | `invalid_request_error` | | `unknown_field` | 400 | `invalid_request_error` | | `method_not_allowed` | 405 | `invalid_request_error` | | `duplicate_resource` | 409 | `invalid_request_error` | | `state_precondition_failed` | 409 | `invalid_request_error` | ### Routing — capability | Code | HTTP | `type` | |------|------|--------| | `tools_not_supported` | 400 | `invalid_request_error` | | `json_mode_not_supported` | 400 | `invalid_request_error` | | `structured_output_not_supported` | 400 | `invalid_request_error` | | `tools_with_structured_output_not_supported` | 400 | `invalid_request_error` | | `vision_not_supported` | 400 | `invalid_request_error` | | `reasoning_not_supported` | 400 | `invalid_request_error` | | `thinking_disable_not_supported` | 400 | `invalid_request_error` | | `streaming_not_supported` | 400 | `invalid_request_error` | | `non_streaming_not_supported` | 400 | `invalid_request_error` | | `batch_only` | 400 | `invalid_request_error` | | `tier_opt_in_required` | 400 | `invalid_request_error` | | `tool_choice_required_not_supported` | 400 | `invalid_request_error` | | `no_compatible_endpoint` | 400 | `invalid_request_error` | | `no_responses_endpoint` | 400 | `invalid_request_error` | | `input_requires_responses_endpoint` | 400 | `invalid_request_error` | | `response_api_only` | 400 | `invalid_request_error` | | `hosted_tool_not_supported` | 400 | `invalid_request_error` | ### Routing — constraint | Code | HTTP | `type` | |------|------|--------| | `cost_constraint_exceeded` | 400 | `invalid_request_error` | | `latency_constraint_exceeded` | 400 | `invalid_request_error` | | `throughput_constraint_not_met` | 400 | `invalid_request_error` | | `provider_not_in_allowlist` | 400 | `invalid_request_error` | | `provider_blocked` | 400 | `invalid_request_error` | | `required_params_not_supported` | 400 | `invalid_request_error` | ### Routing — policy | Code | HTTP | `type` | |------|------|--------| | `byok_keys_required` | 400 | `invalid_request_error` | | `platform_keys_unavailable` | 400 | `invalid_request_error` | ### Routing — modality | Code | HTTP | `type` | |------|------|--------| | `unsupported_modalities` | 400 | `invalid_request_error` | ### Resources | Code | HTTP | `type` | |------|------|--------| | `model_not_found` | 404 | `not_found_error` | | `resource_not_found` | 404 | `not_found_error` | ### Rate limits and quotas | Code | HTTP | `type` | |------|------|--------| | `rate_limit_exceeded` | 429 | `rate_limit_error` | | `budget_exhausted` | 429 | `rate_limit_error` | | `insufficient_quota` | 429 | `rate_limit_error` | ### Routing and providers | Code | HTTP | `type` | |------|------|--------| | `no_provider_available` | 503 | `api_error` | | `upstream_error` | 502 | `api_error` | | `upstream_timeout` | 504 | `api_error` | | `model_unavailable` | 503 | `api_error` | | `client_disconnected` | — | `api_error` | ### Server | Code | HTTP | `type` | |------|------|--------| | `internal_error` | 500 | `api_error` | | `service_unavailable` | 503 | `api_error` | Auriko abstracts over multiple upstream LLM providers. Error messages name the model and the upstream provider that produced the error. The provider name appears in `error.provider` and may appear in `error.message`. Failover across providers happens before a 429 or 5xx surfaces to the client. --- ## Page: Errors > Section: Retry policy Branch on `type` and `code`, not on HTTP status alone. The SDK retry loop uses the same rules. | Condition | Retryable | Notes | |-----------|-----------|-------| | `type: rate_limit_error` + `code: rate_limit_exceeded` | yes | Honor `Retry-After` header. | | `type: rate_limit_error` + `code: budget_exhausted` | **no** | Top up credits or raise the budget. | | `type: rate_limit_error` + `code: insufficient_quota` | **no** | Account has no quota on the current plan. | | `type: api_error` + status 500 / 502 / 503 / 504 (except `code: internal_error`) | yes | Exponential backoff. Honor `Retry-After` on 503. | | `type: api_error` + `code: internal_error` | **no** | Unclassified fault; retrying won't help. Contact support with `x-request-id`. | | `type: authentication_error` / `permission_error` / `not_found_error` / `invalid_request_error` | no | Client-side fix required. | | Network failure before any response (DNS, TCP, TLS) | yes | SDK raises `APIConnectionError`. | Anthropic-compatible endpoints don't carry `code`. The Anthropic SDK retries based on HTTP status. --- ## Page: Errors > Section: Mid-stream errors (SSE) Streaming endpoints return `Content-Type: text/event-stream`. When the HTTP status is already committed as `200 OK` and an error occurs mid-stream, the envelope surfaces as a final `data:` event and the stream closes: ``` HTTP/1.1 200 OK Content-Type: text/event-stream x-request-id: req_01HXABCDEFGHJKMNPQRSTVWXYZ data: {"id":"chatcmpl_01HX...","choices":[{"delta":{"content":"Hello"},"index":0}]} data: {"error":{"message":"Upstream timed out after 30 seconds.","type":"api_error","code":"upstream_timeout","param":null,"provider":"openai","doc_url":"https://docs.auriko.ai/errors/upstream_timeout"}} ``` Rules: - Errors never emit as partial JSON or a different SSE event name. - `data: [DONE]` signals successful completion only. After an error event, no `[DONE]` follows. - The connection closes immediately after the error event. --- ## Page: Errors > Section: SDK exception dispatch The Python and TypeScript SDKs dispatch incoming envelopes to typed exceptions. Every instance exposes `message`, `type`, `code`, `param`, `request_id`, `doc_url`, `status_code`, `retry_after_seconds`, and `provider`. ```python Python Auriko import os from auriko import Client from auriko.errors import ( AurikoAPIError, APIConnectionError, AuthenticationError, PermissionDeniedError, BadRequestError, ConflictError, NotFoundError, RateLimitError, InternalServerError, APIStatusError, ) client = Client(api_key=os.environ["AURIKO_API_KEY"]) try: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], ) except AuthenticationError as e: print(f"Check your API key. request_id={e.request_id}") except PermissionDeniedError as e: print(f"Not allowed: {e.message} (code={e.code})") except RateLimitError as e: print(f"Rate limited, retry after {e.retry_after_seconds}s") except NotFoundError as e: print(f"Not found: {e.message} (param={e.param})") except BadRequestError as e: print(f"Bad request: {e.message} (param={e.param})") except ConflictError as e: print(f"Conflict: {e.message} (code={e.code})") except InternalServerError as e: print(f"Server error. request_id={e.request_id}") except APIStatusError as e: print(f"Upstream error ({e.status_code}): {e.message}") except APIConnectionError as e: print(f"Network error: {e.message}") except AurikoAPIError as e: print(f"API error ({e.status_code}): {e.message}") ``` ```typescript TypeScript Auriko import { Client, AurikoAPIError, APIConnectionError, AuthenticationError, PermissionDeniedError, BadRequestError, ConflictError, NotFoundError, RateLimitError, InternalServerError, APIStatusError, } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY }); try { const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], }); } catch (e) { if (e instanceof AuthenticationError) { console.log(`Check your API key. requestId=${e.requestId}`); } else if (e instanceof PermissionDeniedError) { console.log(`Not allowed: ${e.message} (code=${e.code})`); } else if (e instanceof RateLimitError) { console.log(`Rate limited, retry after ${e.retryAfterSeconds}s`); } else if (e instanceof NotFoundError) { console.log(`Not found: ${e.message} (param=${e.param})`); } else if (e instanceof BadRequestError) { console.log(`Bad request: ${e.message} (param=${e.param})`); } else if (e instanceof ConflictError) { console.log(`Conflict: ${e.message} (code=${e.code})`); } else if (e instanceof InternalServerError) { console.log(`Server error. requestId=${e.requestId}`); } else if (e instanceof APIStatusError) { console.log(`Upstream error (${e.statusCode}): ${e.message}`); } else if (e instanceof APIConnectionError) { console.log(`Network error: ${e.message}`); } else if (e instanceof AurikoAPIError) { console.log(`API error (${e.statusCode}): ${e.message}`); } } ``` --- ## Page: Errors > Section: Built-in retry The SDK retries on `APIConnectionError`, `rate_limit_error` (except `budget_exhausted` and `insufficient_quota`), and `api_error` (except `code: internal_error`). Retries use exponential backoff and honor `Retry-After` when present. ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", max_retries=2, ) # Disable retries client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", max_retries=0, ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", maxRetries: 2, }); // Disable retries const noRetry = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", maxRetries: 0, }); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], max_retries=2, ) client = Client( api_key=os.environ["AURIKO_API_KEY"], max_retries=0, ) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, maxRetries: 2, }); // Disable retries const noRetry = new Client({ apiKey: process.env.AURIKO_API_KEY, maxRetries: 0, }); ``` --- ## Page: Errors > Section: Support When reporting a failure to support, include the `x-request-id` from the response header (or `request_id` / `requestId` on the SDK exception). That single identifier pairs the client view with the server log. --- ## Page: Create Chat Completion Creates a model response for the given chat conversation. Auriko routes the request to the optimal provider based on your routing preferences (cost, latency, throughput, etc.). --- ## Page: Create Chat Completion > Section: Auriko Extensions Beyond OpenAI compatibility, this endpoint supports: - **Multi-model routing**: Use `models[]` instead of `model` to route across multiple models - **Routing options**: Control provider selection with the `routing` object - **Provider extensions**: Pass provider-specific parameters with `extensions` - **Cost transparency**: Response includes `routing_metadata` with cost breakdown All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Create response Auriko routes the request to the optimal provider based on your routing preferences (cost, latency, throughput, etc.). For guide-level documentation, see the [Response API overview](/response-api/overview). The Response API is in preview. The interface may change before GA. --- ## Page: Create response > Section: Auriko extensions Auriko adds these capabilities to the standard Response API: - **Multi-model routing**: Use `gateway.models[]` instead of `model` to route across multiple models - **Routing options**: Control provider selection with the `gateway.routing` object - **Provider extensions**: Pass provider-specific parameters with `extensions` - **Cost transparency**: Response includes `routing_metadata` with cost breakdown --- ## Page: List callable models `GET /v1/models` returns the OpenAI-compatible model list for your API key. Use this endpoint when you need model IDs for authenticated requests to `/v1/chat/completions`, `/v1/responses`, or `/v1/messages`. By default the list contains models callable via Chat Completions. Add `?endpoint=responses` to also include models that are only available via the [Response API](/response-api/overview), such as `gpt-5.5-pro`. To browse models, compare capabilities, inspect context windows, or review provider pricing, use the [Model directory](/api-reference/model-directory). The response includes Auriko fields in addition to OpenAI model fields: - `providers[]`: Available providers with pricing for each model - `supported_endpoints`: APIs the model supports (e.g., `chat_completions`, `responses`, `batch`) - `context_window`: Largest context window across the model's providers - `catalog_version`: Version of the model catalog - `catalog_age_seconds`: Age of the catalog data All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Retrieve callable model `GET /v1/models/{model_id}` returns metadata for one callable model. Pass a canonical model ID such as `claude-sonnet-4-6`, or pass an alias such as `gpt-4o-mini`. Aliases resolve to the canonical ID in the response. To find model IDs, compare capabilities, inspect context windows, or review provider pricing, use the [Model directory](/api-reference/model-directory). All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Model directory `GET /v1/directory/models` returns the full model catalog. Use this endpoint when you want to browse models, compare providers, inspect capabilities, or choose a model ID for inference requests. Each model includes provider routes, context windows, maximum output tokens, capabilities, modalities, and pricing tiers. To list model IDs available to your API key, use [List callable models](/api-reference/list-callable-models). This endpoint is public and does not require authentication. All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Canonical model records `GET /v1/registry/models` returns a compact record for each canonical model. Use this endpoint when you need normalized model IDs, capabilities, authors, and provider availability for catalog views. For provider routes, context windows, modalities, and pricing tiers, use the [Model directory](/api-reference/model-directory). This endpoint is public and does not require authentication. All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Provider catalog `GET /v1/registry/providers` returns provider records used in Auriko's model catalog. Use this endpoint when you need provider IDs, display names, icons, or data policy values for catalog UI. For per-model provider routes and pricing, use the [Model directory](/api-reference/model-directory). This endpoint is public and does not require authentication. All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Get API key identity Returns credential introspection for the calling API key: its workspace, granted scopes, capability profile, rate limits, and expiry. The response reflects current state. Any valid active API key can call this endpoint; no scope is required. --- ## Page: List API keys Excludes system-managed playground keys. Requires `keys:read` scope. Naturally idempotent. All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Get API key Never returns raw key secrets. Requires `keys:read` scope. Naturally idempotent. All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Get API key usage Includes request counts, token counts, and cost. Requires `keys:read` or `usage:read` scope. Naturally idempotent. All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Create API key Requires `keys:write` scope. No idempotency. The response includes the raw API key secret once. It can't be retrieved again. If you lose the response, list keys, revoke the orphan, and create a replacement. With an API key, you can only create inference-profile keys. To create management keys, use the dashboard. All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Update API key Change a key's name, rate limit, expiry, or active state. Requires `keys:write` scope. Naturally idempotent. Scopes are immutable after creation. Only name, rate limit, expiry, and active state can be updated. Restrictive changes (lowering rate limit, shortening expiry, disabling) may return `202` with `propagation_status: "pending"` while the change takes effect. All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Revoke API key Requires `keys:write` scope. Naturally idempotent. Revoked keys stop working within 60 seconds. May return `202` with `propagation_status: "pending"` while the change takes effect. All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: List BYOK providers Returns each provider's conservative default account tier. Public endpoint — no authentication required. Naturally idempotent. Allowed `account_tier` values for key creation come from [List Provider Account Tiers](/api-reference/get-byok-provider-tiers). Auriko derives each key's data policy from provider + tier. You don't submit a data policy directly. All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: List provider account tiers Returns each tier's display name and data policy. Public endpoint — no authentication required. Naturally idempotent. Account tier helps Auriko track provider rate limits, choose safer routing, and enforce your data-policy constraints. Auriko uses the tier you set rather than assuming a higher provider policy. Auriko derives the key's data policy from provider + tier. You don't submit a data policy directly. All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: List BYOK keys Requires `byok:read` scope. Naturally idempotent. Responses contain redacted metadata only — a masked `key_prefix`, never the provider secret. Submitted secrets can't be retrieved through any endpoint. Items may carry `propagation_status: "pending"` while a change to their provider takes effect (within about 5 minutes). All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Create BYOK key Auriko validates the submitted secret against the provider before activation. Requires `byok:write` scope. Supports `Idempotency-Key`. Audit event: `byok_key.created`. The provider secret is write-only: accepted once here, encrypted at rest, and never returned by any endpoint. `Idempotency-Key` replays return the redacted metadata response only — never the secret. To replace a secret, create a new key, set it as default, then delete the old one. Validation outcomes: invalid credentials are rejected with `400` and nothing is saved; transient provider validation failures return a retryable `502` and nothing is saved. A `409` during an in-flight create with the same `Idempotency-Key` means the original request is still running. Wait and retry. `account_tier` accepts the values from [List Provider Account Tiers](/api-reference/get-byok-provider-tiers). When omitted, Auriko auto-detects the tier or falls back to the provider's conservative default. Auriko derives the key's data policy from provider + tier. May return `202` with `propagation_status: "pending"` while the change takes effect. Propagation completes within about 5 minutes. All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Get BYOK key Requires `byok:read` scope. Naturally idempotent. The response is redacted metadata only — a masked `key_prefix` and coarse `validation_status`, never the provider secret or provider diagnostics. The response may carry `propagation_status: "pending"` while a change to this key's provider takes effect (within about 5 minutes). All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Update BYOK key Change a key's `name`, `is_default`, `account_tier`, or `disabled` state. Requires `byok:write` scope. Naturally idempotent (last write wins). Audit event: `byok_key.updated`. The provider secret is immutable. Secret fields are rejected, and this endpoint never triggers provider validation. To replace a secret: create a new key, set it as default, then delete the old key. There's no rotation operation. Setting `disabled: true` excludes the key from routing without deleting it; the secret stays encrypted at rest. A disabled key can't be set as default unless the same request re-enables it. May return `202` with `propagation_status: "pending"` while the change takes effect. Propagation completes within about 5 minutes. All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Delete BYOK key Requires `byok:write` scope. Naturally idempotent. Audit event: `byok_key.deleted`. If the deleted key was the provider's default, Auriko stops routing through it within about 5 minutes. May return `202` with `propagation_status: "pending"` while the change takes effect. All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- ## Page: Get credit balance Requires `billing:read` scope. All parameters, request/response schemas, and examples are auto-generated from the [OpenAPI specification](/api-reference/overview). --- # OpenAPI Specification Reference The following is extracted from the OpenAPI spec — the canonical source of truth for endpoint parameters, request/response schemas, and error codes. --- ## Authentication (ApiKeyAuth) API key authentication. Keys start with `ak_` prefix. Example: `Authorization: Bearer ak_live_xxxxxxxxxxxx` ## Endpoint: POST /v1/chat/completions **Create a chat completion** Creates a model response for the given chat conversation. Auriko routes the request to the optimal provider based on your routing preferences (cost, latency, throughput, etc.). ## Streaming When `stream: true`, responses are delivered as Server-Sent Events (SSE). The final event contains `routing_metadata` with routing decision details. ## Multi-Model Routing Use `gateway.models[]` instead of top-level `model` to enable multi-model routing: - `gateway.routing.mode: "pool"` (default): Best provider across all models - `gateway.routing.mode: "fallback"`: Try models in order ### Request Parameters - `model` (string, optional): Model to route to, required for single-model requests. Mutually exclusive with `gateway.models`; providing both returns 400. Examples: `gpt-4o`, `claude-sonnet-4-6`, `llama-3.3-70b-instruct` - `messages` (array[Message], required): The messages to generate a completion for - `temperature` (number, optional): Sampling temperature (0-2). Some providers restrict this value when reasoning is enabled. - `top_p` (number, optional): Nucleus sampling parameter. Some providers restrict this value when reasoning is enabled. - `max_tokens` (integer, optional): Maximum tokens to generate (legacy, use `max_completion_tokens`) - `max_completion_tokens` (integer, optional): Maximum tokens to generate. Reasoning models (o1/o3) use this field instead of `max_tokens`. - `reasoning_effort` (string, optional): Controls reasoning effort for supported models. Auriko translates this into each provider's native reasoning control. See /guides/extensions-and-thinking for per-provider behavior. Values: `low`, `medium`, `high`, `xhigh`, `max`, `off`. - `stop` (string | array, optional): Stop sequences. Restrictions vary by provider and model. - `presence_penalty` (number, optional): Presence penalty (-2 to 2). Not supported by all providers. - `frequency_penalty` (number, optional): Frequency penalty (-2 to 2). Not supported by all providers. - `logit_bias` (object, optional): Token logit bias. Not supported by all providers. - `seed` (integer, optional): Random seed for reproducibility - `top_k` (integer, optional): Top-K sampling. Restricted by some providers when reasoning is enabled. Supported by Anthropic, Google, and vLLM. - `min_p` (number, optional): Min-P sampling. Supported by some vLLM providers. - `top_a` (number, optional): Top-A sampling. Supported by some vLLM providers. - `repetition_penalty` (number, optional): Repetition penalty. Supported by vLLM providers. - `tools` (array[Tool], optional): Tools the model can call - `tool_choice` (ToolChoice, optional): - `parallel_tool_calls` (boolean, optional): Allow parallel tool calls - `functions` (array[FunctionDefinition], optional): **Deprecated.** Deprecated. Use `tools` instead. Auto-converted. - `function_call` (string | object, optional): **Deprecated.** Deprecated. Use `tool_choice` instead. Auto-converted. - `response_format` (ResponseFormat, optional): - `type` (string, required): Response format type: - `text`: Plain text response (default) - `json_object`: JSON mode - model outputs valid JSON - `json_schema`: Structured output - model follows provided schema Values: `text`, `json_object`, `json_schema`. - `json_schema` (object, optional): Required when type is `json_schema` - `stream` (boolean, optional): Enable streaming responses Default: `False`. - `stream_options` (StreamOptions, optional): - `include_usage` (boolean, optional): Include token usage in final streaming chunk - `user` (string, optional): User identifier for abuse detection - `n` (integer, optional): Number of completions to generate. Not supported by all providers. Default: `1`. - `logprobs` (boolean, optional): Return log probabilities. Not supported by all providers. - `top_logprobs` (integer, optional): Number of top logprobs to return. Requires logprobs support. - `web_search_options` (object, optional): Web search configuration. Supported by OpenAI. - `verbosity` (string, optional): Output verbosity control. Supported by OpenAI. - `prompt_cache_key` (string, optional): Prompt caching identifier. Supported by OpenAI. - `safety_identifier` (string, optional): Safety policy identifier. Supported by OpenAI. - `gateway` (object, optional): Auriko routing, metadata, and multi-model configuration. Omit for default single-model routing. - `routing` (RoutingOptions, optional): Auriko routing configuration (20 fields). Controls how Auriko selects providers for your request. All fields are optional. Setting a field to `null` is equivalent to omitting it. - `metadata` (RequestMetadata, optional): Optional request metadata for tracking and observability. Attached via the `gateway.metadata` field on chat completion requests. - `models` (array[string], optional): Multi-model routing. Mutually exclusive with top-level `model`. Providing both `model` and `gateway.models` returns 400. Models are tried in order per routing mode. - `extensions` (Extensions, optional): Auriko extensions for provider-specific passthrough. For reasoning control, use the top-level `reasoning_effort` parameter instead of extensions. ## Provider Passthrough Pass provider-specific parameters directly: - `anthropic`: Anthropic-specific parameters - `openai`: OpenAI-specific parameters - `google`: Google/Gemini-specific parameters - `deepseek`: DeepSeek-specific parameters Passthrough parameters are forwarded as-is to the target provider. ### Request Examples **Basic completion**: ```json { "model": "gpt-4o", "messages": [ { "role": "user", "content": "Hello!" } ] } ``` **With routing options**: ```json { "model": "claude-sonnet-4-6", "messages": [ { "role": "user", "content": "Explain quantum computing" } ], "gateway": { "routing": { "optimize": "cost", "max_cost_per_1m": 10.0 } } } ``` **Multi-model routing**: ```json { "gateway": { "models": [ "gpt-4o", "claude-sonnet-4-6" ], "routing": { "mode": "pool", "optimize": "cost" } }, "messages": [ { "role": "user", "content": "Hello!" } ] } ``` ### Response (200) Successful completion. For streaming (`stream: true`), responses are Server-Sent Events. Each event is a `ChatCompletionChunk`. The final chunk has `choices: []` (empty) and contains `usage` and `routing_metadata`. Stream ends with `data: [DONE]`. ### Response Properties - `id` (string, required): Unique completion identifier - `object` (string, required): Constant: `chat.completion`. - `created` (integer, required): Unix timestamp of creation - `model` (string, required): Model used for completion - `choices` (array[Choice], required): Completion choices - `usage` (Usage, optional): - `prompt_tokens` (integer, required): Input tokens used - `completion_tokens` (integer, required): Output tokens generated - `total_tokens` (integer, required): Total tokens (prompt + completion) - `prompt_tokens_details` (PromptTokensDetails, optional): Detailed breakdown of prompt tokens - `completion_tokens_details` (CompletionTokensDetails, optional): Breakdown of completion tokens. Provider-dependent: present when the upstream provider reports token-level details, absent otherwise. - `system_fingerprint` (string, optional): Backend fingerprint for reproducibility. Not all models include this field. - `routing_metadata` (RoutingMetadata, optional): Routing decision metadata included in successful responses. 8 STABLE fields (4 required + 4 optional) in the current public contract. - `service_tier` (string | null, optional): The service tier used for processing the request. Present for OpenAI-routed models. ### Error Responses - **400**: Bad request - invalid parameters - **401**: Authentication failed - **404**: Model not found - **429**: Rate limit exceeded. Two sub-causes share this status: - `rate_limit_exceeded`: throughput-based rate limit. - `budget_exhausted`: account or project budget reached (self-service; not auto-retried by SDKs — requires adding credits or raising the limit). - **500**: Internal server error - **502**: Upstream provider failure (Bad Gateway). All non-timeout 5xx errors from upstream providers are normalized to 502 (code: `upstream_error`). Upstream failures may also surface as: - 429: Provider rate limit (code: `rate_limit_exceeded`) - 504: Provider timed out (code: `upstream_timeout`) These use their respective status codes with the same `ErrorResponse` body format. - **503**: Service unavailable — transient issue. Possible causes: - All providers for the model are rate-limited or temporarily unavailable (`no_provider_available`) - Transient infrastructure issue (`service_unavailable`) Note: If the model doesn't support a requested capability (e.g., reasoning), the response is 400 with a specific code (e.g., `reasoning_not_supported`), not 503. If routing constraints excluded all providers, the response is 400 with a specific constraint code (e.g., `cost_constraint_exceeded`). - **504**: Upstream provider timed out. The client may retry with a longer timeout. ## Endpoint: POST /v1/responses **Create a response (OpenAI Response API)** Auriko routes the request to the optimal provider based on your routing preferences (cost, latency, throughput, etc.). ## Streaming When `stream: true`, Auriko delivers responses as Server-Sent Events (SSE) using the Response API event format: `event: \ndata: \n\n`. Terminal events: `response.completed`, `response.incomplete`, `response.failed`. There's no `data: [DONE]` terminator. ## Multi-Model Routing Use `gateway.models[]` instead of top-level `model` to enable multi-model routing. ### Request Parameters - `model` (string, optional): Model ID to use (e.g., "gpt-4o", "claude-sonnet-4-20250514"). - `input` (string | array, required): The input to generate a response for. - `instructions` (string, optional): System instructions for the model. - `tools` (array[ResponseTool], optional): Tools available to the model. - `tool_choice` (string | object | object, optional): How the model should use tools. - `parallel_tool_calls` (boolean, optional): Whether the model can make multiple tool calls in parallel. - `max_output_tokens` (integer, optional): Maximum number of output tokens. - `temperature` (number, optional): Sampling temperature. - `top_p` (number, optional): Nucleus sampling parameter. - `top_k` (integer, optional): Top-k sampling parameter. - `top_logprobs` (integer, optional): Number of top logprobs to return per token position. Requires provider logprobs support. - `stream` (boolean, optional): Whether to stream the response. - `text` (object, optional): Text generation configuration. - `format` (ResponseTextFormat, optional): Text output format configuration. - `reasoning` (object, optional): Reasoning/thinking configuration. - `effort` (string, optional): Level of reasoning effort. Values: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, `off`. - `summary` (string, optional): Reasoning summary format. Values: `auto`, `concise`, `detailed`. - `generate_summary` (string, optional): Reasoning summary format (alternative field name). Values: `auto`, `concise`, `detailed`. - `truncation` (string, optional): Truncation strategy for long inputs. - `metadata` (object, optional): Arbitrary key-value metadata. - `include` (array[string], optional): Additional data to include in the response. - `user` (string, optional): End-user identifier for abuse detection. - `store` (boolean, optional): Omit this field or set to false. Sending true returns 400. Values: `False`. - `gateway` (object, optional): Auriko gateway directives. - `routing` (RoutingOptions, optional): Auriko routing configuration (20 fields). Controls how Auriko selects providers for your request. All fields are optional. Setting a field to `null` is equivalent to omitting it. - `metadata` (RequestMetadata, optional): Optional request metadata for tracking and observability. Attached via the `gateway.metadata` field on chat completion requests. - `models` (array[string], optional): Model pool for multi-model routing. - `extensions` (Extensions, optional): Auriko extensions for provider-specific passthrough. For reasoning control, use the top-level `reasoning_effort` parameter instead of extensions. ## Provider Passthrough Pass provider-specific parameters directly: - `anthropic`: Anthropic-specific parameters - `openai`: OpenAI-specific parameters - `google`: Google/Gemini-specific parameters - `deepseek`: DeepSeek-specific parameters Passthrough parameters are forwarded as-is to the target provider. - `prompt_cache_key` (string, optional): Key for prompt caching. - `safety_identifier` (string, optional): Safety policy identifier. - `frequency_penalty` (number, optional): Penalizes new tokens based on their frequency in the text so far. - `presence_penalty` (number, optional): Penalizes new tokens based on whether they appear in the text so far. - `max_tool_calls` (integer, optional): Maximum number of built-in tool calls (e.g., web_search, code_interpreter) allowed in a response. ### Request Examples **Basic text response**: ```json { "model": "gpt-4o", "input": "Hello!" } ``` **With function tools**: ```json { "model": "gpt-4o", "input": [ { "type": "message", "role": "user", "content": "What's the weather in NYC?" } ], "tools": [ { "type": "function", "name": "get_weather", "parameters": { "type": "object", "properties": { "city": { "type": "string" } } } } ] } ``` ### Response (200) Successful response. For non-streaming requests, returns a `ResponseObject`. For streaming (`stream: true`), returns Server-Sent Events in the Response API format: `event: \ndata: \n\n`. ### Response Properties - `id` (string, required): Unique response identifier. - `object` (string, required): Constant: `response`. - `created_at` (integer, required): Unix timestamp of creation. - `model` (string, required): Model used for generation. - `status` (string, required): Response status. Values: `completed`, `failed`, `incomplete`, `in_progress`. - `output` (array[ResponseOutputItem], required): Output items generated by the model. Known types include `message`, `function_call`, and `reasoning`. Additional types from the provider (e.g., `web_search_call`, `file_search_call`) are passed through verbatim. - `output_text` (string, optional): Concatenated text output for convenience. - `parallel_tool_calls` (boolean, optional): Whether parallel tool calls were enabled. - `tool_choice` (object, optional): Tool choice setting used. - `tools` (array[object], optional): Tools that were available. - `usage` (ResponseUsageSchema, optional): Token usage for a Response API request. - `input_tokens` (integer, required): - `output_tokens` (integer, required): - `total_tokens` (integer, required): - `input_tokens_details` (ResponseInputTokensDetailsSchema, optional): Detailed breakdown of input tokens for a Response API request. - `output_tokens_details` (ResponseOutputTokensDetailsSchema, optional): Detailed breakdown of output tokens for a Response API request. - `error` (object | null, optional): Error details if status is "failed". - `code` (string, optional): - `message` (string, optional): - `incomplete_details` (object | null, optional): Details if status is "incomplete". - `reason` (string, optional): - `metadata` (object | null, optional): - `routing_metadata` (RoutingMetadata, optional): Routing decision metadata included in successful responses. 8 STABLE fields (4 required + 4 optional) in the current public contract. - `temperature` (number, optional): Sampling temperature used. - `top_p` (number, optional): Nucleus sampling parameter used. - `max_output_tokens` (integer | null, optional): Maximum output tokens setting. - `frequency_penalty` (number, optional): Frequency penalty setting. - `presence_penalty` (number, optional): Presence penalty setting. - `top_logprobs` (integer, optional): Number of top logprobs returned. - `instructions` (string | null, optional): System instructions used. - `truncation` (string, optional): Truncation strategy used. - `reasoning` (object | null, optional): Reasoning configuration used. - `text` (object, optional): Text generation configuration used. - `user` (string | null, optional): End-user identifier. - `prompt_cache_key` (string | null, optional): Prompt cache key used. - `safety_identifier` (string | null, optional): Safety policy identifier used. - `max_tool_calls` (integer | null, optional): Maximum number of built-in tool calls allowed in a response. - `store` (boolean, optional): Whether the response is stored. - `previous_response_id` (string | null, optional): Previous response ID for conversation continuity. - `background` (boolean, optional): Whether this was a background response. - `completed_at` (integer | null, optional): Unix timestamp when the response completed. - `service_tier` (string | null, optional): Service tier used by the provider. - `tool_usage` (object | null, optional): Built-in tool consumption metrics from the provider (e.g. image generation tokens, web search request counts). Present on OpenAI responses; absent for other providers. ### Error Responses - **400**: Bad request - invalid parameters - **401**: Authentication failed - **404**: Model not found - **429**: Rate limit exceeded. Two sub-causes share this status: - `rate_limit_exceeded`: throughput-based rate limit. - `budget_exhausted`: account or project budget reached (self-service; not auto-retried by SDKs — requires adding credits or raising the limit). - **500**: Internal server error - **502**: Upstream provider failure (Bad Gateway). All non-timeout 5xx errors from upstream providers are normalized to 502 (code: `upstream_error`). Upstream failures may also surface as: - 429: Provider rate limit (code: `rate_limit_exceeded`) - 504: Provider timed out (code: `upstream_timeout`) These use their respective status codes with the same `ErrorResponse` body format. - **503**: Service unavailable — transient issue. Possible causes: - All providers for the model are rate-limited or temporarily unavailable (`no_provider_available`) - Transient infrastructure issue (`service_unavailable`) Note: If the model doesn't support a requested capability (e.g., reasoning), the response is 400 with a specific code (e.g., `reasoning_not_supported`), not 503. If routing constraints excluded all providers, the response is 400 with a specific constraint code (e.g., `cost_constraint_exceeded`). - **504**: Upstream provider timed out. The client may retry with a longer timeout. ## Endpoint: GET /v1/models **List callable models** Returns the OpenAI-compatible model list for your API key. Use this endpoint to get model IDs for authenticated inference requests. The response includes Auriko fields in addition to OpenAI model fields: - `providers[]`: Available providers with pricing - `supported_endpoints`: APIs the model supports (e.g., `chat_completions`, `responses`, `batch`) - `context_window`: Largest context window across the model's providers - `catalog_version`: Version of the model catalog - `catalog_age_seconds`: Age of the catalog data By default the list contains models callable via Chat Completions. Add `?endpoint=responses` to also include models that are only available via the Response API. ### Response (200) List of callable models ### Response Properties - `object` (string, required): Constant: `list`. - `data` (array[Model], required): - `catalog_version` (string, optional): Version of the model catalog - `catalog_age_seconds` (number, optional): Age of catalog in seconds ### Error Responses - **401**: Authentication failed - **429**: Rate limit exceeded. Two sub-causes share this status: - `rate_limit_exceeded`: throughput-based rate limit. - `budget_exhausted`: account or project budget reached (self-service; not auto-retried by SDKs — requires adding credits or raising the limit). - **500**: Internal server error ## Endpoint: GET /v1/models/{model_id} **Retrieve callable model** Returns metadata for one callable model. Accepts canonical model IDs or aliases. ### Response (200) Model details ### Response Properties - `id` (string, required): Canonical model ID - `object` (string, required): Constant: `model`. - `created` (integer, required): Unix timestamp - `owned_by` (string, required): Always "auriko" - `context_window` (integer, optional): Auriko extension: Largest context window across the model's providers. - `supported_endpoints` (array[string], optional): Auriko extension: APIs the model supports (e.g., `chat_completions`, `responses`, `batch`). - `providers` (array[ModelProvider], optional): Auriko extension: Available providers with pricing. Allows clients to see all available options for this model. ### Error Responses - **401**: Authentication failed - **404**: Model not found - **429**: Rate limit exceeded. Two sub-causes share this status: - `rate_limit_exceeded`: throughput-based rate limit. - `budget_exhausted`: account or project budget reached (self-service; not auto-retried by SDKs — requires adding credits or raising the limit). - **500**: Internal server error ## Endpoint: GET /v1/registry/providers **Provider catalog** Returns provider records used in Auriko's model catalog. ### Response (200) List of providers ### Response Properties - `providers` (array[ProviderResponse], required): - `count` (integer, required): Total number of providers ### Error Responses - **429**: Rate limit exceeded. Two sub-causes share this status: - `rate_limit_exceeded`: throughput-based rate limit. - `budget_exhausted`: account or project budget reached (self-service; not auto-retried by SDKs — requires adding credits or raising the limit). - **500**: Internal server error - **502**: API gateway is temporarily unavailable. - **503**: Service unavailable — transient issue. Possible causes: - All providers for the model are rate-limited or temporarily unavailable (`no_provider_available`) - Transient infrastructure issue (`service_unavailable`) Note: If the model doesn't support a requested capability (e.g., reasoning), the response is 400 with a specific code (e.g., `reasoning_not_supported`), not 503. If routing constraints excluded all providers, the response is 400 with a specific constraint code (e.g., `cost_constraint_exceeded`). ## Endpoint: GET /v1/registry/models **Canonical model records** Returns compact canonical model records with normalized model IDs, capabilities, authors, and provider availability. ### Response (200) Canonical model records ### Response Properties - `models` (array[CanonicalModelResponse], required): - `count` (integer, required): Total number of models - `playground_defaults` (array[string], optional): Ordered list of model IDs recommended as playground defaults. Frontend should select the first available. ### Error Responses - **429**: Rate limit exceeded. Two sub-causes share this status: - `rate_limit_exceeded`: throughput-based rate limit. - `budget_exhausted`: account or project budget reached (self-service; not auto-retried by SDKs — requires adding credits or raising the limit). - **500**: Internal server error - **502**: API gateway is temporarily unavailable. - **503**: Service unavailable — transient issue. Possible causes: - All providers for the model are rate-limited or temporarily unavailable (`no_provider_available`) - Transient infrastructure issue (`service_unavailable`) Note: If the model doesn't support a requested capability (e.g., reasoning), the response is 400 with a specific code (e.g., `reasoning_not_supported`), not 503. If routing constraints excluded all providers, the response is 400 with a specific constraint code (e.g., `cost_constraint_exceeded`). ## Endpoint: GET /v1/directory/models **Model directory** Returns the full model catalog with provider routes, context windows, capabilities, modalities, and pricing tiers. ### Response (200) Model directory ### Response Properties - `models` (object, required): Map of canonical model ID to model entry - `generated_at` (string, required): ISO 8601 timestamp when the directory was generated ### Error Responses - **429**: Rate limit exceeded. Two sub-causes share this status: - `rate_limit_exceeded`: throughput-based rate limit. - `budget_exhausted`: account or project budget reached (self-service; not auto-retried by SDKs — requires adding credits or raising the limit). - **500**: Internal server error - **502**: API gateway is temporarily unavailable. - **503**: Service unavailable — transient issue. Possible causes: - All providers for the model are rate-limited or temporarily unavailable (`no_provider_available`) - Transient infrastructure issue (`service_unavailable`) Note: If the model doesn't support a requested capability (e.g., reasoning), the response is 400 with a specific code (e.g., `reasoning_not_supported`), not 503. If routing constraints excluded all providers, the response is 400 with a specific constraint code (e.g., `cost_constraint_exceeded`). ## Endpoint: GET /v1/workspaces **List workspaces** Returns the workspaces accessible to the API key. Requires `workspace:read` scope. ### Response (200) OK ### Response Properties - `workspaces` (array[PublicWorkspaceItem], required): - `count` (integer, required): Total number of workspaces ### Error Responses - **401**: Authentication failed ## Endpoint: GET /v1/workspaces/{workspace_id} **Get workspace** Returns workspace details. Requires `workspace:read` scope. ### Response (200) OK ### Response Properties - `id` (string, required): Workspace identifier - `name` (string, required): Human-readable workspace name - `slug` (string, required): URL-safe workspace slug - `tier` (string, required): Workspace pricing tier - `created_at` (string, required): When the workspace was created - `updated_at` (string, required): When the workspace was last updated ### Error Responses - **401**: Authentication failed - **404**: Resource not found ## Endpoint: PATCH /v1/workspaces/{workspace_id} **Update workspace** Updates workspace metadata. API key callers can update `name` only. Requires `workspace:write` scope. ### Request Parameters - `name` (string, optional): ### Response (200) OK ### Response Properties - `id` (string, required): Workspace identifier - `name` (string, required): Human-readable workspace name - `slug` (string, required): URL-safe workspace slug - `tier` (string, required): Workspace pricing tier - `created_at` (string, required): When the workspace was created - `updated_at` (string, required): When the workspace was last updated ### Error Responses - **400**: Bad request - invalid parameters - **401**: Authentication failed - **403**: You do not have permission to perform this action - **404**: Resource not found ## Endpoint: GET /v1/workspaces/{workspace_id}/routing-defaults **Get routing defaults** Returns the current routing defaults for a workspace. Requires `routing:read` scope. ### Response (200) OK ### Response Properties - `workspace_id` (string, required): Workspace these defaults belong to - `routing_defaults` (RoutingDefaultsModel | null, optional): Current routing defaults (null if not set) - `propagation_status` (string | null, optional): "pending" when the change committed but the edge-KV sync did not complete immediately; the edge converges on its next read. Absent/null otherwise. Values: `pending`, `None`. ### Error Responses - **401**: Authentication failed - **404**: Resource not found ## Endpoint: PATCH /v1/workspaces/{workspace_id}/routing-defaults **Update routing defaults** Partially updates routing defaults for a workspace. Only fields present in the request are changed. Requires `routing:write` scope. ### Request Parameters - `optimize` (string, optional): Values: `cost`, `cost-focus`, `ttft`, `ttft-focus`, `tps`, `tps-focus`, `balanced`. - `data_policy` (string, optional): Values: `none`, `no_training`, `zdr`. - `allow_fallbacks` (boolean, optional): - `max_fallback_attempts` (integer, optional): Default: `19`. - `providers` (array[string], optional): - `exclude_providers` (array[string], optional): ### Response (200) OK ### Response Properties - `workspace_id` (string, required): Workspace these defaults belong to - `routing_defaults` (RoutingDefaultsModel | null, optional): Current routing defaults (null if not set) - `propagation_status` (string | null, optional): "pending" when the change committed but the edge-KV sync did not complete immediately; the edge converges on its next read. Absent/null otherwise. Values: `pending`, `None`. ### Error Responses - **400**: Bad request - invalid parameters - **401**: Authentication failed - **403**: You do not have permission to perform this action - **404**: Resource not found ## Endpoint: DELETE /v1/workspaces/{workspace_id}/routing-defaults **Clear routing defaults** Clears all routing defaults for a workspace. Requires `routing:write` scope. ### Response (200) OK ### Response Properties - `workspace_id` (string, required): Workspace these defaults belong to - `routing_defaults` (RoutingDefaultsModel | null, optional): Current routing defaults (null if not set) - `propagation_status` (string | null, optional): "pending" when the change committed but the edge-KV sync did not complete immediately; the edge converges on its next read. Absent/null otherwise. Values: `pending`, `None`. ### Error Responses - **401**: Authentication failed - **403**: You do not have permission to perform this action - **404**: Resource not found ## Endpoint: GET /v1/workspaces/{workspace_id}/usage **Get workspace usage** Returns aggregated usage metrics for a workspace. Requires `usage:read` scope. ### Response (200) OK ### Response Properties - `data` (array[UsageAggregateItem], required): - `total_requests` (integer, required): Total requests across all groups - `total_cost_usd` (number, required): Total cost in USD across all groups - `period_start` (string, required): Start of the aggregation period - `period_end` (string, required): End of the aggregation period - `group_by` (string, required): Dimension used for grouping ### Error Responses - **401**: Authentication failed ## Endpoint: GET /v1/workspaces/{workspace_id}/requests **List requests** Lists request metadata for a workspace. Requires `usage:read` scope. ### Response (200) OK ### Response Properties - `requests` (array[RequestListItem], required): - `total` (integer, required): Total number of matching requests - `offset` (integer, optional): Current offset Default: `0`. - `limit` (integer, optional): Items per page Default: `100`. ### Error Responses - **401**: Authentication failed ## Endpoint: GET /v1/workspaces/{workspace_id}/requests/{request_id} **Get request detail** Returns metadata for a single request. Requires `usage:read` scope. ### Response (200) OK ### Response Properties - `request_id` (string, required): Unique request identifier - `timestamp` (string, required): When the request was made - `provider` (string, required): Provider that handled the request - `model` (string | null, optional): Requested model name - `model_used` (string | null, optional): Actual model used by the provider - `ttft_ms` (number | null, optional): Time to first token in milliseconds - `total_latency_ms` (number | null, optional): Total latency in milliseconds - `routing_decision_ms` (number | null, optional): Routing decision time in milliseconds - `throughput_tps` (number | null, optional): Throughput in tokens per second - `tokens_in` (integer | null, optional): Input token count - `tokens_out` (integer | null, optional): Output token count - `tokens_cached` (integer | null, optional): Cached token count - `tokens_reasoning` (integer | null, optional): Reasoning token count - `success` (boolean, required): Whether the request succeeded - `http_status` (integer, required): HTTP status code returned - `error_code` (string | null, optional): Error code if the request failed - `streaming` (boolean, optional): Whether the request used streaming Default: `False`. - `has_tools` (boolean, optional): Whether the request included tool definitions Default: `False`. - `has_json_mode` (boolean, optional): Whether JSON mode was enabled Default: `False`. - `cost_usd` (number | null, optional): Request cost in USD - `attempt_index` (integer, optional): Retry attempt index (0 = first attempt) Default: `0`. - `is_final_attempt` (boolean, optional): Whether this was the final attempt Default: `True`. - `trace_id` (string | null, optional): Trace identifier for correlation ### Error Responses - **401**: Authentication failed - **404**: Resource not found ## Endpoint: GET /v1/workspaces/{workspace_id}/budgets **List budgets** Returns the budgets for a workspace. Requires `budgets:read` scope. ### Response (200) OK ### Response Properties - `budgets` (array[BudgetResponse], required): - `count` (integer, required): Total number of budgets ### Error Responses - **401**: Authentication failed - **403**: You do not have permission to perform this action ## Endpoint: POST /v1/workspaces/{workspace_id}/budgets **Create budget** Creates a new budget limit for a workspace, API key, or BYOK provider scope. Requires `budgets:write` scope. Supports `Idempotency-Key` header. ### Request Parameters - `scope_type` (string, required): Budget target class Values: `workspace`, `api_key`, `byok_provider`. - `scope_id` (string | null, optional): Required for api_key scope (the target key ID in the same workspace) - `scope_provider` (string | null, optional): Required for byok_provider scope (e.g., "openai", "anthropic") - `period` (string, required): Budget period Values: `monthly`, `weekly`, `daily`. - `limit_usd` (number, required): Spend limit in USD (max 2 decimal places) - `enforce` (boolean, optional): Whether to enforce the budget at the edge (block requests when exhausted) Default: `True`. - `include_byok` (boolean, optional): Include BYOK-attributed spend in budget calculation (workspace and api_key scopes only) Default: `False`. ### Error Responses - **401**: Authentication failed - **400**: Invalid parameter value - **409**: Budget already exists for this scope and period, or idempotency conflict - **422**: Validation error ## Endpoint: GET /v1/workspaces/{workspace_id}/budgets/{budget_id} **Get budget** Returns a specific budget. Requires `budgets:read` scope. ### Response (200) OK ### Response Properties - `id` (string, required): Unique identifier for the budget - `workspace_id` (string, required): Workspace this budget belongs to - `scope_type` (string, required): What the budget applies to Values: `workspace`, `api_key`, `byok_provider`. - `scope_id` (string | null, optional): API key id for `api_key` scope (null otherwise) - `scope_provider` (string | null, optional): Provider identifier for `byok_provider` scope (null otherwise) - `period` (string, required): Budget reset period Values: `daily`, `weekly`, `monthly`. - `limit_usd` (number, required): Spend limit in USD - `enforce` (boolean, required): Whether requests are blocked when the budget is exceeded - `include_byok` (boolean, required): Whether BYOK usage counts toward the budget - `created_by` (string | null, optional): Identifier of the principal that created the budget - `created_at` (string, required): When the budget was created - `updated_at` (string, required): When the budget was last updated - `spend_microdollars` (integer, optional): Spend in the current period, in microdollars - `spend_usd` (number, optional): Spend in the current period, in USD - `percent_used` (number, optional): Fraction of the limit consumed in the current period - `propagation_status` (string | null, optional): "pending" when the change committed but the edge-KV sync did not complete immediately; the edge converges on its next read. Absent/null otherwise. Values: `pending`, `None`. ### Error Responses - **401**: Authentication failed - **403**: You do not have permission to perform this action - **404**: Resource not found ## Endpoint: PATCH /v1/workspaces/{workspace_id}/budgets/{budget_id} **Update budget** Updates mutable fields of an existing budget (limit, enforce, include_byok). Requires `budgets:write` scope. ### Request Parameters - `limit_usd` (number, optional): Updated spend limit in USD - `enforce` (boolean, optional): Updated enforcement setting - `include_byok` (boolean, optional): Updated BYOK inclusion setting ### Response (200) OK ### Response Properties - `id` (string, required): Unique identifier for the budget - `workspace_id` (string, required): Workspace this budget belongs to - `scope_type` (string, required): What the budget applies to Values: `workspace`, `api_key`, `byok_provider`. - `scope_id` (string | null, optional): API key id for `api_key` scope (null otherwise) - `scope_provider` (string | null, optional): Provider identifier for `byok_provider` scope (null otherwise) - `period` (string, required): Budget reset period Values: `daily`, `weekly`, `monthly`. - `limit_usd` (number, required): Spend limit in USD - `enforce` (boolean, required): Whether requests are blocked when the budget is exceeded - `include_byok` (boolean, required): Whether BYOK usage counts toward the budget - `created_by` (string | null, optional): Identifier of the principal that created the budget - `created_at` (string, required): When the budget was created - `updated_at` (string, required): When the budget was last updated - `spend_microdollars` (integer, optional): Spend in the current period, in microdollars - `spend_usd` (number, optional): Spend in the current period, in USD - `percent_used` (number, optional): Fraction of the limit consumed in the current period - `propagation_status` (string | null, optional): "pending" when the change committed but the edge-KV sync did not complete immediately; the edge converges on its next read. Absent/null otherwise. Values: `pending`, `None`. ### Error Responses - **401**: Authentication failed - **400**: Invalid parameter value - **404**: Resource not found - **403**: You do not have permission to perform this action - **422**: Validation error ## Endpoint: DELETE /v1/workspaces/{workspace_id}/budgets/{budget_id} **Delete budget** Deletes a budget limit. Requires `budgets:write` scope. ### Error Responses - **401**: Authentication failed - **403**: You do not have permission to perform this action - **404**: Resource not found ## Endpoint: GET /v1/workspaces/{workspace_id}/api-keys **List API keys** Returns API keys for a workspace. Excludes playground keys. Requires `keys:read` scope. ### Response (200) OK ### Response Properties - `object` (string, required): Object type identifier Values: `list`. - `data` (array[PublicApiKeyItem], required): - `count` (integer, required): Total number of API keys ### Error Responses - **401**: Authentication failed ## Endpoint: POST /v1/workspaces/{workspace_id}/api-keys **Create API key** Creates a new API key for the workspace. The full key value is returned only in this response and cannot be retrieved again. Requires `keys:write` scope. ### Request Parameters - `name` (string, required): Human-readable name for the key - `key_type` (string, optional): Key type — `api` for inference Values: `api`. - `scopes` (array[string], optional): Permission scopes to grant - `rate_limit_rpm` (integer, optional): Per-key rate limit in requests per minute (minimum 1) - `expires_at` (string, optional): Expiration timestamp - `budget` (ApiKeyBudget, optional): Budget configuration for an API key. - `period` (string, optional): Budget reset period Values: `daily`, `weekly`, `monthly`. - `limit_usd` (number, optional): Spend limit in USD (minimum $0.01, maximum $1,000,000, max 2 decimal places) - `enforce` (boolean, optional): Whether to block requests when budget is exceeded - `include_byok` (boolean, optional): Whether BYOK usage counts toward the budget ### Error Responses - **400**: Invalid parameter value - **401**: Authentication failed ## Endpoint: GET /v1/workspaces/{workspace_id}/api-keys/{api_key_id} **Get API key** Returns a specific API key's metadata. Requires `keys:read` scope. ### Response (200) OK ### Response Properties - `id` (string, required): Unique identifier for the API key - `workspace_id` (string, required): Workspace this key belongs to - `name` (string, required): Human-readable name for the key - `key_prefix` (string, required): Key prefix for identification (e.g. "ak_live_abcdefgh") - `profile` (string, required): Key profile derived from granted scopes Values: `inference`, `management`, `mixed`. - `scopes` (array[string], required): Granted permission scopes - `is_active` (boolean, required): Whether the key is currently active - `rate_limit_rpm` (integer | null, optional): Per-key rate limit in requests per minute (null inherits workspace default) - `expires_at` (string | null, optional): Expiration timestamp (null means no expiration) - `created_at` (string, required): When the key was created - `last_used_at` (string | null, optional): When the key was last used (null if never used) - `created_by_key_id` (string | null, optional): ID of the API key that created this key (null if created via dashboard) ### Error Responses - **401**: Authentication failed - **404**: Resource not found ## Endpoint: PATCH /v1/workspaces/{workspace_id}/api-keys/{api_key_id} **Update API key** Updates an API key's metadata or configuration. Requires `keys:write` scope. ### Request Parameters - `name` (string, optional): Human-readable name for the key - `rate_limit_rpm` (integer, optional): Per-key rate limit in requests per minute (minimum 1) - `expires_at` (string | null, optional): Expiration timestamp. Send null to remove expiration. - `is_active` (boolean, optional): Whether the key is active - `budget` (ApiKeyBudget, optional): Budget configuration for an API key. - `period` (string, optional): Budget reset period Values: `daily`, `weekly`, `monthly`. - `limit_usd` (number, optional): Spend limit in USD (minimum $0.01, maximum $1,000,000, max 2 decimal places) - `enforce` (boolean, optional): Whether to block requests when budget is exceeded - `include_byok` (boolean, optional): Whether BYOK usage counts toward the budget ### Response (200) OK ### Response Properties - `id` (string, required): Unique identifier for the API key - `workspace_id` (string, required): Workspace this key belongs to - `name` (string, required): Human-readable name for the key - `key_prefix` (string, required): Key prefix for identification - `profile` (string, required): Key profile derived from granted scopes Values: `inference`, `management`, `mixed`. - `scopes` (array[string], required): Granted permission scopes - `is_active` (boolean, required): Whether the key is currently active - `rate_limit_rpm` (integer | null, optional): Per-key rate limit in requests per minute - `expires_at` (string | null, optional): Expiration timestamp - `created_at` (string, required): When the key was created - `last_used_at` (string | null, optional): When the key was last used - `created_by_key_id` (string | null, optional): ID of the API key that created this key - `propagation_status` (string | null, optional): Status of change propagation to edge caches Values: `pending`, `None`. ### Error Responses - **400**: Invalid parameter value - **401**: Authentication failed - **404**: Resource not found ## Endpoint: DELETE /v1/workspaces/{workspace_id}/api-keys/{api_key_id} **Revoke API key** Revokes an API key. The key cannot be used after revocation. Requires `keys:write` scope. ### Error Responses - **400**: Invalid parameter value - **401**: Authentication failed - **404**: Resource not found ## Endpoint: GET /v1/workspaces/{workspace_id}/api-keys/{api_key_id}/usage **Get API key usage** Returns usage statistics for a specific API key. Requires `keys:read` or `usage:read` scope. ### Response (200) OK ### Response Properties - `key_id` (string, required): API key identifier - `workspace_id` (string, required): Workspace identifier - `period` (string, required): Aggregation period (e.g. day, week, month) - `start_date` (string, required): Period start date - `end_date` (string, required): Period end date - `request_count` (integer, required): Total requests made with this key - `error_count` (integer, required): Number of failed requests - `rate_limit_count` (integer, required): Number of rate-limited requests - `tokens_input` (integer, required): Total input tokens - `tokens_output` (integer, required): Total output tokens - `tokens_total` (integer, required): Total tokens (input + output) - `cost_usd` (number, required): Total cost in USD ### Error Responses - **401**: Authentication failed - **404**: Resource not found ## Endpoint: GET /v1/byok/providers **List BYOK providers** Returns providers that accept bring-your-own-key credentials, with each provider's conservative default account tier. Allowed `account_tier` values for key creation come from the tiers endpoint. ### Response (200) List of BYOK providers ### Response Properties - `object` (string, required): Values: `list`. - `data` (array[ByokProviderItem], required): - `count` (integer, required): ### Error Responses - **429**: Rate limit exceeded. Two sub-causes share this status: - `rate_limit_exceeded`: throughput-based rate limit. - `budget_exhausted`: account or project budget reached (self-service; not auto-retried by SDKs — requires adding credits or raising the limit). - **500**: Internal server error - **502**: API gateway is temporarily unavailable. - **503**: Service unavailable — transient issue. Possible causes: - All providers for the model are rate-limited or temporarily unavailable (`no_provider_available`) - Transient infrastructure issue (`service_unavailable`) Note: If the model doesn't support a requested capability (e.g., reasoning), the response is 400 with a specific code (e.g., `reasoning_not_supported`), not 503. If routing constraints excluded all providers, the response is 400 with a specific constraint code (e.g., `cost_constraint_exceeded`). ## Endpoint: GET /v1/byok/providers/{provider}/tiers **List provider account tiers** Returns the allowed `account_tier` values for a BYOK provider. Auriko derives each key's data policy from provider + tier — callers never submit a data policy directly. ### Response (200) Tier options for the provider ### Response Properties - `object` (string, required): Values: `list`. - `provider` (string, required): - `data` (array[ByokTierItem], required): - `count` (integer, required): ### Error Responses - **404**: Resource not found - **429**: Rate limit exceeded. Two sub-causes share this status: - `rate_limit_exceeded`: throughput-based rate limit. - `budget_exhausted`: account or project budget reached (self-service; not auto-retried by SDKs — requires adding credits or raising the limit). - **500**: Internal server error ## Endpoint: GET /v1/workspaces/{workspace_id}/byok-keys **List BYOK keys** Returns the workspace's BYOK keys as redacted metadata (masked key prefix only). Provider secrets are never returned by any endpoint. Requires `byok:read` scope. ### Response (200) OK ### Response Properties - `object` (string, required): Values: `list`. - `data` (array[PublicByokKeyItem], required): - `count` (integer, required): ### Error Responses - **401**: Authentication failed ## Endpoint: POST /v1/workspaces/{workspace_id}/byok-keys **Create BYOK key** Creates a BYOK key with create-time admission validation: the submitted secret is verified against the provider before activation. Invalid credentials are rejected and nothing is saved; transient validation failures return a retryable `502` and nothing is saved. The secret is write-only — accepted once here, encrypted at rest, and never returned by any endpoint. `Idempotency-Key` replays return the redacted metadata response only, never the secret. `account_tier` accepts the values published by the tiers endpoint; when omitted, Auriko auto-detects the tier where the provider exposes reliable signals and otherwise uses the provider's conservative default. Auriko derives the key's data policy from provider + tier. May return `202` with `propagation_status: "pending"` when the change is committed but edge routing propagation is still being applied. Requires `byok:write` scope. ### Request Parameters - `provider` (string, required): Provider identifier from `GET /v1/byok/providers` - `api_key` (string, required): Provider secret. Accepted once at creation, encrypted at rest, and never returned by any endpoint. - `name` (string | null, optional): Human-readable name (defaults to " Key") - `is_default` (boolean, optional): Whether this key becomes the provider's routing default Default: `True`. - `account_tier` (string | null, optional): Provider account tier from the tiers endpoint. Omit to let Auriko auto-detect or use the provider's conservative default. ### Error Responses - **401**: Authentication failed - **400**: Invalid parameter value - **409**: Idempotency-Key conflict - **422**: Validation error - **502**: Provider validation did not complete — retry the request ## Endpoint: GET /v1/workspaces/{workspace_id}/byok-keys/{byok_key_id} **Get BYOK key** Returns a BYOK key's redacted metadata. The provider secret is never returned. Requires `byok:read` scope. ### Response (200) OK ### Response Properties - `id` (string, required): Unique identifier for the BYOK key - `workspace_id` (string, required): Workspace this key belongs to - `provider` (string, required): Provider identifier - `name` (string, required): Human-readable name for the key - `key_prefix` (string, required): Masked display prefix of the submitted secret - `is_default` (boolean, required): Whether this key is the provider's default for routing - `disabled` (boolean, required): Disabled keys are excluded from routing; the secret stays encrypted at rest - `account_tier` (string | null, optional): Provider account tier used for rate-limit and data-policy routing - `account_tier_source` (string | null, optional): How the tier was determined Values: `auto_detected`, `user_specified`, `fallback`, `None`. - `validation_status` (string, required): Coarse validation status (no provider diagnostics) Values: `valid`, `pending`, `invalid`, `error`. - `created_at` (string, required): - `updated_at` (string, required): - `last_validated_at` (string | null, optional): When the key last passed validation (null if never) - `propagation_status` (string | null, optional): "pending" while a routing-affecting change for this key's provider is committed but edge propagation has not yet been applied Values: `pending`, `None`. ### Error Responses - **401**: Authentication failed - **404**: Resource not found ## Endpoint: PATCH /v1/workspaces/{workspace_id}/byok-keys/{byok_key_id} **Update BYOK key** Updates safe metadata and routing-affecting state: `name`, `is_default`, `account_tier`, and `disabled`. The provider secret is immutable and secret fields are rejected — to replace a secret, create a new key, set it as default, then delete the old key. May return `202` with `propagation_status: "pending"` when the change is committed but edge routing propagation is still being applied. Requires `byok:write` scope. ### Request Parameters - `name` (string | null, optional): New human-readable name - `is_default` (boolean | null, optional): Promote (true) or demote (false) this key as the provider's routing default - `account_tier` (string | null, optional): Provider account tier from the tiers endpoint - `disabled` (boolean | null, optional): Disable (true) to exclude the key from routing without deleting it ### Response (200) OK ### Response Properties - `id` (string, required): Unique identifier for the BYOK key - `workspace_id` (string, required): Workspace this key belongs to - `provider` (string, required): Provider identifier - `name` (string, required): Human-readable name for the key - `key_prefix` (string, required): Masked display prefix of the submitted secret - `is_default` (boolean, required): Whether this key is the provider's default for routing - `disabled` (boolean, required): Disabled keys are excluded from routing; the secret stays encrypted at rest - `account_tier` (string | null, optional): Provider account tier used for rate-limit and data-policy routing - `account_tier_source` (string | null, optional): How the tier was determined Values: `auto_detected`, `user_specified`, `fallback`, `None`. - `validation_status` (string, required): Coarse validation status (no provider diagnostics) Values: `valid`, `pending`, `invalid`, `error`. - `created_at` (string, required): - `updated_at` (string, required): - `last_validated_at` (string | null, optional): When the key last passed validation (null if never) - `propagation_status` (string | null, optional): "pending" while a routing-affecting change for this key's provider is committed but edge propagation has not yet been applied Values: `pending`, `None`. ### Error Responses - **401**: Authentication failed - **404**: Resource not found - **409**: State conflict — for example, setting a disabled key as default ## Endpoint: DELETE /v1/workspaces/{workspace_id}/byok-keys/{byok_key_id} **Delete BYOK key** Deletes a BYOK key. If the key was the provider's default, edge routing stops using it within the propagation window; may return `202` with `propagation_status: "pending"` while propagation applies. Requires `byok:write` scope. ### Error Responses - **401**: Authentication failed - **404**: Resource not found ## Endpoint: GET /v1/workspaces/{workspace_id}/billing/balance **Get credit balance** Returns the workspace credit balance, tier, and billing configuration. Requires `billing:read` scope. ### Response (200) Credit balance and billing details ### Response Properties - `balance_microdollars` (integer, required): Current balance in microdollars (1 USD = 1,000,000 μ$) - `balance_cents` (integer, required): Current balance in cents (computed) - `balance_dollars` (string, required): Current balance in dollars (computed, string for precision) - `lifetime_purchased_microdollars` (integer, required): Total credits ever purchased in microdollars - `lifetime_purchased_cents` (integer, required): Total credits ever purchased in cents (computed) - `lifetime_used_microdollars` (integer, required): Total credits ever consumed in microdollars - `lifetime_used_cents` (integer, required): Total credits ever consumed in cents (computed) - `auto_reload_enabled` (boolean, required): Whether auto-reload is enabled - `auto_reload_threshold_microdollars` (integer | null, optional): Balance threshold triggering auto-reload - `auto_reload_threshold_cents` (integer | null, optional): Balance threshold in cents (computed) - `auto_reload_amount_microdollars` (integer | null, optional): Target balance amount for auto-reload - `auto_reload_amount_cents` (integer | null, optional): Target balance amount in cents (computed) - `byok_monthly_cap` (integer | null, optional): Monthly BYOK request cap (null if unlimited) - `byok_monthly_remaining` (integer | null, optional): Remaining BYOK requests this month - `has_payment_method` (boolean, required): Whether a payment method is on file ### Error Responses - **400**: Bad request - invalid parameters - **401**: Authentication failed - **403**: You do not have permission to perform this action - **404**: Resource not found - **500**: Internal server error - **502**: API gateway is temporarily unavailable. ## Endpoint: GET /v1/workspaces/{workspace_id}/billing/purchases **List billing purchases** Returns credit purchase history for a workspace. Requires `billing:read` scope. ### Response (200) OK ### Response Properties - `purchases` (array[PurchaseResponse], required): - `total` (integer, required): Total number of purchases - `offset` (integer, optional): Current offset Default: `0`. - `limit` (integer, optional): Items per page Default: `20`. ### Error Responses - **400**: Bad request - invalid parameters - **401**: Authentication failed - **403**: You do not have permission to perform this action - **404**: Resource not found - **500**: Internal server error - **502**: API gateway is temporarily unavailable. ## Endpoint: GET /v1/workspaces/{workspace_id}/billing/usage **Get billing usage** Returns billing usage summary for a workspace. Requires `billing:read` scope. ### Response (200) OK ### Response Properties - `summary` (UsageSummary, required): Usage summary for a billing period. - `total_requests` (integer, required): Total API requests - `total_inference_microdollars` (integer, required): Total provider costs in micro-dollars - `total_inference_dollars` (number, required): Total provider costs in dollars - `total_platform_fee_microdollars` (integer, required): Total platform fees in micro-dollars - `total_platform_fee_dollars` (number, required): Total platform fees in dollars - `total_deducted_microdollars` (integer, required): Total credits deducted in micro-dollars - `total_deducted_dollars` (number, required): Total credits deducted in dollars - `period_start` (string, required): Start of the billing period - `period_end` (string, required): End of the billing period - `usage` (array[UsageDetailItem], required): - `total` (integer, required): Total number of usage entries - `offset` (integer, optional): Current offset Default: `0`. - `limit` (integer, optional): Items per page Default: `100`. ### Error Responses - **400**: Bad request - invalid parameters - **401**: Authentication failed - **403**: You do not have permission to perform this action - **404**: Resource not found - **500**: Internal server error - **502**: API gateway is temporarily unavailable. ## Endpoint: GET /v1/workspaces/{workspace_id}/billing/auto-reload **Get auto-reload settings** Returns auto-reload configuration for a workspace. API-key callers receive a `payment_method_configured` boolean instead of card details. Requires `billing:read` scope. ### Response (200) Auto-reload settings ### Response Properties - `enabled` (boolean, optional): - `threshold_cents` (integer, optional): - `threshold_dollars` (number, optional): - `amount_cents` (integer, optional): - `amount_dollars` (number, optional): - `payment_method_configured` (boolean, optional): - `failed_count` (integer, optional): - `disabled_at` (string | null, optional): ### Error Responses - **401**: Authentication failed ## Endpoint: GET /v1/workspaces/{workspace_id}/billing/subscription **Get subscription status** Returns tier, subscription status, and period information. Does not trigger Stripe reconciliation. Requires `billing:read` scope. ### Response (200) Subscription status ### Response Properties - `tier` (string, optional): - `subscription_status` (string, optional): - `subscription_current_period_end` (string | null, optional): - `cancel_at_period_end` (boolean, optional): - `subscription_cancel_at` (string | null, optional): - `trial_expires_at` (string | null, optional): ### Error Responses - **401**: Authentication failed ## Endpoint: GET /v1/workspaces/{workspace_id}/members **List workspace members** Returns workspace members with role and profile info. Requires `members:read` scope. ### Response (200) Workspace members list ### Response Properties - `members` (array[object], optional): - `count` (integer, optional): ### Error Responses - **401**: Authentication failed ## Endpoint: GET /v1/workspaces/{workspace_id}/invites **List workspace invites** Returns pending and expired invitations. Requires `members:read` scope. ### Response (200) Workspace invites list ### Response Properties - `invites` (array[object], optional): - `count` (integer, optional): ### Error Responses - **401**: Authentication failed ## Endpoint: POST /v1/workspaces/{workspace_id}/invites **Create workspace invite** Sends a workspace invitation email. API-key callers can only create member-role invites. Requires `invites:write` scope. Supports `Idempotency-Key` header. ### Request Parameters - `email` (string, required): Email address to invite - `role` (string, optional): Role for the invited member Values: `admin`, `member`. Default: `member`. ### Error Responses - **401**: Authentication failed - **409**: Idempotency-Key conflict ## Endpoint: DELETE /v1/workspaces/{workspace_id}/invites/{invite_id} **Cancel workspace invite** Cancels a pending invitation. Requires `invites:write` scope. ### Response (200) Invite cancelled ### Response Properties - `success` (boolean, optional): - `message` (string, optional): ### Error Responses - **401**: Authentication failed - **403**: You do not have permission to perform this action - **404**: Resource not found - **409**: Invite is not in a cancellable state ## Endpoint: POST /v1/workspaces/{workspace_id}/invites/{invite_id}/resend **Resend workspace invite** Resends an invitation with a new token. API-key callers can only resend member-role invites. Requires `invites:write` scope. ### Response (200) Invite resent ### Response Properties - `success` (boolean, optional): - `message` (string, optional): ### Error Responses - **401**: Authentication failed - **403**: You do not have permission to perform this action - **404**: Resource not found - **409**: Invite is not in a resendable state ## Endpoint: GET /v1/workspaces/{workspace_id}/audit/events **List audit events** Returns workspace management audit history. Supports filtering by time range, actor, action, and target. Requires `audit:read` scope. ### Response (200) Audit events list ### Response Properties - `events` (array[object], optional): - `total` (integer, optional): - `limit` (integer, optional): - `offset` (integer, optional): ### Error Responses - **401**: Authentication failed - **403**: You do not have permission to perform this action ## Endpoint: GET /v1/me **Get API key identity** Returns database-fresh credential introspection for the calling API key: credential type, status, derived profile, key metadata, workspace, granted scopes, structured rate limits, and expiry. Any valid active API key may call this endpoint — no scope is required. ### Response (200) API key identity ### Response Properties - `credential_type` (string, required): The authentication scheme used by the caller. Values: `api_key`. - `status` (string, required): Always "active" on success — disabled or expired keys fail authentication with 401 before this response is produced. Values: `active`. - `profile` (string, required): Capability class derived server-side from the key's scopes. Values: `inference`, `management`, `mixed`. - `key_id` (string, required): The API key's resource ID (not a secret). - `key_prefix` (string, required): The key's display prefix (e.g. "ak_live_abc1"). - `key_name` (string, required): Human-readable key name. - `workspace` (object, required): The workspace this key is scoped to. - `id` (string, required): - `name` (string, required): - `scopes` (array[string], required): Granted scopes for fine-grained capability decisions. - `rate_limits` (object, required): - `inference_rpm` (integer, required): Requests per minute on inference endpoints (key override or tier default). - `management_reads_rpm` (integer, required): Requests per minute on management read endpoints. - `management_writes_rpm` (integer, required): Requests per minute on management write endpoints. - `expires_at` (string | null, required): Key expiry, or null if the key does not expire. - `created_at` (string, required): ### Error Responses - **401**: Authentication failed - **429**: Rate limit exceeded. Two sub-causes share this status: - `rate_limit_exceeded`: throughput-based rate limit. - `budget_exhausted`: account or project budget reached (self-service; not auto-retried by SDKs — requires adding credits or raising the limit). ## Endpoint: POST /v1/messages **Create a message (Anthropic format)** Creates a model response using the Anthropic Messages API format. Auriko routes the request to the optimal provider based on your routing preferences (cost, latency, throughput, etc.). ## Streaming When `stream: true`, responses are delivered as Server-Sent Events (SSE) using Anthropic's native event types: `message_start`, `content_block_start`, `content_block_delta`, `content_block_stop`, `message_delta`, `message_stop`. A terminal `auriko_metadata` event delivers routing metadata (Auriko extension). ## Extended Thinking Use the `thinking` parameter to enable extended thinking. Response will include `thinking` and optionally `redacted_thinking` content blocks. ## Multi-Model Routing Use `gateway.models[]` instead of `model` to enable multi-model routing. ### Request Parameters - `model` (string, optional): Model ID. Mutually exclusive with `gateway.models`. Examples: `claude-sonnet-4-20250514`, `claude-haiku-4-5-20251001` - `messages` (array[AnthropicMessage], required): Messages in the conversation - `system` (string | array, optional): System prompt (string or array of text blocks with optional cache_control) - `max_tokens` (integer, required): Maximum output tokens to generate - `stream` (boolean, optional): Enable streaming responses (SSE) Default: `False`. - `tools` (array[AnthropicTool], optional): Tools available for the model to call - `tool_choice` (AnthropicToolChoice, optional): - `temperature` (number, optional): Sampling temperature - `top_p` (number, optional): Nucleus sampling parameter - `top_k` (integer, optional): Top-K sampling parameter - `min_p` (number, optional): Min-P sampling. Forwarded to providers that support it. - `top_a` (number, optional): Top-A sampling. Forwarded to providers that support it. - `repetition_penalty` (number, optional): Repetition penalty. Forwarded to providers that support it. - `stop_sequences` (array[string], optional): Custom stop sequences - `thinking` (AnthropicThinkingConfig, optional): Controls extended thinking behavior. - `metadata` (object, optional): Anthropic request metadata - `user_id` (string, optional): - `gateway` (object, optional): Auriko gateway directives. Controls routing, metadata, and multi-model selection. - `routing` (RoutingOptions, optional): Auriko routing configuration (20 fields). Controls how Auriko selects providers for your request. All fields are optional. Setting a field to `null` is equivalent to omitting it. - `metadata` (RequestMetadata, optional): Optional request metadata for tracking and observability. Attached via the `gateway.metadata` field on chat completion requests. - `models` (array[string], optional): Multi-model routing. Mutually exclusive with top-level `model`. - `extensions` (Extensions, optional): Auriko extensions for provider-specific passthrough. For reasoning control, use the top-level `reasoning_effort` parameter instead of extensions. ## Provider Passthrough Pass provider-specific parameters directly: - `anthropic`: Anthropic-specific parameters - `openai`: OpenAI-specific parameters - `google`: Google/Gemini-specific parameters - `deepseek`: DeepSeek-specific parameters Passthrough parameters are forwarded as-is to the target provider. - `prompt_cache_key` (string, optional): Prompt caching identifier - `safety_identifier` (string, optional): Safety policy identifier - `verbosity` (string, optional): Output verbosity control ### Request Examples **Basic message**: ```json { "model": "claude-sonnet-4-20250514", "max_tokens": 1024, "messages": [ { "role": "user", "content": "What is the capital of France?" } ] } ``` **With extended thinking**: ```json { "model": "claude-sonnet-4-20250514", "max_tokens": 16000, "thinking": { "type": "enabled", "budget_tokens": 10000 }, "messages": [ { "role": "user", "content": "Solve this step by step: what is 127 * 389?" } ] } ``` ### Response (200) Successful message response. For streaming (`stream: true`), responses use Anthropic SSE format: `event: \ndata: \n\n` Event types: `message_start`, `ping`, `content_block_start`, `content_block_delta` (with delta types: `thinking_delta`, `signature_delta`, `text_delta`, `input_json_delta`), `content_block_stop`, `message_delta`, `message_stop`. A terminal `event: auriko_metadata` delivers routing metadata. ### Response Properties - `id` (string, required): Unique message identifier (gateway request ID) - `type` (string, required): Constant: `message`. - `role` (string, required): Constant: `assistant`. - `content` (array[AnthropicContentBlock], required): Content blocks (text, thinking, tool_use, etc.) - `model` (string, required): Model that generated the response (canonical model ID from request) - `stop_reason` (string | null, required): Reason generation stopped Values: `end_turn`, `max_tokens`, `stop_sequence`, `tool_use`, `pause_turn`, `refusal`, `None`. - `stop_sequence` (string | null, required): Always null (Auriko does not surface matched stop sequences) - `usage` (AnthropicUsage, required): - `input_tokens` (integer, required): Input tokens that were neither read from nor written to cache - `output_tokens` (integer, required): Output tokens generated - `cache_creation_input_tokens` (integer, optional): Input tokens written to cache - `cache_read_input_tokens` (integer, optional): Input tokens read from cache ### Error Responses - **400**: Bad request - invalid parameters (Anthropic format) - **401**: Authentication failed (Anthropic format) - **429**: Rate limit exceeded (Anthropic format) - **500**: Internal server error (Anthropic format) - **503**: Service unavailable (Anthropic format) ## Endpoint: POST /v1/messages/count_tokens **Count tokens (Anthropic format)** Counts the number of tokens in a message payload without generating a response. Uses the Anthropic Messages API format. Token counts for non-Claude models use a reference tokenizer. When this occurs, the response includes an `X-Token-Count-Model` header indicating the model used for counting. Only `gateway.routing` is allowed. Other gateway keys return 400. ### Request Parameters - `model` (string, required): Model to use for tokenization. For non-Claude models, a reference tokenizer is used (response includes X-Token-Count-Model header). - `messages` (array[AnthropicMessage], required): Messages to count tokens for - `system` (string | array, optional): System prompt (counted alongside messages) - `tools` (array[AnthropicTool], optional): Tools to include in token count - `tool_choice` (AnthropicToolChoice, optional): - `thinking` (AnthropicThinkingConfig, optional): Controls extended thinking behavior. - `gateway` (object, optional): Auriko gateway directives. For count_tokens, only `routing` is allowed. Other gateway keys return 400. - `routing` (RoutingOptions, optional): Auriko routing configuration (20 fields). Controls how Auriko selects providers for your request. All fields are optional. Setting a field to `null` is equivalent to omitting it. ### Response (200) Token count result ### Response Properties - `input_tokens` (integer, required): Number of input tokens ### Error Responses - **400**: Bad request - invalid parameters (Anthropic format) - **401**: Authentication failed (Anthropic format) - **429**: Rate limit exceeded (Anthropic format) - **500**: Internal server error (Anthropic format) - **503**: Service unavailable (Anthropic format) ## Schema: Message Variants (discriminator: `role`): `SystemMessage`, `UserMessage`, `AssistantMessage`, `ToolMessage`, `DeveloperMessage`, `FunctionMessage` **SystemMessage**: - `role` (string, required): Constant: `system`. - `content` (string | array, required): - `name` (string, optional): **UserMessage**: - `role` (string, required): Constant: `user`. - `content` (string | array, required): - `name` (string, optional): **AssistantMessage**: - `role` (string, required): Constant: `assistant`. - `content` (string | null | array, optional): - `name` (string, optional): - `tool_calls` (array | null, optional): - `reasoning` (array | null, optional): Structured reasoning blocks with cryptographic signatures for multi-turn round-trip. - `reasoning_content` (string | null, optional): Reasoning content from the model's previous response. Echo back the value from the assistant message's reasoning_content field for DeepSeek multi-turn tool calling. Other providers accept but ignore this field. - `refusal` (string | null, optional): - `function_call` (object, optional): **Deprecated.** **ToolMessage**: - `role` (string, required): Constant: `tool`. - `content` (string, required): - `tool_call_id` (string, required): **DeveloperMessage**: - `role` (string, required): Constant: `developer`. - `content` (string, required): - `name` (string, optional): **FunctionMessage**: - `role` (string, required): Constant: `function`. - `content` (string | null, required): - `name` (string, required): ## Schema: RoutingOptions Auriko routing configuration (20 fields). Controls how Auriko selects providers for your request. All fields are optional. Setting a field to `null` is equivalent to omitting it. - `optimize` (string | null, optional): Optimization strategy: - `cost`: Minimize cost per token (well-rounded) - `cost-focus`: Aggressively minimize cost (default) - `ttft`: Minimize time to first token (well-rounded) - `ttft-focus`: Aggressively minimize time to first token - `tps`: Maximize tokens per second (well-rounded) - `tps-focus`: Aggressively maximize tokens per second - `balanced`: All dimensions weighted evenly Values: `cost`, `cost-focus`, `ttft`, `ttft-focus`, `tps`, `tps-focus`, `balanced`, `None`. Default: `cost-focus`. - `weights` (object | null, optional): Custom scoring weights for routing optimization. When provided, overrides the `optimize` preset coefficients. All values must be non-negative. At least one dimension must be > 0. Unspecified dimensions default to 0. Server normalizes to sum to 1.0. - `cost` (number | null, optional): Weight for cost minimization. - `ttft` (number | null, optional): Weight for time-to-first-token optimization. - `throughput` (number | null, optional): Weight for tokens-per-second optimization. - `ttft_percentile` (string | null, optional): Which percentile to use for TTFT metrics in scoring and constraint filtering. p50 uses median, p95 uses worst-case. Default: p50. Values: `p50`, `p95`, `None`. Default: `p50`. - `throughput_percentile` (string | null, optional): Which percentile to use for throughput metrics in scoring and constraint filtering. p50 uses median, p95 uses worst-case. Default: p50. Values: `p50`, `p95`, `None`. Default: `p50`. - `max_cost_per_1m` (number | null, optional): Maximum cost per 1 million tokens (USD). Providers exceeding this cost are excluded from selection. - `max_ttft_ms` (integer | null, optional): Maximum time to first token in milliseconds. Providers with estimated TTFT above this threshold are excluded. - `min_throughput_tps` (number | null, optional): Minimum throughput in tokens per second. Providers with estimated throughput below this threshold are excluded. - `providers` (array | null, optional): Provider allowlist. Only consider these providers. Examples: `["openai", "anthropic", "fireworks_ai"]` - `exclude_providers` (array | null, optional): Provider blocklist. Exclude these providers. Examples: `["together_ai"]` - `prefer` (string | null, optional): Preference boost for this provider. Provider will be selected if it meets constraints. - `mode` (string | null, optional): How to interpret `gateway.models[]` array: - `pool` (default): Route to best provider across all models - `fallback`: Try models in order until one succeeds Values: `pool`, `fallback`, `None`. Default: `pool`. - `allow_fallbacks` (boolean | null, optional): Enable automatic fallback to alternative providers on failure Default: `True`. - `max_fallback_attempts` (integer | null, optional): Maximum fallback attempts before giving up (chain length 20 = primary + 19 fallbacks). Range: 1-19. Default: `19`. - `timeout_ms` (integer | null, optional): Per-attempt timeout in milliseconds. For streaming requests: time to first SSE byte. For non-streaming requests: time to complete response. Default: 120000 (120s streaming first-byte), 300000 (5min non-streaming total). - `deadline_ms` (integer | null, optional): Request deadline in milliseconds. Hard wall-clock cap across all fallback attempts. Must be >= timeout_ms when both are set. Non-streaming requests default to 1080000 (18 minutes); streaming has no default. - `data_policy` (string | null, optional): Data retention policy requirement: - `none`: No restrictions (default) - `no_training`: Provider must not use data for training - `zdr`: Zero Data Retention (strictest) Values: `none`, `no_training`, `zdr`, `None`. Default: `none`. - `only_byok` (boolean | null, optional): Only use Bring Your Own Key (BYOK) providers. Mutually exclusive with `only_platform`. Returns 400 if both are set. Default: `False`. - `only_platform` (boolean | null, optional): Only use platform-managed API keys. Mutually exclusive with `only_byok`. Returns 400 if both are set. Default: `False`. - `require_parameters` (boolean | null, optional): When true, only route to providers whose accepted_params includes all optional parameters sent in this request. Default: false. Default: `False`. - `tier` (string | null, optional): Opt in to a pricing tier. Currently supported: `priority` (Anthropic fast mode — 2.5x speed at 6x cost). Note: Auriko's "priority" tier refers to Anthropic Fast Mode, not Anthropic's separate Priority Tier (committed capacity SLA). Omitting this field (default) excludes premium-tier offerings from routing. Values: `priority`, `None`. ## Schema: Extensions Auriko extensions for provider-specific passthrough. For reasoning control, use the top-level `reasoning_effort` parameter instead of extensions. ## Provider Passthrough Pass provider-specific parameters directly: - `anthropic`: Anthropic-specific parameters - `openai`: OpenAI-specific parameters - `google`: Google/Gemini-specific parameters - `deepseek`: DeepSeek-specific parameters Passthrough parameters are forwarded as-is to the target provider. - `anthropic` (object, optional): Anthropic-specific parameters (passed through) - `openai` (object, optional): OpenAI-specific parameters (passed through) - `google` (object, optional): Google/Gemini-specific parameters (passed through) - `deepseek` (object, optional): DeepSeek-specific parameters (passed through) ## Schema: RoutingMetadata Routing decision metadata included in successful responses. 8 STABLE fields (4 required + 4 optional) in the current public contract. - `provider` (string, required): Provider name (e.g., "fireworks_ai", "anthropic") - `provider_model_id` (string, required): Provider's model ID - `model_canonical` (string, required): Canonical model ID requested - `routing_strategy` (string, required): Strategy used for routing. Known values: `cost`, `cost-focus`, `ttft`, `ttft-focus`, `tps`, `tps-focus`, `balanced`, `custom`. `custom` is returned when explicit `routing.weights` are provided. Additional strategies may be added in future versions. - `ttft_ms` (number, optional): Time to first token (streaming only) - `throughput_tps` (number, optional): Output throughput (tokens per second) - `cost` (CostInfo, optional): Cost for the request - `warnings` (array[StructuredWarning], optional): Structured warnings emitted when the gateway modifies or ignores part of the request (e.g., unsupported parameters, blocked fields). ## Schema: CostInfo Cost for the request - `usd` (number, required): Billable cost in USD - `cache_savings_percent` (integer, optional): Cache savings as integer percentage (0-100). Present only when savings > 0. - `cache_savings_usd` (number, optional): Cache savings in USD. Present only when savings > 0. ## Error Response Details - **BadRequest**: Bad request - invalid parameters — example: "Missing required parameter: 'model'." - **Unauthorized**: Authentication failed — code: `invalid_api_key`, message: "API key is invalid." - **ModelNotFound**: Model not found — code: `model_not_found`, message: "Model 'unknown-model' is not in the catalog. See https://api.auriko.ai/v1/directory/models for available models." - **RateLimited**: Rate limit exceeded. Two sub-causes share this status: - `rate_limit_exceeded`: throughput-based rate limit. - `budget_exhausted`: account or project budget reached (self-service; not auto-retried by SDKs — requires adding credits or raising the limit). — example: "Rate limit exceeded. Retry after 60 seconds." - **InternalError**: Internal server error — code: `internal_error`, message: "An unexpected error occurred. Contact support with the request ID." - **ServiceUnavailable**: Service unavailable — transient issue. Possible causes: - All providers for the model are rate-limited or temporarily unavailable (`no_provider_available`) - Transient infrastructure issue (`service_unavailable`) Note: If the model doesn't support a requested capability (e.g., reasoning), the response is 400 with a specific code (e.g., `reasoning_not_supported`), not 503. If routing constraints excluded all providers, the response is 400 with a specific constraint code (e.g., `cost_constraint_exceeded`). — example: "No provider available for model 'gpt-4o'. Try a different model." - **ProviderError**: Upstream provider failure (Bad Gateway). All non-timeout 5xx errors from upstream providers are normalized to 502 (code: `upstream_error`). Upstream failures may also surface as: - 429: Provider rate limit (code: `rate_limit_exceeded`) - 504: Provider timed out (code: `upstream_timeout`) These use their respective status codes with the same `ErrorResponse` body format. — code: `upstream_error`, message: "Model 'gpt-4o' is temporarily unavailable. Retry in a few seconds." - **ProviderTimeout**: Upstream provider timed out. The client may retry with a longer timeout. — code: `upstream_timeout`, message: "Model 'gpt-4o' timed out. Retry or use a smaller input." - **Forbidden**: You do not have permission to perform this action — code: `insufficient_permissions`, message: "You do not have permission to perform this action." - **NotFound**: Resource not found — code: `resource_not_found`, message: "The requested resource was not found." - **GatewayUnavailable**: API gateway is temporarily unavailable. — code: `upstream_error`, message: "Gateway is temporarily unavailable. Retry in a few seconds." - **AnthropicBadRequest**: Bad request - invalid parameters (Anthropic format) — code: ``, message: "Missing required parameter: 'max_tokens'." - **AnthropicUnauthorized**: Authentication failed (Anthropic format) — code: ``, message: "API key is invalid." - **AnthropicRateLimit**: Rate limit exceeded (Anthropic format) — code: ``, message: "Rate limit exceeded. Retry after 60 seconds." - **AnthropicInternalError**: Internal server error (Anthropic format) — code: ``, message: "An unexpected error occurred." - **AnthropicServiceUnavailable**: Service unavailable (Anthropic format) — code: ``, message: "Service is temporarily unavailable." === # Auriko SDKs ## Page: Integration Guide Every integration channel provides Auriko's routing, fallback, cost optimization, and rate limiting — these features are **server-side**. The channel you choose affects developer ergonomics, not platform capability. --- ## Page: Integration Guide > Section: Which integration should I use? | If you... | Use | Tier | |---|---|---| | Want the full Auriko experience | [Native SDK](/sdk/python) ([TypeScript](/sdk/typescript)) (`auriko.AsyncClient`) | 1 — Primary | | Already use LangChain, LlamaIndex, CrewAI, or ADK | [Framework adapter](/frameworks/langchain) | 2 — Framework | | Use the Vercel AI SDK (TypeScript) | [`@auriko/ai-sdk-provider`](/frameworks/vercel-ai-sdk) | 2 — Framework | | Use Claude Code or Claude Agent SDK | Set `ANTHROPIC_BASE_URL` env var ([guide](/frameworks/claude-agent-sdk)) | 2 — Framework | | Want to point existing OpenAI code at Auriko | `AsyncOpenAI(base_url="https://api.auriko.ai/v1")` | 3 — Migration | | Need to pass an OpenAI client to a framework that discards routing metadata | [`AurikoAsyncOpenAI`](/sdk/python-reference#aurikoasyncopenai-experimental) (experimental) | 4 — Experimental | If you just want to point existing OpenAI code at Auriko, use `AsyncOpenAI(base_url=...)` directly. `AurikoAsyncOpenAI` is experimental and only needed when a framework discards routing metadata from responses. --- ## Page: Integration Guide > Section: Feature comparison | Feature | Native SDK | Framework Adapters | Plain OpenAI API | `AurikoAsyncOpenAI` (experimental) | Vercel AI SDK Provider | Claude Agent SDK / Anthropic SDK | |---|---|---|---|---|---|---| | Routing options | `gateway={...}` or `GatewayOptions(...)` | Adapter-specific params | `extra_body={"gateway": {...}}` | `extra_body={"gateway": {...}}` | `createAuriko({ routing })` | `extra_body={"gateway": {...}}` | | Metadata access | `response.routing_metadata` | Adapter-specific | `parse_routing_metadata(response)` or headers | `client.last_routing_metadata` | `result.providerMetadata?.auriko` | Response headers | | Error types | `AurikoAPIError` hierarchy | `AurikoAPIError` (automatic) | `openai.APIStatusError` | Dual-inheritance (both hierarchies) | `APICallError` (`@ai-sdk/provider`) | `anthropic.APIStatusError` | | Streaming metadata | On stream object | Adapter-specific | Response headers | Automatic | `await result.providerMetadata` | `auriko_metadata` SSE event | | Dependencies | `auriko` | `auriko[framework]` | `openai` (or any HTTP client) | `auriko[openai-compat]` | `@auriko/ai-sdk-provider` + `ai ^6.0.0` | `anthropic` SDK or Claude Code CLI | --- ## Page: Integration Guide > Section: Error behavior by channel | Channel | Error type | Automatic? | |---|---|---| | Native SDK | `AurikoAPIError` hierarchy | Yes | | Framework adapters | `AurikoAPIError` via `map_openai_error()` | Yes | | Plain OpenAI API | `openai.APIStatusError` | Opt-in via `map_openai_error()` | | `AurikoAsyncOpenAI` (experimental) | Dual-inheritance (both hierarchies) | HTTP-level only | | Vercel AI SDK provider | `APICallError` (`@ai-sdk/provider`) | Yes | | Claude Agent SDK / Anthropic SDK | `anthropic.APIStatusError` | Yes | `AurikoAsyncOpenAI` dual-inheritance errors are catchable as both `auriko.RateLimitError` and `openai.RateLimitError`. Mid-stream SSE errors (raised after the HTTP 200 during `stream=True`) remain unmapped `openai.APIError` — the bridge covers HTTP-level status errors only. --- ## Page: Python SDK The `auriko` Python package provides an OpenAI-compatible client for the Auriko API. Complete API reference with all types, parameters, and examples --- ## Page: Python SDK > Section: Installation ```bash pip install auriko ``` Requires Python 3.10 or later. --- ## Page: Python SDK > Section: Get started ```python from auriko import Client client = Client() # reads AURIKO_API_KEY from environment response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` --- ## Page: Python SDK > Section: Configure ### API Key ```python import os # Option 1: Auto-detect from AURIKO_API_KEY env var (recommended) client = Client() # Option 2: Pass explicitly client = Client(api_key=os.environ["AURIKO_API_KEY"]) ``` ### Base URL ```python # Default: https://api.auriko.ai/v1 # Override for self-hosted or proxy setups: client = Client(base_url="https://your-proxy.example.com/v1") ``` ### Timeout ```python client = Client(timeout=60.0) # seconds ``` ### Retries ```python client = Client(max_retries=3) # default is 2 ``` --- ## Page: Python SDK > Section: Create chat completions ### Basic request Send a chat completion request: ```python response = client.chat.completions.create( model="gpt-5.4", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is 2+2?"} ] ) print(response.choices[0].message.content) ``` ### With routing options ```python response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], gateway={ "routing": { "optimize": "cost", "max_ttft_ms": 1000, }, } ) # Access routing metadata print(f"Provider: {response.routing_metadata.provider}") if response.routing_metadata.cost: print(f"Cost: ${response.routing_metadata.cost.usd:.6f}") ``` You can also pass a `RoutingOptions` object for IDE autocomplete and validation: ```python from auriko.route_types import GatewayOptions, Optimize, RoutingOptions response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], gateway=GatewayOptions(routing=RoutingOptions(optimize=Optimize.COST, max_ttft_ms=1000)), ) ``` **All routing fields:** | Field | Type | Description | |-------|------|-------------| | `optimize` | `Optimize` | Strategy: `"cost"`, `"cost-focus"`, `"ttft"`, `"ttft-focus"`, `"tps"`, `"tps-focus"`, `"balanced"` | | `weights` | `dict[str, float]` | Custom scoring weights: `cost`, `ttft`, `throughput`. Overrides preset. | | `ttft_percentile` | `str` | TTFT scoring percentile: `"p50"` (default) or `"p95"` | | `throughput_percentile` | `str` | Throughput scoring percentile: `"p50"` (default) or `"p95"` | | `max_cost_per_1m` | `float` | Max $ per 1M tokens (average of input + output) | | `max_ttft_ms` | `int` | Max TTFT in milliseconds | | `min_throughput_tps` | `float` | Min throughput in tokens/sec | | `providers` | `list[str]` | Allowlist of providers | | `exclude_providers` | `list[str]` | Blocklist of providers | | `prefer` | `str` | Preferred provider (soft preference) | | `mode` | `Mode` | `"pool"` (default) or `"fallback"` | | `allow_fallbacks` | `bool` | Enable fallback on failure | | `max_fallback_attempts` | `int` | Max fallback retries | | `data_policy` | `DataPolicy` | `"none"`, `"no_training"`, `"zdr"` | | `only_byok` | `bool` | Only use BYOK providers | | `only_platform` | `bool` | Only use platform providers | See [Advanced Routing](/guides/advanced-routing) for detailed strategy guides. ### Multi-model routing Route a request across multiple models. The router picks the best option based on your routing strategy: ```python response = client.chat.completions.create( messages=[{"role": "user", "content": "Explain quantum computing briefly."}], gateway={ "models": ["gpt-4o", "claude-sonnet-4-20250514", "gemini-2.5-flash"], "routing": {"optimize": "cost"}, }, ) print(f"Model used: {response.model}") print(f"Provider: {response.routing_metadata.provider}") print(response.choices[0].message.content) ``` `model` and `gateway.models` are mutually exclusive. Specify exactly one. Passing both raises `BadRequestError`. ### Reasoning effort Enable extended reasoning for complex tasks using the `reasoning_effort` parameter: ```python response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Solve step by step: what is 23! / 20!?"}], reasoning_effort="high", ) # Access the reasoning output (if the model returns it) if response.choices[0].message.reasoning_content: print(f"Reasoning: {response.choices[0].message.reasoning_content}") print(f"Answer: {response.choices[0].message.content}") ``` You can also pass provider-specific parameters through `extensions`: ```python response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], extensions={"openai": {"logit_bias": {"1234": -100}}} ) ``` See [Extensions and Thinking](/guides/extensions-and-thinking) for provider details and streaming thinking output. ### Request metadata Attach metadata to requests for tracking and analytics: ```python response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], gateway={"metadata": {"user_id": "user-123", "tags": ["premium"]}}, ) ``` Valid metadata fields: `user_id`, `tags` (list), `trace_id`, and `custom_fields` (dict for arbitrary key-value pairs). See the [Python SDK Reference](/sdk/python-reference#parameters) for field constraints. ### Stream responses ```python stream = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Count to 10"}], stream=True ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` After consuming all chunks, access stream-level metadata: ```python print(f"\nProvider: {stream.routing_metadata.provider}") print(f"Tokens: {stream.usage.total_tokens}") print(f"Request ID: {stream.response_headers.request_id}") ``` Use a context manager for automatic cleanup: ```python with client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Count to 10"}], stream=True ) as stream: for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) # stream is automatically closed ``` Or close manually with `stream.close()`. Routing metadata, usage, and response headers are available only after consuming all chunks. See [Streaming Guide](/guides/streaming) for full patterns including tool call streaming. ### Tool calling ```python tools = [ { "type": "function", "function": { "name": "get_weather", "description": "Get weather for a city", "parameters": { "type": "object", "properties": { "city": {"type": "string"} }, "required": ["city"] } } } ] response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "What's the weather in Paris?"}], tools=tools ) if response.choices[0].message.tool_calls: tool_call = response.choices[0].message.tool_calls[0] print(f"Function: {tool_call.function.name}") print(f"Arguments: {tool_call.function.arguments}") ``` See [Tool Calling Guide](/guides/tool-calling) for multi-turn tool conversations. --- ## Page: Python SDK > Section: Read response headers Every response and error includes a `response_headers` object with typed accessors: ```python response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}] ) response.response_headers.request_id # str | None response.response_headers.rate_limit_remaining # int | None response.response_headers.rate_limit_limit # int | None response.response_headers.rate_limit_reset # str | None response.response_headers.credits_balance_microdollars # int | None response.response_headers.get("x-custom-header") # generic lookup ``` | Property | Header | Type | |----------|--------|------| | `request_id` | `x-request-id` | `str \| None` | | `rate_limit_remaining` | `x-ratelimit-remaining-requests` | `int \| None` | | `rate_limit_limit` | `x-ratelimit-limit-requests` | `int \| None` | | `rate_limit_reset` | `x-ratelimit-reset-requests` | `str \| None` | | `credits_balance_microdollars` | `x-credits-balance-microdollars` | `int \| None` | Error objects also carry `response_headers`. Use `e.response_headers.request_id` when filing support tickets to correlate with server logs. See the [Python SDK Reference](/sdk/python-reference#response-headers) for the complete `ResponseHeaders` API. --- ## Page: Python SDK > Section: Read token usage The `Usage` object on every response carries optional detail breakdowns: ```python response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}] ) usage = response.usage # Prompt token breakdown if usage.prompt_tokens_details: print(f"Cached: {usage.prompt_tokens_details.cached_tokens}") # Completion token breakdown if usage.completion_tokens_details: print(f"Reasoning: {usage.completion_tokens_details.reasoning_tokens}") ``` | Field | Sub-fields | Type | |-------|-----------|------| | `prompt_tokens_details` | `cached_tokens` | `Optional[int]` | | `completion_tokens_details` | `reasoning_tokens` | `Optional[int]` | Availability depends on the provider. `completion_tokens_details.reasoning_tokens` is present for OpenAI o-series, DeepSeek, xAI, and Google Gemini. It's `None` for providers that don't report reasoning token counts (Anthropic, Moonshot, Fireworks). See [Check reasoning token availability](/guides/extensions-and-thinking#check-reasoning-token-availability) for the full breakdown. --- ## Page: Python SDK > Section: Handle errors Catch typed exceptions: ```python from auriko import Client from auriko.errors import ( AurikoAPIError, APIConnectionError, AuthenticationError, PermissionDeniedError, BadRequestError, ConflictError, NotFoundError, RateLimitError, InternalServerError, APIStatusError, ) client = Client() try: response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}] ) except AuthenticationError as e: print(f"Check your API key (request_id={e.request_id})") except RateLimitError as e: print(f"Rate limited, retry after {e.retry_after_seconds}s (code={e.code})") except NotFoundError as e: print(f"Not found: {e.message}") except BadRequestError as e: print(f"Bad request: {e.message} (param={e.param})") except PermissionDeniedError as e: print(f"Not allowed: {e.message}") except ConflictError as e: print(f"Conflict: {e.message} (code={e.code})") except InternalServerError as e: print(f"Server error (request_id={e.request_id})") except APIStatusError as e: print(f"Upstream error ({e.status_code}): {e.message}") except APIConnectionError as e: print(f"Network error: {e.message}") except AurikoAPIError as e: print(f"API error ({e.status_code}): {e.message}") ``` See [Error Handling Guide](/guides/error-handling) for retry patterns and `map_openai_error()`. --- ## Page: Python SDK > Section: Use identity and model discovery APIs Query identity and model information: ```python # Identity (discover your workspace) identity = client.me.get() print(f"Workspace: {identity.workspace.id}") # Models models = client.models.list() model = client.models.retrieve("claude-sonnet-4-6") registry = client.models.list_registry() directory = client.models.list_directory() providers = client.models.list_providers() ``` ### Model listing choices | Method | Returns | Use when | |--------|---------|----------| | `list()` | All models with provider availability, pricing, data policy | You need the full model catalog | | `retrieve(model_id)` | Single model: provider availability, pricing, data policy | You have a model ID and need its details | | `list_registry()` | Flat list: `id`, `family`, `display_name` | You need a quick model ID lookup | | `list_directory()` | Rich detail: provider entries, context windows, capabilities, pricing tiers | You need to compare providers or check capabilities | | `list_providers()` | Provider catalog: display name, description, data policy | You need to see available providers | See the [Python SDK Reference](/sdk/python-reference) for the complete API. --- ## Page: Python SDK > Section: Use async client Use the async client for non-blocking requests: ```python from auriko import AsyncClient async def main(): client = AsyncClient() response = await client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) import asyncio asyncio.run(main()) ``` ### Async streaming Stream responses asynchronously: ```python from auriko import AsyncClient async def stream_response(): client = AsyncClient() stream = await client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Count to 10"}], stream=True ) async for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` ### Async context manager Use `async with` for automatic connection cleanup: ```python from auriko import AsyncClient async def main(): async with AsyncClient() as client: response = await client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) # client.close() called automatically ``` Or close explicitly: `await client.close()` --- ## Page: Python SDK > Section: Use with OpenAI-compatible frameworks `AurikoAsyncOpenAI` (experimental) is an `AsyncOpenAI` subclass that captures routing metadata automatically. Pass it to any framework that accepts an external `AsyncOpenAI` instance. The kwarg name varies across frameworks. Install with the optional `openai-compat` extra: ```bash pip install "auriko[openai-compat]" ``` ### Basic usage Call it directly like any `AsyncOpenAI` client, then read `last_routing_metadata` on the client after the response completes: ```python import asyncio from auriko import AurikoAsyncOpenAI async def main(): client = AurikoAsyncOpenAI() response = await client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], ) print(response.choices[0].message.content) print(client.last_routing_metadata.provider) asyncio.run(main()) ``` ### Capture metadata per request `last_routing_metadata` is a single-slot property. Under concurrent use it reflects the most recent response. For per-request capture, pass an `on_response` callback: ```python import asyncio from auriko import AurikoAsyncOpenAI captured = [] def handle(metadata): captured.append(metadata.provider) async def main(): client = AurikoAsyncOpenAI(on_response=handle) await asyncio.gather( client.chat.completions.create(model="gpt-5.4", messages=[{"role": "user", "content": "one"}]), client.chat.completions.create(model="gpt-5.4", messages=[{"role": "user", "content": "two"}]), ) print(captured) asyncio.run(main()) ``` The callback must be synchronous. An async callable raises `TypeError` at construction. ### Pass routing options Pass routing options via the `extra_body` kwarg. `RoutingOptions.to_extra_body()` returns a dict shaped for the Auriko API: ```python import asyncio from auriko import AurikoAsyncOpenAI from auriko.route_types import RoutingOptions async def main(): client = AurikoAsyncOpenAI() response = await client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], extra_body=RoutingOptions(optimize="cost").to_extra_body(), ) print(response.choices[0].message.content) asyncio.run(main()) ``` `RoutingOptions` lives in `auriko.route_types`. It is not exported at top-level. ### Framework wiring Each supported framework accepts an external `AsyncOpenAI` instance via its own kwarg: | Framework | Constructor call | |-----------|------------------| | OpenAI Agents SDK | `OpenAIChatCompletionsModel(model="gpt-5.4", openai_client=client)` | | LangChain `ChatOpenAI` | `ChatOpenAI(model="gpt-5.4", async_client=client.chat.completions, api_key="placeholder")` | | LlamaIndex `OpenAI` | `OpenAI(model="gpt-5.4", async_openai_client=client, api_key="placeholder")` | LangChain takes the `chat.completions` resource rather than the full client. LangChain and LlamaIndex both still require an `api_key` argument for their own parent-class construction; pass any placeholder value. For the Agents SDK path, see [OpenAI Agents SDK](/frameworks/openai-agents-sdk). For the full class reference, see [`AurikoAsyncOpenAI`](/sdk/python-reference#aurikoasyncopenai-experimental). ### `AurikoAsyncOpenAI` (experimental) or `AsyncClient`? Use `AurikoAsyncOpenAI` when a framework needs an `AsyncOpenAI` instance. Use `auriko.AsyncClient` for direct Python code. `AsyncClient` exposes `routing_metadata` directly on each response, so you do not need to read a separate client-level property. `AurikoAsyncOpenAI` is Python-only. TypeScript consumers can use [`@auriko/ai-sdk-provider`](/frameworks/vercel-ai-sdk) with the Vercel AI SDK, or the OpenAI TS SDK with `baseURL: 'https://api.auriko.ai/v1'`. --- ## Page: Python SDK > Section: Use context managers Use a context manager for automatic cleanup: ```python with Client() as client: response = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` --- ## Page: Python SDK > Section: SDK scope The Auriko SDK covers: inference (chat completions and the Response API, both with routing), identity, and model discovery. For full platform operations, use the [REST API](/api-reference/overview) directly. --- ## Page: Python SDK > Section: Use type hints The SDK provides typed responses, errors, and routing configuration. Use your IDE's autocomplete for the best experience: ```python from auriko import Client from auriko.models.chat import ChatCompletion, ChatCompletionChunk client = Client() response: ChatCompletion = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}] ) ``` --- ## Page: Python SDK Reference # Python SDK Reference See the [Python SDK Guide](/sdk/python) for usage examples and getting started. --- ## Page: Python SDK Reference > Section: Client Initialize a client with configuration options: ```python from auriko import Client, AsyncClient client = Client( api_key="ak_...", # or AURIKO_API_KEY env var base_url="https://api.auriko.ai/v1", # default timeout=60.0, # seconds, default 60 max_retries=2, # default 2 (0 disables) ) ``` ### Resources | Resource | Methods | |----------|---------| | `client.chat.completions` | `create(...)` | | `client.responses` | `create(...)` | | `client.models` | `list()`, `retrieve(model_id)`, `list_directory()`, `list_registry()`, `list_providers()` | | `client.me` | `get()` | All resources are available on both `Client` (sync) and `AsyncClient` (async). --- --- ## Page: Python SDK Reference > Section: Chat Completions ### `client.chat.completions.create(...)` Creates a chat completion. Supports single-model and multi-model routing. ```python # Non-streaming response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], max_tokens=100, ) # Streaming stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], stream=True, ) ``` ### Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `messages` | `list[dict]` | Yes | Conversation messages (non-empty) | | `model` | `str` | Yes | Model ID (or use `gateway.models` for multi-model routing) | | `stream` | `bool` | No | Enable streaming (default: `False`) | | `temperature` | `float` | No | Sampling temperature (0–2) | | `max_tokens` | `int` | No | Max tokens to generate | | `max_completion_tokens` | `int` | No | Max completion tokens (alias for `max_tokens`) | | `reasoning_effort` | `Literal['low', 'medium', 'high', 'xhigh', 'max', 'off']` | No | Reasoning effort for supported models — translated to provider-native control (see [guide](/guides/extensions-and-thinking)) | | `top_p` | `float` | No | Nucleus sampling (0–1) | | `frequency_penalty` | `float` | No | Frequency penalty (-2 to 2) | | `presence_penalty` | `float` | No | Presence penalty (-2 to 2) | | `top_k` | `int` | No | Top-K sampling | | `min_p` | `float` | No | Min-P sampling (0–1) | | `top_a` | `float` | No | Top-A sampling (0–1) | | `repetition_penalty` | `float` | No | Repetition penalty | | `stop` | `str \| list[str]` | No | Stop sequences | | `seed` | `int` | No | Deterministic sampling seed | | `n` | `int` | No | Number of completions to generate | | `tools` | `list[dict]` | No | Function calling tool definitions | | `tool_choice` | `str \| dict` | No | Tool selection: `"auto"`, `"none"`, `"required"`, or function spec | | `parallel_tool_calls` | `bool` | No | Allow parallel function calls | | `response_format` | `dict` | No | Output format (e.g., `{"type": "json_object"}`) | | `stream_options` | `dict` | No | Stream options (e.g., `{"include_usage": True}`) | | `logprobs` | `bool` | No | Return log probabilities | | `top_logprobs` | `int` | No | Number of top logprobs per token (0–20) | | `logit_bias` | `dict[str, float]` | No | Token bias adjustments | | `user` | `str` | No | End-user identifier | | `gateway` | `GatewayOptions \| dict` | No | Gateway namespace for routing, multi-model, and metadata options (see `gateway.routing`, `gateway.models`, `gateway.metadata`) | | `extensions` | `Extensions \| dict` | No | Provider-specific extensions (provider passthrough) | | `extra_body` | `dict` | No | Additional body fields (merged last, except `stream`) | #### `gateway.metadata` fields | Field | Type | Description | |-------|------|-------------| | `tags` | `list[str]` | Tags for categorizing requests (max 100 items, each ≤50 chars) | | `user_id` | `str` | Your application's user identifier for per-user analytics (max 255 chars) | | `trace_id` | `str` | Distributed tracing identifier (max 255 chars) | | `custom_fields` | `dict[str, str]` | Arbitrary key-value pairs (max 10 keys, keys ≤50 chars, values ≤200 chars) | ```python from auriko import Client from auriko.route_types import GatewayOptions client = Client() response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], gateway=GatewayOptions( metadata={ "user_id": "user_123", "trace_id": "req-abc", "custom_fields": {"env": "prod", "team": "backend"} } ), ) ``` Only the four fields above are accepted. Use `custom_fields` for arbitrary key-value pairs. ### Response (non-streaming) ```python class ChatCompletion: id: str created: int model: str object: str # "chat.completion" system_fingerprint: Optional[str] # not all models include this choices: list[Choice] usage: Optional[Usage] routing_metadata: Optional[RoutingMetadata] service_tier: Optional[str] # processing tier (OpenAI-routed models) response_headers: Optional[ResponseHeaders] class ChoiceMessage: role: str content: Optional[str] reasoning_content: Optional[str] # reasoning text (plain string) reasoning: Optional[list[ThinkingReasoningBlock | RedactedReasoningBlock]] # structured reasoning blocks with signatures refusal: Optional[str] # model refusal content (OpenAI passthrough) tool_calls: Optional[list[ToolCall]] annotations: Optional[list[Any]] # URL citations and model annotations (OpenAI-routed models) ``` ### Response (streaming) Returns a `Stream` that yields `ChatCompletionChunk` objects. ```python stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], stream=True, ) for chunk in stream: chunk.choices[0].delta.content # incremental content chunk.choices[0].delta.reasoning_content # incremental reasoning text (if enabled) chunk.choices[0].delta.reasoning_signature # signature for current thinking block chunk.choices[0].delta.reasoning_redacted_data # encrypted redacted thinking data stream.usage # available after iteration stream.routing_metadata # available after iteration stream.response_headers # available immediately stream.close() # manual cleanup (or use context manager) ``` --- --- ## Page: Python SDK Reference > Section: Responses ### `client.responses.create(...)` Creates a response using the OpenAI Response API format. Supports single-model and multi-model routing. ```python # Non-streaming response = client.responses.create( model="gpt-4o", input="Hello!", ) # Streaming stream = client.responses.create( model="gpt-4o", input="Hello!", stream=True, ) ``` ### Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `input` | `str \| list[dict]` | Yes | Text string or structured input items | | `model` | `str \| None` | Yes* | Model ID (*or use `gateway.models` for multi-model routing) | | `stream` | `bool` | No | Enable streaming (default: `False`) | | `instructions` | `str` | No | System instructions for the model | | `tools` | `list[dict]` | No | Tool definitions | | `tool_choice` | `str \| dict` | No | Tool selection: `"auto"`, `"none"`, `"required"`, or function spec | | `parallel_tool_calls` | `bool` | No | Allow parallel function calls | | `max_output_tokens` | `int` | No | Max tokens to generate | | `temperature` | `float` | No | Sampling temperature (0–2) | | `top_p` | `float` | No | Nucleus sampling (0–1) | | `top_k` | `int` | No | Top-K sampling | | `top_logprobs` | `int` | No | Number of top logprobs per token (0–20) | | `frequency_penalty` | `float` | No | Frequency penalty (-2 to 2) | | `presence_penalty` | `float` | No | Presence penalty (-2 to 2) | | `max_tool_calls` | `int` | No | Max built-in tool calls per response | | `reasoning` | `dict` | No | Reasoning config: `effort`, `summary`, `generate_summary` | | `text` | `dict` | No | Text format config (e.g., `{"format": {"type": "json_schema", ...}}`) | | `user` | `str` | No | End-user identifier | | `metadata` | `dict[str, str]` | No | Arbitrary key-value metadata | | `include` | `list[str]` | No | Additional data to include in the response | | `truncation` | `str` | No | Truncation strategy for long inputs | | `prompt_cache_key` | `str` | No | Key for prompt caching | | `safety_identifier` | `str` | No | Safety policy identifier | | `gateway` | `GatewayOptions \| dict` | No | Gateway namespace for routing, multi-model, and metadata options | | `extensions` | `Extensions \| dict` | No | Provider-specific extensions | | `extra_body` | `dict` | No | Additional body fields (merged last) | ### Response (non-streaming) ```python class Response: id: str object: str # "response" created_at: int model: str status: str # "completed", "failed", "incomplete", "in_progress" output: list[ResponseOutputItem] output_text: str # concatenated text output parallel_tool_calls: bool tool_choice: Any tools: list[Any] usage: Optional[ResponseUsage] error: Optional[dict] incomplete_details: Optional[dict] metadata: Optional[dict[str, str]] routing_metadata: Optional[RoutingMetadata] temperature: Optional[float] top_p: Optional[float] max_output_tokens: Optional[int] frequency_penalty: Optional[float] presence_penalty: Optional[float] top_logprobs: Optional[int] instructions: Optional[str] truncation: Optional[str] reasoning: Optional[dict] text: Optional[dict] user: Optional[str] prompt_cache_key: Optional[str] safety_identifier: Optional[str] max_tool_calls: Optional[int] store: Optional[bool] previous_response_id: Optional[str] background: Optional[bool] completed_at: Optional[int] service_tier: Optional[str] response_headers: Optional[ResponseHeaders] # property, set by SDK after parsing ``` ### Response (streaming) Returns a `ResponseStream` that yields Response API events. ```python stream = client.responses.create( model="gpt-4o", input="Hello!", stream=True, ) for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="") # After iteration, the terminal event's response is available: final = stream.completed_response # Response object from the terminal event final.usage # token usage final.routing_metadata # routing details stream.response_headers # available immediately (before iteration) stream.close() # manual cleanup (or use context manager) ``` > `routing_metadata` on `completed_response` is available for both streaming and non-streaming responses. For streaming, it's populated after iteration completes. --- --- ## Page: Python SDK Reference > Section: Models Query the model catalog: ```python models = client.models.list() # GET /v1/models model = client.models.retrieve("gpt-4o") # GET /v1/models/{model_id} directory = client.models.list_directory() # GET /v1/directory/models registry = client.models.list_registry() # GET /v1/registry/models providers = client.models.list_providers() # GET /v1/registry/providers ``` --- --- ## Page: Python SDK Reference > Section: Identity Get current API key identity: ```python identity = client.me.get() # GET /v1/me # Returns: ApiKeyIdentity { credential_type, status, profile, key_id, key_prefix, # key_name, workspace, scopes, rate_limits, expires_at, created_at } ``` --- --- ## Page: Python SDK Reference > Section: Error Classes All errors extend `AurikoAPIError`. Dispatch is driven by the `type` field of the canonical error envelope (see [Errors](/api-reference/errors) for the full envelope and retry policy). | Error Class | HTTP | `type` | |-------------|------|--------| | `BadRequestError` | 400 / 413 / 422 | `invalid_request_error` | | `AuthenticationError` | 401 | `authentication_error` | | `PermissionDeniedError` | 403 | `permission_error` | | `NotFoundError` | 404 | `not_found_error` | | `ConflictError` | 409 | `invalid_request_error` | | `RateLimitError` | 429 | `rate_limit_error` | | `InternalServerError` | 500 | `api_error` | | `APIStatusError` | 502 / 503 / 504 | `api_error` | | `APIConnectionError` | — | (network failure before response) | ### `AurikoAPIError` Fields | Field | Type | Description | |-------|------|-------------| | `message` | `str` | Human-readable error description | | `status_code` | `int` | HTTP status code | | `code` | `str` | Machine-readable error code (see [Error Codes](/contract/error-codes)) | | `type` | `str` | Canonical error type (one of six values) | | `param` | `Optional[str]` | Parameter that caused the error, when attributable | | `request_id` | `str` | Value of `x-request-id` on the failing response | | `doc_url` | `Optional[str]` | Link to the error's docs page | | `retry_after_seconds` | `Optional[int]` | `Retry-After` header value (429 / 503 only) | | `provider` | `Optional[str]` | Upstream provider that produced this error, when attributable | ```python from auriko import RateLimitError, AuthenticationError try: client.chat.completions.create(...) except RateLimitError as e: print(f"retry after {e.retry_after_seconds}s (request_id={e.request_id})") except AuthenticationError as e: print(f"{e.message} (request_id={e.request_id})") ``` Unknown error responses fall through to the base `AurikoAPIError` class. Always keep a catch-all for forward compatibility. --- --- ## Page: Python SDK Reference > Section: Response Headers Available on `ChatCompletion.response_headers` and `Stream.response_headers`: ```python response.response_headers.request_id # X-Request-ID response.response_headers.rate_limit_remaining # X-RateLimit-Remaining-Requests response.response_headers.rate_limit_limit # X-RateLimit-Limit-Requests response.response_headers.rate_limit_reset # X-RateLimit-Reset-Requests response.response_headers.credits_balance_microdollars # X-Credits-Balance-Microdollars response.response_headers.get("x-custom-header") # any header by name ``` --- --- ## Page: Python SDK Reference > Section: `AurikoAsyncOpenAI` (experimental) `AurikoAsyncOpenAI` is an `AsyncOpenAI` subclass that captures routing metadata from every successful response. Use it with frameworks that accept an external `AsyncOpenAI` instance. This is a Tier 4 experimental integration. For most use cases, use the [native SDK](/sdk/python) or [`AsyncOpenAI(base_url=...)`](/openai-compatibility) directly. Install with the optional `openai-compat` extra: ```bash pip install "auriko[openai-compat]" ``` ### Constructor ```python class AurikoAsyncOpenAI(AsyncOpenAI): def __init__( self, *, api_key: str | None = None, base_url: str = "https://api.auriko.ai/v1", on_response: Callable[[RoutingMetadata], Any] | None = None, **kwargs: Any, ) -> None: ... ``` All named parameters are keyword-only. | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `api_key` | `str \| None` | `None` | Falls back to `AURIKO_API_KEY` env var. Does not fall back to `OPENAI_API_KEY`. Raises `AuthenticationError` if neither source supplies a key. | | `base_url` | `str` | `"https://api.auriko.ai/v1"` | Auriko API base URL. | | `on_response` | `Callable[[RoutingMetadata], Any] \| None` | `None` | Sync callback invoked on every successful response. Passing an async callable raises `TypeError`. | | `**kwargs` | `Any` | — | Forwarded to `AsyncOpenAI.__init__` (for example `max_retries`, `timeout`). Passing `http_client` raises `TypeError`. | ### `last_routing_metadata` property Returns `RoutingMetadata | None`. Populated after a successful response completes. Returns `None` before any request, after a request errors, or when the response carried no `routing_metadata` field. Concurrency caveat: the property uses last-write-wins semantics on a shared client. For per-request capture across concurrent callers, use the `on_response` callback. Streaming caveat: metadata is extracted during SDK byte iteration, not on stream creation. A streaming caller that does not iterate every chunk may read `None`. ### `on_response` callback Signature: `Callable[[RoutingMetadata], Any]`. - **Sync only.** Passing an async callable raises `TypeError` at construction. - **Fires once per successful response** with a populated `routing_metadata`. Does not fire on error status, absent `routing_metadata`, or malformed `routing_metadata`. - Use this callback for per-request capture in concurrent scenarios where the shared `last_routing_metadata` property is race-prone. Import `RoutingMetadata` for type annotations from `auriko.route_types`: ```python from auriko.route_types import RoutingMetadata ``` `RoutingMetadata` is not exported at top-level `auriko`. The import `from auriko import RoutingMetadata` raises `ImportError`. ### Error behavior `AurikoAsyncOpenAI` raises dual-inheritance errors for HTTP failures (4xx, 5xx). Each error is catchable as both an Auriko error and an OpenAI error: ```python import asyncio import auriko from auriko import AurikoAsyncOpenAI async def main(): client = AurikoAsyncOpenAI() try: response = await client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], ) except auriko.RateLimitError as e: # also catchable as openai.RateLimitError print(f"Rate limited: {e.message}") asyncio.run(main()) ``` The same error is also catchable as `openai.RateLimitError`. No `map_openai_error()` wrapping is needed. Network-layer exceptions (`openai.APITimeoutError`, `openai.APIConnectionError`) propagate unchanged. Mid-stream SSE errors (raised after the HTTP 200 during `stream=True`) remain unmapped `openai.APIError`. The bridge covers HTTP-level status errors only. For `map_openai_error()` usage with plain `AsyncOpenAI`, see [Error mapping](/openai-compatibility#map-errors). --- --- ## Page: Python SDK Reference > Section: Types ### Client & Stream ```python from auriko import Client, AsyncClient, ResponseStream, AsyncResponseStream ``` ### Chat Response Types ```python from auriko.models.chat import ( ChatCompletion, ChatCompletionChunk, Choice, ChoiceMessage, StreamChoice, Delta, ToolCall, ToolCallFunction, ToolCallDelta, ToolCallDeltaFunction, ThinkingReasoningBlock, RedactedReasoningBlock, ) ``` ### Response Types ```python from auriko.models.responses import Response, ResponseStreamEvent, ResponseUsage, UnknownStreamEvent ``` ### Common Types ```python from auriko.models.common import Usage, PromptTokensDetails, CompletionTokensDetails, ApiKeyIdentity ``` ### Routing Types ```python from auriko.route_types import ( RoutingOptions, RoutingMetadata, CostInfo, StructuredWarning, StructuredWarningType, Optimize, Mode, DataPolicy, ) ``` ### Extensions ```python from auriko.models.extensions import Extensions ``` | Field | Type | Description | |-------|------|-------------| | `anthropic` | `dict` | Anthropic-specific parameters | | `openai` | `dict` | OpenAI-specific parameters | | `google` | `dict` | Google-specific parameters | | `deepseek` | `dict` | DeepSeek-specific parameters | | `[key]` | `dict` | Arbitrary provider passthrough | ### Model Catalog Types ```python from auriko.models.providers import ( ModelsListResponse, CanonicalModel, DirectoryResponse, DirectoryModel, ProviderEntry, TierEntry, ProviderList, ProviderInfo, ) ``` ### Error Classes ```python from auriko.errors import ( AurikoAPIError, APIConnectionError, APIStatusError, AuthenticationError, BadRequestError, ConflictError, InternalServerError, NotFoundError, PermissionDeniedError, RateLimitError, ) ``` ### Utilities ```python from auriko import ResponseHeaders, map_openai_error from auriko.route_types import parse_routing_metadata from auriko.errors import map_error_from_code ``` `parse_routing_metadata(response)` extracts `RoutingMetadata` from an OpenAI SDK response. Returns `None` if absent or unparseable. Returns `None` on Auriko SDK responses — use `response.routing_metadata` directly instead. `parse_routing_metadata` is Python-only. TypeScript SDK responses include `routing_metadata` as a typed property. `map_error_from_code(code, message, *, param=None, doc_url=None, provider=None, suggestion=None, response_headers=None)` constructs a typed `AurikoAPIError` subclass from an error code string (e.g., `"rate_limit_error"` → `RateLimitError`). --- ## Page: TypeScript SDK The `@auriko/sdk` package provides a typed TypeScript client for the Auriko API. Complete API reference with all types, parameters, and examples --- ## Page: TypeScript SDK > Section: Installation ```bash npm install @auriko/sdk # or yarn add @auriko/sdk # or pnpm add @auriko/sdk ``` --- ## Page: TypeScript SDK > Section: Get started ```typescript import { Client } from "@auriko/sdk"; const client = new Client(); // reads AURIKO_API_KEY from environment const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], }); console.log(response.choices[0].message.content); ``` --- ## Page: TypeScript SDK > Section: Configure ### API Key ```typescript // Option 1: Auto-detect from AURIKO_API_KEY env var (recommended) const client = new Client(); // Option 2: Pass explicitly const client = new Client({ apiKey: process.env.AURIKO_API_KEY, }); ``` ### Base URL ```typescript // Default: https://api.auriko.ai/v1 // Override for self-hosted or proxy setups: const client = new Client({ baseUrl: "https://your-proxy.example.com/v1", }); ``` ### Timeout ```typescript const client = new Client({ timeout: 60000, // milliseconds }); ``` ### Retries ```typescript const client = new Client({ maxRetries: 3, // default is 2 }); ``` --- ## Page: TypeScript SDK > Section: Create chat completions ### Basic request Send a chat completion request: ```typescript const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: "What is 2+2?" }, ], }); console.log(response.choices[0].message.content); ``` ### With routing options ```typescript import { Optimize } from "@auriko/sdk"; const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { optimize: "cost", max_ttft_ms: 1000, }, }, }); // Access routing metadata console.log(`Provider: ${response.routing_metadata?.provider}`); if (response.routing_metadata?.cost) { console.log(`Cost: $${response.routing_metadata.cost.usd}`); } ``` You can also use the `RoutingOptions` type with enum constants for IDE autocomplete: ```typescript import { Optimize } from "@auriko/sdk"; import type { RoutingOptions } from "@auriko/sdk"; const routing: RoutingOptions = { optimize: Optimize.COST, max_ttft_ms: 1000, }; ``` **All routing fields:** | Field | Type | Description | |-------|------|-------------| | `optimize` | `Optimize` | Strategy: `"cost"`, `"cost-focus"`, `"ttft"`, `"ttft-focus"`, `"tps"`, `"tps-focus"`, `"balanced"` | | `weights` | `RoutingWeights` | Custom scoring weights: `cost`, `ttft`, `throughput`. Overrides preset. | | `ttft_percentile` | `MetricPercentile` | TTFT scoring percentile: `"p50"` (default) or `"p95"` | | `throughput_percentile` | `MetricPercentile` | Throughput scoring percentile: `"p50"` (default) or `"p95"` | | `max_cost_per_1m` | `number` | Max $ per 1M tokens (average of input + output) | | `max_ttft_ms` | `number` | Max TTFT in milliseconds | | `min_throughput_tps` | `number` | Min throughput in tokens/sec | | `providers` | `string[]` | Allowlist of providers | | `exclude_providers` | `string[]` | Blocklist of providers | | `prefer` | `string` | Preferred provider (soft preference) | | `mode` | `Mode` | `"pool"` (default) or `"fallback"` | | `allow_fallbacks` | `boolean` | Enable fallback on failure | | `max_fallback_attempts` | `number` | Max fallback retries | | `data_policy` | `DataPolicy` | `"none"`, `"no_training"`, `"zdr"` | | `only_byok` | `boolean` | Only use BYOK providers | | `only_platform` | `boolean` | Only use platform providers | See [Advanced Routing](/guides/advanced-routing) for detailed strategy guides. ### Multi-model routing Route a request across multiple models. The router picks the best option based on your routing strategy: ```typescript const response = await client.chat.completions.create({ gateway: { models: ["gpt-4o", "claude-sonnet-4-20250514", "gemini-2.5-flash"], routing: { optimize: "cost" }, }, messages: [{ role: "user", content: "Explain quantum computing briefly." }], }); console.log(`Model used: ${response.model}`); console.log(`Provider: ${response.routing_metadata?.provider}`); console.log(response.choices[0].message.content); ``` `model` and `gateway.models` are mutually exclusive. Specify exactly one. Passing both raises `BadRequestError`. ### Reasoning effort Enable extended reasoning for complex tasks using the `reasoning_effort` parameter: ```typescript const response = await client.chat.completions.create({ model: "claude-sonnet-4-6", messages: [{ role: "user", content: "Solve step by step: what is 23! / 20!?" }], reasoning_effort: "high", }); // Access the reasoning output (if the model returns it) if (response.choices[0].message.reasoning_content) { console.log(`Reasoning: ${response.choices[0].message.reasoning_content}`); } console.log(`Answer: ${response.choices[0].message.content}`); ``` You can also pass provider-specific parameters through `extensions`: ```typescript const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], extensions: { openai: { logit_bias: { "1234": -100 } } }, }); ``` See [Extensions and Thinking](/guides/extensions-and-thinking) for provider details and streaming thinking output. ### Request metadata Attach metadata to requests for tracking and analytics: ```typescript const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], gateway: { metadata: { user_id: "user-123", tags: ["premium"] } }, }); ``` Valid metadata fields: `user_id`, `tags` (list), `trace_id`, and `custom_fields` (object for arbitrary key-value pairs). See the [TypeScript SDK Reference](/sdk/typescript-reference#parameters) for field constraints. ### Stream responses ```typescript const stream = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Count to 10" }], stream: true, }); for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { process.stdout.write(chunk.choices[0].delta.content); } } ``` After consuming all chunks, access stream-level metadata: ```typescript console.log(`\nProvider: ${stream.routing_metadata?.provider}`); console.log(`Tokens: ${stream.usage?.total_tokens}`); console.log(`Request ID: ${stream.responseHeaders.requestId}`); console.log(`Closed: ${stream.isClosed}`); ``` Close a stream manually with `stream.close()`. Routing metadata, usage, and response headers are available only after consuming all chunks. See [Streaming Guide](/guides/streaming) for full patterns including tool call streaming. ### Tool calling ```typescript const tools = [ { type: "function" as const, function: { name: "get_weather", description: "Get weather for a city", parameters: { type: "object", properties: { city: { type: "string" }, }, required: ["city"], }, }, }, ]; const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "What's the weather in Paris?" }], tools, }); if (response.choices[0].message.tool_calls) { const toolCall = response.choices[0].message.tool_calls[0]; console.log(`Function: ${toolCall.function.name}`); console.log(`Arguments: ${toolCall.function.arguments}`); } ``` See [Tool Calling Guide](/guides/tool-calling) for multi-turn tool conversations. --- ## Page: TypeScript SDK > Section: Create responses Send a request using the OpenAI Response API format: ```typescript const response = await client.responses.create({ model: "gpt-5.4", input: "What is the capital of France?", }); console.log(response.output_text); ``` ### Stream Response API events ```typescript const stream = await client.responses.create({ model: "gpt-5.4", input: "Count to 10", stream: true, }); for await (const event of stream) { if (event.type === "response.output_text.delta") { process.stdout.write(event.delta); } } console.log(`\nTokens: ${stream.completedResponse?.usage?.total_tokens}`); ``` See the [TypeScript SDK Reference](/sdk/typescript-reference#responses) for all parameters and event types. --- ## Page: TypeScript SDK > Section: Read response headers Every response and error includes a `responseHeaders` object with typed accessors: ```typescript const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], }); response.responseHeaders.requestId; // string | undefined response.responseHeaders.rateLimitRemaining; // number | undefined response.responseHeaders.rateLimitLimit; // number | undefined response.responseHeaders.rateLimitReset; // string | undefined response.responseHeaders.creditsBalanceMicrodollars; // number | undefined response.responseHeaders.get("x-custom-header"); // generic lookup response.responseHeaders.getAll("x-multi-header"); // string[] for multi-value headers ``` | Property | Header | Type | |----------|--------|------| | `requestId` | `x-request-id` | `string \| undefined` | | `rateLimitRemaining` | `x-ratelimit-remaining-requests` | `number \| undefined` | | `rateLimitLimit` | `x-ratelimit-limit-requests` | `number \| undefined` | | `rateLimitReset` | `x-ratelimit-reset-requests` | `string \| undefined` | | `creditsBalanceMicrodollars` | `x-credits-balance-microdollars` | `number \| undefined` | Error objects also carry `responseHeaders`. Use `e.responseHeaders.requestId` when filing support tickets to correlate with server logs. See the [TypeScript SDK Reference](/sdk/typescript-reference#response-headers) for the complete `ResponseHeaders` API. --- ## Page: TypeScript SDK > Section: Read token usage The `Usage` object on every response carries optional detail breakdowns: ```typescript const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], }); const usage = response.usage; // Prompt token breakdown if (usage?.prompt_tokens_details) { console.log(`Cached: ${usage.prompt_tokens_details.cached_tokens}`); } // Completion token breakdown if (usage?.completion_tokens_details) { console.log(`Reasoning: ${usage.completion_tokens_details.reasoning_tokens}`); } ``` | Field | Sub-fields | Type | |-------|-----------|------| | `prompt_tokens_details` | `cached_tokens` | `number \| undefined` | | `completion_tokens_details` | `reasoning_tokens` | `number \| undefined` | Availability depends on the provider. `completion_tokens_details.reasoning_tokens` is present for OpenAI o-series, DeepSeek, xAI, and Google Gemini. It's `undefined` for providers that don't report reasoning token counts (Anthropic, Moonshot, Fireworks). See [Check reasoning token availability](/guides/extensions-and-thinking#check-reasoning-token-availability) for the full breakdown. --- ## Page: TypeScript SDK > Section: Handle errors Catch typed exceptions: ```typescript import { Client, AurikoAPIError, APIConnectionError, APIStatusError, AuthenticationError, BadRequestError, ConflictError, InternalServerError, NotFoundError, PermissionDeniedError, RateLimitError, } from "@auriko/sdk"; const client = new Client(); try { const response = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], }); } catch (e) { if (e instanceof AuthenticationError) { console.log(`Check your API key: ${e.message} (requestId=${e.requestId})`); } else if (e instanceof RateLimitError) { console.log(`Rate limited: retry after ${e.retryAfterSeconds}s`); } else if (e instanceof PermissionDeniedError) { console.log(`Permission denied (code=${e.code}): ${e.message}`); } else if (e instanceof NotFoundError) { console.log(`Not found: ${e.message}`); } else if (e instanceof BadRequestError) { console.log(`Invalid request (param=${e.param}): ${e.message}`); } else if (e instanceof APIStatusError) { console.log(`Upstream/api error (${e.statusCode}, code=${e.code}): ${e.message}`); } else if (e instanceof APIConnectionError) { console.log(`Network failure before response: ${e.message}`); } else if (e instanceof AurikoAPIError) { console.log(`API error (${e.statusCode}): ${e.message}`); } } ``` See [Error Handling Guide](/guides/error-handling) for retry patterns. --- ## Page: TypeScript SDK > Section: Use identity and model discovery APIs Query identity and model information: ```typescript // Identity (discover your workspace) const identity = await client.me.get(); // Models const models = await client.models.list(); const model = await client.models.retrieve("claude-sonnet-4-6"); const registry = await client.models.listRegistry(); const directory = await client.models.listDirectory(); const providers = await client.models.listProviders(); ``` ### Model listing choices | Method | Returns | Use when | |--------|---------|----------| | `list()` | All models with provider availability, pricing, data policy | You need the full model catalog | | `retrieve(modelId)` | Single model: provider availability, pricing, data policy | You have a model ID and need its details | | `listRegistry()` | Flat list: `id`, `family`, `display_name` | You need a quick model ID lookup | | `listDirectory()` | Rich detail: provider entries, context windows, capabilities, pricing tiers | You need to compare providers or check capabilities | | `listProviders()` | Provider catalog: display name, description, data policy | You need to see available providers | See the [TypeScript SDK Reference](/sdk/typescript-reference) for the complete API. --- ## Page: TypeScript SDK > Section: SDK scope The Auriko SDK covers: inference (chat completions and the Response API, both with routing), identity, and model discovery. For full platform operations, use the [REST API](/api-reference/overview) directly. If you use the Vercel AI SDK, see [`@auriko/ai-sdk-provider`](/frameworks/vercel-ai-sdk) instead. --- ## Page: TypeScript SDK > Section: Use TypeScript types The SDK provides typed responses, errors, and routing configuration. Import types directly: ```typescript import type { ChatCompletion, ChatCompletionChunk, ChoiceMessage, Choice, Usage, RoutingMetadata, RoutingOptions, Extensions, ResponseObject, ResponseStreamEvent, ResponseCreateParams, } from "@auriko/sdk"; ``` --- ## Page: TypeScript SDK > Section: Node.js, Deno, and Browser The SDK works in multiple environments: ### Node.js ```typescript import { Client } from "@auriko/sdk"; const client = new Client(); // reads AURIKO_API_KEY from env ``` ### Deno ```typescript import { Client } from "npm:@auriko/sdk"; const client = new Client({ apiKey: Deno.env.get("AURIKO_API_KEY"), }); ``` ### Browser (with bundler) ```typescript import { Client } from "@auriko/sdk"; // Pass API key from your backend - never expose in client-side code! const client = new Client({ apiKey: apiKeyFromBackend, }); ``` Never expose your API key in client-side code. Use a backend proxy instead. --- ## Page: TypeScript SDK Reference # TypeScript SDK Reference See the [TypeScript SDK Guide](/sdk/typescript) for usage examples and getting started. --- ## Page: TypeScript SDK Reference > Section: Client Initialize a client with configuration options: ```typescript import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: "ak_...", // or AURIKO_API_KEY env var baseUrl: "https://api.auriko.ai/v1", // default timeout: 60_000, // ms, default 60s maxRetries: 2, // default 2 (0 disables) }); ``` ### Resources | Resource | Methods | |----------|---------| | `client.chat.completions` | `create(params)` | | `client.responses` | `create(params)` | | `client.models` | `list()`, `retrieve(modelId)`, `listDirectory()`, `listRegistry()`, `listProviders()` | | `client.me` | `get()` | --- --- ## Page: TypeScript SDK Reference > Section: Chat Completions ### `client.chat.completions.create(params)` Creates a chat completion. Supports single-model and multi-model routing. ```typescript // Non-streaming const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], max_tokens: 100, }); // Streaming const stream = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], stream: true, }); ``` ### Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `messages` | `Array` | Yes | Conversation messages (non-empty) | | `model` | `string` | One of model/gateway.models | Model ID | | `stream` | `boolean` | No | Enable streaming (default: `false`) | | `temperature` | `number` | No | Sampling temperature (0–2) | | `max_tokens` | `number` | No | Max tokens to generate | | `max_completion_tokens` | `number` | No | Max completion tokens (alias for `max_tokens`) | | `reasoning_effort` | `'low' \| 'medium' \| 'high' \| 'xhigh' \| 'max' \| 'off'` | No | Reasoning effort for supported models — translated to provider-native control (see [guide](/guides/extensions-and-thinking)) | | `top_p` | `number` | No | Nucleus sampling (0–1) | | `frequency_penalty` | `number` | No | Frequency penalty (-2 to 2) | | `presence_penalty` | `number` | No | Presence penalty (-2 to 2) | | `top_k` | `number` | No | Top-K sampling | | `min_p` | `number` | No | Min-P sampling (0–1) | | `top_a` | `number` | No | Top-A sampling (0–1) | | `repetition_penalty` | `number` | No | Repetition penalty | | `stop` | `string \| string[]` | No | Stop sequences | | `seed` | `number` | No | Deterministic sampling seed | | `n` | `number` | No | Number of completions to generate | | `tools` | `Tool[]` | No | Function calling tool definitions | | `tool_choice` | `string \| object` | No | Tool selection: `"auto"`, `"none"`, `"required"`, or function spec | | `parallel_tool_calls` | `boolean` | No | Allow parallel function calls | | `response_format` | `object` | No | Output format (e.g., `{ type: "json_object" }`) | | `stream_options` | `object` | No | Stream options (e.g., `{ include_usage: true }`) | | `logprobs` | `boolean` | No | Return log probabilities | | `top_logprobs` | `number` | No | Number of top logprobs per token (0–20) | | `logit_bias` | `Record` | No | Token bias adjustments | | `user` | `string` | No | End-user identifier | | `gateway` | `GatewayOptions \| Record` | No | Gateway directives: `routing`, `metadata`, `models` | | `extensions` | `Extensions \| Record` | No | Provider-specific extensions (provider passthrough) | | `extra_body` | `Record` | No | Additional body fields (merged last except `stream`; gateway-aware one-level-deep merge on `gateway`) | #### `gateway.metadata` fields | Field | Type | Description | |-------|------|-------------| | `tags` | `string[]` | Tags for categorizing requests (max 100 items, each ≤50 chars) | | `user_id` | `string` | Your application's user identifier for per-user analytics (max 255 chars) | | `trace_id` | `string` | Distributed tracing identifier (max 255 chars) | | `custom_fields` | `Record` | Arbitrary key-value pairs (max 10 keys, keys ≤50 chars, values ≤200 chars) | ```typescript import { Client } from "@auriko/sdk"; const client = new Client(); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { metadata: { user_id: "user_123", trace_id: "req-abc", custom_fields: { env: "prod", team: "backend" }, }, }, }); ``` Only the four fields above are accepted. Use `custom_fields` for arbitrary key-value pairs. ### Response (non-streaming) ```typescript interface ChatCompletion { id: string; created: number; model: string; object: "chat.completion"; system_fingerprint?: string; // not all models include this choices: Choice[]; usage?: Usage; routing_metadata?: RoutingMetadata; service_tier?: string | null; // processing tier (OpenAI-routed models) responseHeaders: ResponseHeaders; // SDK-added } interface ChoiceMessage { role: string; content: string | null; reasoning_content?: string; // reasoning text (plain string) reasoning?: ReasoningBlock[]; // structured reasoning blocks with signatures refusal?: string | null; // model refusal content (OpenAI passthrough) tool_calls?: ToolCall[]; annotations?: unknown[]; // URL citations and model annotations (OpenAI-routed models) } ``` ### Response (streaming) Returns a `Stream` that yields `ChatCompletionChunk` objects. ```typescript const stream = await client.chat.completions.create({ stream: true, ... }); for await (const chunk of stream) { chunk.choices[0]?.delta?.content; // incremental content chunk.choices[0]?.delta?.reasoning_content; // incremental reasoning text (if enabled) chunk.choices[0]?.delta?.reasoning_signature; // signature for current thinking block chunk.choices[0]?.delta?.reasoning_redacted_data; // encrypted redacted thinking data } stream.usage; // available after iteration stream.routing_metadata; // available after iteration stream.responseHeaders; // available immediately stream.isClosed; // boolean stream.close(); // manual cleanup ``` --- --- ## Page: TypeScript SDK Reference > Section: Responses ### `client.responses.create(params)` Creates a response using the OpenAI Response API format. Supports single-model and multi-model routing. ```typescript // Non-streaming const response = await client.responses.create({ model: "gpt-4o", input: "Hello!", }); // Streaming const stream = await client.responses.create({ model: "gpt-4o", input: "Hello!", stream: true, }); ``` ### Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `input` | `string \| ResponseInputItemParam[]` | Yes | Text string or structured input items | | `model` | `string` | Yes* | Model ID (*or use `gateway.models` for multi-model routing) | | `stream` | `boolean` | No | Enable streaming (default: `false`) | | `instructions` | `string` | No | System instructions for the model | | `tools` | `ResponseToolParam[]` | No | Tool definitions | | `tool_choice` | `string \| Record` | No | Tool selection: `"auto"`, `"none"`, `"required"`, or function spec | | `parallel_tool_calls` | `boolean` | No | Allow parallel function calls | | `max_output_tokens` | `number` | No | Max tokens to generate | | `temperature` | `number` | No | Sampling temperature (0–2) | | `top_p` | `number` | No | Nucleus sampling (0–1) | | `top_k` | `number` | No | Top-K sampling | | `top_logprobs` | `number` | No | Number of top logprobs per token (0–20) | | `frequency_penalty` | `number` | No | Frequency penalty (-2 to 2) | | `presence_penalty` | `number` | No | Presence penalty (-2 to 2) | | `max_tool_calls` | `number` | No | Max built-in tool calls per response | | `reasoning` | `ResponseReasoningParam` | No | Reasoning config: `effort`, `summary`, `generate_summary` | | `text` | `Record` | No | Text format config (e.g., `{ format: { type: "json_schema", ... } }`) | | `user` | `string` | No | End-user identifier | | `metadata` | `Record` | No | Arbitrary key-value metadata | | `include` | `string[]` | No | Additional data to include in the response | | `truncation` | `string` | No | Truncation strategy for long inputs | | `prompt_cache_key` | `string` | No | Key for prompt caching | | `safety_identifier` | `string` | No | Safety policy identifier | | `gateway` | `GatewayOptions \| Record` | No | Gateway namespace for routing, multi-model, and metadata options | | `extensions` | `Extensions \| Record` | No | Provider-specific extensions | | `extra_body` | `Record` | No | Additional body fields (merged last) | ### Response (non-streaming) ```typescript interface ResponseObject { id: string; object: "response"; created_at: number; model: string; status: "completed" | "failed" | "incomplete" | "in_progress"; output: ResponseOutputItem[]; output_text: string; parallel_tool_calls: boolean; tool_choice: unknown; tools: unknown[]; usage?: ResponseUsage | null; error?: ResponseError | null; incomplete_details?: Record | null; metadata?: Record | null; routing_metadata?: RoutingMetadata | null; temperature?: number; top_p?: number; max_output_tokens?: number | null; frequency_penalty?: number; presence_penalty?: number; top_logprobs?: number; instructions?: string | null; truncation?: string; reasoning?: Record | null; text?: Record; user?: string | null; prompt_cache_key?: string | null; safety_identifier?: string | null; max_tool_calls?: number | null; store?: boolean; previous_response_id?: string | null; background?: boolean; completed_at?: number | null; service_tier?: string | null; responseHeaders: ResponseHeaders; } ``` ### Response (streaming) Returns a `ResponseStream` that yields Response API events. ```typescript const stream = await client.responses.create({ model: "gpt-4o", input: "Hello!", stream: true, }); for await (const event of stream) { if (event.type === "response.output_text.delta") { process.stdout.write(event.delta); } } // After iteration, the terminal event's response is available: const final = stream.completedResponse; // ResponseObject from the terminal event final?.usage; // token usage final?.routing_metadata; // routing details stream.responseHeaders; // available immediately (before iteration) stream.close(); // manual cleanup ``` `routing_metadata` on `completedResponse` is available for both streaming and non-streaming responses. For streaming, it's populated after iteration completes. --- --- ## Page: TypeScript SDK Reference > Section: Models Query the model catalog: ```typescript const models = await client.models.list(); // GET /v1/models const model = await client.models.retrieve("gpt-4o"); // GET /v1/models/{model_id} const directory = await client.models.listDirectory(); // GET /v1/directory/models const registry = await client.models.listRegistry(); // GET /v1/registry/models const providers = await client.models.listProviders(); // GET /v1/registry/providers ``` --- --- ## Page: TypeScript SDK Reference > Section: Identity Get current API key identity: ```typescript const identity = await client.me.get(); // GET /v1/me // Returns: { credential_type, status, profile, key_id, key_prefix, key_name, // workspace, scopes, rate_limits, expires_at, created_at } ``` --- --- ## Page: TypeScript SDK Reference > Section: Error Classes All errors extend `AurikoAPIError`. Dispatch is driven by the `type` field of the canonical error envelope (see [Errors](/api-reference/errors) for the full envelope and retry policy). | Error Class | HTTP | `type` | |-------------|------|--------| | `BadRequestError` | 400 / 413 / 422 | `invalid_request_error` | | `AuthenticationError` | 401 | `authentication_error` | | `PermissionDeniedError` | 403 | `permission_error` | | `NotFoundError` | 404 | `not_found_error` | | `ConflictError` | 409 | `invalid_request_error` | | `RateLimitError` | 429 | `rate_limit_error` | | `InternalServerError` | 500 | `api_error` | | `APIStatusError` | 502 / 503 / 504 | `api_error` | | `APIConnectionError` | — | (network failure before response) | ### `AurikoAPIError` Fields | Field | Type | Description | |-------|------|-------------| | `message` | `string` | Human-readable error description (inherited from `Error`) | | `statusCode` | `number` | HTTP status code | | `code` | `string` | Machine-readable error code (see [Error Codes](/contract/error-codes)) | | `type` | `string` | Canonical error type (one of six values) | | `param` | `string \| null` | Parameter that caused the error, when attributable | | `requestId` | `string` | Value of `x-request-id` on the failing response | | `docUrl` | `string \| undefined` | Link to the error's docs page | | `retryAfterSeconds` | `number \| undefined` | `Retry-After` header value (429 / 503 only) | | `provider` | `string \| undefined` | Upstream provider that produced this error, when attributable | ```typescript import { Client, RateLimitError, AuthenticationError } from "@auriko/sdk"; try { await client.chat.completions.create({ ... }); } catch (e) { if (e instanceof RateLimitError) { console.log(`retry after ${e.retryAfterSeconds}s (requestId=${e.requestId})`); } else if (e instanceof AuthenticationError) { console.log(`${e.message} (requestId=${e.requestId})`); } } ``` Unknown error responses fall through to the base `AurikoAPIError` class. Always keep a catch-all for forward compatibility. `mapErrorFromCode(code, message, responseHeaders, opts?)` constructs a typed `AurikoAPIError` subclass from an error code string (e.g., `"rate_limit_error"` → `RateLimitError`): ```typescript import { mapErrorFromCode, RateLimitError } from "@auriko/sdk"; const err = mapErrorFromCode("rate_limit_error", "Too many requests", responseHeaders); if (err instanceof RateLimitError) { console.log(err.retryAfterSeconds); } ``` --- --- ## Page: TypeScript SDK Reference > Section: Response Headers Available on `ChatCompletion.responseHeaders`, `Stream.responseHeaders`, and `ResponseObject.responseHeaders`: ```typescript response.responseHeaders.requestId; // X-Request-ID response.responseHeaders.rateLimitRemaining; // X-RateLimit-Remaining-Requests response.responseHeaders.rateLimitLimit; // X-RateLimit-Limit-Requests response.responseHeaders.rateLimitReset; // X-RateLimit-Reset-Requests response.responseHeaders.creditsBalanceMicrodollars; // X-Credits-Balance-Microdollars response.responseHeaders.get("x-custom-header"); // any header by name response.responseHeaders.getAll("x-multi-header"); // string[] for multi-value headers ``` --- --- ## Page: TypeScript SDK Reference > Section: Constants Runtime enum objects for routing configuration: ```typescript import { Optimize, Mode, DataPolicy } from "@auriko/sdk"; // Optimize strategy Optimize.COST // "cost" Optimize.COST_FOCUS // "cost-focus" Optimize.TTFT // "ttft" Optimize.TTFT_FOCUS // "ttft-focus" Optimize.TPS // "tps" Optimize.TPS_FOCUS // "tps-focus" Optimize.BALANCED // "balanced" // Routing mode Mode.POOL // "pool" Mode.FALLBACK // "fallback" // Data policy DataPolicy.NONE // "none" DataPolicy.NO_TRAINING // "no_training" DataPolicy.ZDR // "zdr" ``` --- --- ## Page: TypeScript SDK Reference > Section: Types All types use snake_case field names matching the wire format: ### Client & Stream ```typescript import { Client, Stream, ResponseStream } from "@auriko/sdk"; ``` ### Chat Response Types ```typescript import type { ChatCompletion, ChatCompletionChunk, Choice, ChoiceMessage, ReasoningBlock, StreamChoice, Delta, ToolCall, ToolCallFunction, ToolCallDelta, ToolCallDeltaFunction, } from "@auriko/sdk"; ``` ### Response Types Common types for the Response API. The SDK exports all event types in the `ResponseStreamEvent` union — import individual event types (e.g., `ResponseTextDeltaEvent`, `ResponseFunctionCallArgumentsDoneEvent`) as needed. ```typescript import type { ResponseObject, ResponseObjectBase, ResponseUsage, ResponseError, ResponseOutputItem, ResponseMessageOutputItem, ResponseFunctionCallOutputItem, ResponseReasoningOutputItem, ResponseOutputContentPart, ResponseReasoningSummary, ResponseStreamEvent, ResponseCreateParams, ResponseInputItemParam, ResponseToolParam, ResponseReasoningParam, } from "@auriko/sdk"; ``` ### Common Types ```typescript import type { Usage, PromptTokensDetails, CompletionTokensDetails, ApiKeyIdentity } from "@auriko/sdk"; ``` ### Routing Types ```typescript import type { GatewayOptions, RoutingOptions, RoutingMetadata, CostInfo, StructuredWarning, StructuredWarningType } from "@auriko/sdk"; ``` ### Extensions ```typescript import type { Extensions } from "@auriko/sdk"; ``` | Field | Type | Description | |-------|------|-------------| | `anthropic` | `Record` | Anthropic-specific parameters | | `openai` | `Record` | OpenAI-specific parameters | | `google` | `Record` | Google-specific parameters | | `deepseek` | `Record` | DeepSeek-specific parameters | | `[key]` | `Record` | Arbitrary provider passthrough | ### Model Catalog Types ```typescript import type { DirectoryResponse, ModelsListResponse, ProviderList } from "@auriko/sdk"; ``` ### Request Types ```typescript import type { ChatCompletionCreateParams, ClientOptions } from "@auriko/sdk"; ``` ### Runtime Constants ```typescript import { Optimize, Mode, DataPolicy, ResponseHeaders } from "@auriko/sdk"; ``` ### Error Classes ```typescript import { AurikoAPIError, APIConnectionError, APIStatusError, AuthenticationError, BadRequestError, ConflictError, InternalServerError, NotFoundError, PermissionDeniedError, RateLimitError, mapErrorFromCode, } from "@auriko/sdk"; ``` === # Auriko Framework Integrations ## Page: LangChain Use Auriko as your LLM provider in LangChain with a drop-in `ChatOpenAI` replacement. This integration is Python-only. For TypeScript, use the [Vercel AI SDK](/frameworks/vercel-ai-sdk) integration or configure the OpenAI SDK directly with Auriko's base URL. --- ## Page: LangChain > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) --- ## Page: LangChain > Section: Installation ```bash pip install "auriko[langchain]" ``` --- ## Page: LangChain > Section: Use SDK adapter Use the `AurikoChatOpenAI` adapter: ```python from auriko.frameworks.langchain import AurikoChatOpenAI llm = AurikoChatOpenAI(model="gpt-5.4") ``` `AurikoChatOpenAI` extends LangChain's `ChatOpenAI` with: - `use_responses_api=False` set by default (ensures routing metadata and typed error mapping) - Routing injection via `extra_body` - OpenAI error mapping to typed Auriko error classes ```python from auriko.frameworks.langchain import AurikoChatOpenAI llm = AurikoChatOpenAI(model="gpt-5.4") # Simple invoke response = llm.invoke("What is 2+2?") print(response.content) # Streaming for chunk in llm.stream("Count to 5"): print(chunk.content, end="", flush=True) # With messages from langchain_core.messages import HumanMessage, SystemMessage messages = [ SystemMessage(content="You are a helpful assistant."), HumanMessage(content="Explain quantum computing briefly."), ] response = llm.invoke(messages) print(response.content) ``` --- ## Page: LangChain > Section: Configure options `AurikoChatOpenAI` accepts these parameters: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `model` | `str` | (required, via parent) | Model ID | | `api_key` | `str \| None` | `AURIKO_API_KEY` env | API key | | `routing` | `RoutingOptions \| None` | `None` | Routing configuration | | `base_url` | `str` | `"https://api.auriko.ai/v1"` | API base URL | | `**kwargs` | | | Passed through to `ChatOpenAI` (e.g., `temperature`, `max_tokens`) | --- ## Page: LangChain > Section: Configure routing You can pass a `RoutingOptions` instance to control cost, latency, and quality trade-offs: ```python from auriko.frameworks.langchain import AurikoChatOpenAI from auriko.route_types import RoutingOptions llm = AurikoChatOpenAI( model="gpt-5.4", routing=RoutingOptions(optimize="cost", max_ttft_ms=1000), ) response = llm.invoke("Hello!") print(response.content) ``` Access routing metadata through `generation_info` when using `generate()`: ```python result = llm.generate([[HumanMessage(content="Hello!")]]) info = result.generations[0][0].generation_info if info and "routing_metadata" in info: print(f"Provider: {info['routing_metadata']['provider']}") ``` --- ## Page: LangChain > Section: Configure manually If you prefer to use `ChatOpenAI` directly: ```python import os from langchain_openai import ChatOpenAI llm = ChatOpenAI( model="gpt-5.4", api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", use_responses_api=False, ) ``` `use_responses_api=False` is the default. Both Chat Completions and Response API streaming include `routing_metadata`. --- ## Page: LangChain > Section: Alternative: use `AurikoAsyncOpenAI` (experimental) If you can't use `auriko[langchain]` (for example, your project pins a different `langchain-openai` version), pass `AurikoAsyncOpenAI` into LangChain's `async_client` parameter: ```python from langchain_openai import ChatOpenAI from auriko import AurikoAsyncOpenAI client = AurikoAsyncOpenAI() llm = ChatOpenAI( model="gpt-4o", async_client=client.chat.completions, api_key="placeholder", ) ``` Pass `client.chat.completions` (not the whole client) and provide any string as `api_key` (LangChain requires it for construction). Read `client.last_routing_metadata` after each call. See [`AurikoAsyncOpenAI`](/sdk/python-reference#aurikoasyncopenai-experimental) for the full class reference. --- ## Page: LangChain > Section: Notes - OpenAI API errors map to typed Auriko error classes (`RateLimitError`, `PermissionDeniedError`, `BadRequestError`, etc.). - `AurikoChatOpenAI` sets `use_responses_api=False` by default. --- ## Page: Vercel AI SDK Use Auriko as your LLM provider in the Vercel AI SDK with a first-party provider package. This integration is for TypeScript and JavaScript. For Python, use the [LangChain](/frameworks/langchain) or [LlamaIndex](/frameworks/llamaindex) integration. `@auriko/ai-sdk-provider` is at 0.2.0. Expect API changes before 1.0. --- ## Page: Vercel AI SDK > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) - Node.js 18+ --- ## Page: Vercel AI SDK > Section: Install ```bash npm install @auriko/ai-sdk-provider ai ``` --- ## Page: Vercel AI SDK > Section: Use the provider Create a provider instance and pass it to any AI SDK function: ```typescript import { createAuriko } from "@auriko/ai-sdk-provider"; import { generateText } from "ai"; const auriko = createAuriko(); const { text } = await generateText({ model: auriko("gpt-4o"), prompt: "What is the capital of France?", }); console.log(text); ``` `createAuriko()` reads your `AURIKO_API_KEY` environment variable by default. --- ## Page: Vercel AI SDK > Section: Stream responses Use `streamText` for streaming: ```typescript import { createAuriko } from "@auriko/ai-sdk-provider"; import { streamText } from "ai"; const auriko = createAuriko(); const result = streamText({ model: auriko("gpt-4o"), prompt: "Count to 10", }); for await (const chunk of result.textStream) { process.stdout.write(chunk); } ``` --- ## Page: Vercel AI SDK > Section: Configure options `createAuriko()` accepts these parameters: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `apiKey` | `string` | `AURIKO_API_KEY` env | API key | | `baseURL` | `string` | `"https://api.auriko.ai/v1"` | API base URL | | `headers` | `Record` | `undefined` | Custom headers | | `fetch` | `typeof fetch` | `globalThis.fetch` | Custom fetch implementation | | `routing` | `RoutingOptions` | `undefined` | Default [routing configuration](/guides/routing-options) | | `metadata` | `AurikoMetadataParam` | `undefined` | Request metadata (tags, user ID, trace ID) | --- ## Page: Vercel AI SDK > Section: Configure routing Set routing defaults when you create the provider: ```typescript import { createAuriko, Optimize } from "@auriko/ai-sdk-provider"; import type { AurikoResponseMetadata } from "@auriko/ai-sdk-provider"; import { generateText } from "ai"; const auriko = createAuriko({ routing: { optimize: Optimize.COST, max_ttft_ms: 1000 }, }); const result = await generateText({ model: auriko("gpt-4o"), prompt: "Hello!", }); const meta = result.providerMetadata?.auriko as AurikoResponseMetadata | undefined; if (meta) { console.log(`Provider: ${meta.provider}`); console.log(`Cost: $${meta.cost?.usd}`); } ``` For routing parameters, see the [routing options guide](/guides/routing-options) and [advanced routing guide](/guides/advanced-routing). --- ## Page: Vercel AI SDK > Section: Access routing metadata For non-streaming calls, read `result.providerMetadata?.auriko`: ```typescript const meta = result.providerMetadata?.auriko as AurikoResponseMetadata | undefined; console.log(meta?.provider); ``` For streaming calls, `await` the metadata: ```typescript const result = streamText({ model: auriko("gpt-4o"), prompt: "Hello!" }); const metadata = await result.providerMetadata; const meta = metadata?.auriko as AurikoResponseMetadata | undefined; console.log(meta?.provider); ``` Import `AurikoResponseMetadata` from `@auriko/ai-sdk-provider` for type-safe access. --- ## Page: Vercel AI SDK > Section: Configure manually You can point the OpenAI-compatible provider at Auriko's API: ```bash npm install @ai-sdk/openai ai ``` ```typescript import { createOpenAI } from "@ai-sdk/openai"; import { generateText } from "ai"; const openai = createOpenAI({ baseURL: "https://api.auriko.ai/v1", apiKey: process.env.AURIKO_API_KEY, }); const { text } = await generateText({ model: openai.chat("gpt-4o"), // .chat() recommended: routing metadata available via Chat Completions prompt: "Hello!", }); ``` This approach doesn't include built-in routing or response metadata. You can set a routing strategy with a [suffix shortcut](/guides/advanced-routing#use-suffix-shortcuts) (e.g., `openai.chat("gpt-4o:cost-focus")`). For routing configuration and typed metadata, use `@auriko/ai-sdk-provider`. --- ## Page: OpenAI Agents SDK Use Auriko as your LLM provider in the OpenAI Agents SDK. This integration is Python-only. For TypeScript, use the [Vercel AI SDK](/frameworks/vercel-ai-sdk) integration. Requires `openai-agents` >=0.13. --- ## Page: OpenAI Agents SDK > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) --- ## Page: OpenAI Agents SDK > Section: Install ```bash pip install "auriko[openai-compat]" openai-agents ``` --- ## Page: OpenAI Agents SDK > Section: Use `AurikoAsyncOpenAI` (experimental) `AurikoAsyncOpenAI` (experimental) is an `AsyncOpenAI` subclass that captures routing metadata from every successful response. Pass it to `OpenAIChatCompletionsModel` via the `openai_client=` parameter: ```python import asyncio from agents import Agent, Runner, set_tracing_disabled from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel from auriko import AurikoAsyncOpenAI set_tracing_disabled(True) client = AurikoAsyncOpenAI() model = OpenAIChatCompletionsModel(model="gpt-4o-mini", openai_client=client) agent = Agent(name="assistant", instructions="Be brief.", model=model) async def main(): result = await Runner.run(agent, input="What is the capital of France?") print(result.final_output) print(client.last_routing_metadata.provider) asyncio.run(main()) ``` `Runner.run_sync()` works the same way. ### Configure routing Pass routing options through `ModelSettings.extra_body`, not through the `client.chat.completions.create()` call: ```python from agents import Agent from agents.model_settings import ModelSettings from auriko.route_types import RoutingOptions model_settings = ModelSettings( extra_body=RoutingOptions(optimize="cost").to_extra_body(), ) agent = Agent(name="assistant", instructions="Be brief.", model=model, model_settings=model_settings) ``` The Agents SDK forwards `ModelSettings.extra_body` to the API call. `RoutingOptions.to_extra_body()` returns a dict the Auriko API accepts. ### Access routing metadata Read `client.last_routing_metadata` after a run completes: ```python result = Runner.run_sync(agent, "Hello!") print(client.last_routing_metadata.provider) print(client.last_routing_metadata.routing_strategy) ``` The property uses last-write-wins semantics on a shared client. For per-request capture across concurrent runs, pass an `on_response` callback: ```python captured = [] client = AurikoAsyncOpenAI(on_response=lambda m: captured.append(m)) ``` The callback must be synchronous. Passing an async callable raises `TypeError`. ### Handle errors `AurikoAsyncOpenAI` raises errors catchable as both Auriko and OpenAI error types: ```python import asyncio import auriko from agents import Agent, Runner, set_tracing_disabled from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel from auriko import AurikoAsyncOpenAI set_tracing_disabled(True) async def main(): client = AurikoAsyncOpenAI() model = OpenAIChatCompletionsModel(model="gpt-4o-mini", openai_client=client) agent = Agent(name="assistant", instructions="Be brief.", model=model) try: result = await Runner.run(agent, input="Hello!") print(result.final_output) except auriko.RateLimitError as e: # also catchable as openai.RateLimitError print(f"Rate limited: {e.message}") asyncio.run(main()) ``` The same error is also catchable as `openai.RateLimitError`. Network-layer exceptions (`openai.APITimeoutError`, `openai.APIConnectionError`) propagate unchanged. Mid-stream SSE errors (raised after the HTTP 200 during `stream=True`) remain unmapped `openai.APIError`. `AurikoAsyncOpenAI` maps HTTP-level status errors only. For the full class reference, see [`AurikoAsyncOpenAI`](/sdk/python-reference#aurikoasyncopenai-experimental). --- ## Page: OpenAI Agents SDK > Section: Configure manually If you prefer to configure the SDK's client directly, without the Auriko integration: ```python import asyncio import os from openai import AsyncOpenAI from agents import Agent, Runner, set_default_openai_client, set_default_openai_api, set_tracing_disabled set_default_openai_api("chat_completions") set_tracing_disabled(True) client = AsyncOpenAI( base_url="https://api.auriko.ai/v1", api_key=os.environ["AURIKO_API_KEY"], ) set_default_openai_client(client, use_for_tracing=False) agent = Agent(name="assistant", instructions="Be helpful.", model="gpt-5.4") async def main(): result = await Runner.run(agent, input="Hello!") print(result.final_output) asyncio.run(main()) ``` `set_default_openai_api("chat_completions")` is the default for the Agents SDK. Both Chat Completions and Response API streaming include `routing_metadata`. --- ## Page: Claude Agent SDK Auriko routes Claude Code and Claude Agent SDK requests through multiple providers, giving you model choice, cost controls, and fallbacks. Claude Code features work through Auriko: tool use, MCP servers, streaming, extended thinking, and prompt caching. Only LLM inference routes through Auriko; agentic operations (file I/O, bash, MCP) run locally. --- ## Page: Claude Agent SDK > Section: Prerequisites - [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) --- ## Page: Claude Agent SDK > Section: Set up Claude Code Add 3 environment variables to your shell profile (`~/.zshrc` or `~/.bashrc`): ```bash export ANTHROPIC_BASE_URL="https://api.auriko.ai" export ANTHROPIC_AUTH_TOKEN="ak_live_..." # from auriko.ai/dashboard export ANTHROPIC_API_KEY="" ``` `ANTHROPIC_API_KEY=""` must be explicitly set to empty. If it contains a value, Claude Code uses it directly against Anthropic, bypassing Auriko. Reload your shell after saving: ```bash source ~/.zshrc # or: source ~/.bashrc ``` --- ## Page: Claude Agent SDK > Section: Verify setup ```bash claude -p "Say exactly: setup-ok" --model claude-haiku-4-5-20251001 ``` Claude Code includes a system prompt on every request. The first request in a session costs more than follow-ups due to [prompt caching](/guides/prompt-caching). --- ## Page: Claude Agent SDK > Section: Use different models You can pass an Auriko model ID with the `--model` flag: ```bash claude --model deepseek-v4-flash claude --model gemini-2.5-flash claude --model grok-4.3 ``` Model IDs must be exact. Claude Code requires reasoning support from every model. Models that don't support reasoning return a `400` error. Browse per-model capabilities in the [directory API](https://api.auriko.ai/v1/directory/models). Available models include: | Model | Author | Context | |-------|--------|---------| | `claude-sonnet-4-6` | Anthropic | 1M | | `claude-opus-4-6` | Anthropic | 1M | | `claude-opus-4-7` | Anthropic | 1M | | `deepseek-v4-flash` | DeepSeek | 1M | | `deepseek-v4-pro` | DeepSeek | 1M | | `gemini-2.5-flash` | Google | 1M | | `gemini-2.5-pro` | Google | 1M | | `gemini-3.1-pro-preview` | Google | 1M | | `glm-5.1` | Z.AI | 200K | | `grok-4.3` | xAI | 1M | | `kimi-k2.5` | Moonshot | 262K | | `kimi-k2.6` | Moonshot | 262K | | `minimax-m2-7` | MiniMax | 205K | | `minimax-m2-7-highspeed` | MiniMax | 205K | | `qwen-3.6-plus` | Alibaba | 1M | To list available models: ```bash curl -s -H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \ https://api.auriko.ai/v1/models | jq '.data[].id' ``` The `/model` picker in interactive sessions lists only Claude tier names (Opus, Sonnet, and Haiku). To switch to a non-Claude model mid-session, type the full ID: `/model deepseek-v4-flash`. You can override which model each tier maps to: ```bash export ANTHROPIC_DEFAULT_SONNET_MODEL="deepseek-v4-flash" export ANTHROPIC_DEFAULT_OPUS_MODEL="claude-opus-4-7" export ANTHROPIC_DEFAULT_HAIKU_MODEL="claude-haiku-4-5-20251001" ``` Add these to your shell profile alongside the other environment variables. --- ## Page: Claude Agent SDK > Section: Set up Claude Agent SDK The Claude Agent SDK is Python-only. For TypeScript, use the Anthropic SDK directly (see below). The Claude Agent SDK spawns Claude Code as a subprocess. Pass Auriko credentials through `ClaudeAgentOptions.env`: ```python import os from claude_agent_sdk import ClaudeAgentOptions, query options = ClaudeAgentOptions( model="sonnet", system_prompt="You are a code review assistant.", allowed_tools=["Read", "Grep", "Glob"], env={ "ANTHROPIC_BASE_URL": "https://api.auriko.ai", "ANTHROPIC_AUTH_TOKEN": os.environ["AURIKO_API_KEY"], "ANTHROPIC_API_KEY": "", }, ) async for message in query(prompt="Review main.py for bugs", options=options): print(message) ``` To prevent filesystem settings from overriding your `env` values, pass `setting_sources=[]` in options. --- ## Page: Claude Agent SDK > Section: Use the Anthropic SDK directly You can point the Anthropic SDK at Auriko's API: ```python Python Auriko import os import anthropic client = anthropic.Anthropic( base_url="https://api.auriko.ai", api_key=os.environ["AURIKO_API_KEY"], ) response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], ) ``` ```typescript TypeScript Auriko import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://api.auriko.ai", apiKey: process.env.AURIKO_API_KEY, }); const response = await client.messages.create({ model: "claude-sonnet-4-6", max_tokens: 1024, messages: [{ role: "user", content: "Hello" }], }); ``` ```bash cURL curl https://api.auriko.ai/v1/messages \ -H "x-api-key: $AURIKO_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello"}] }' ``` --- ## Page: Claude Agent SDK > Section: Configure routing Add a `gateway` object to the request body: ```python Python Auriko response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], extra_body={ "gateway": { "routing": {"optimize": "cost"}, }, }, ) ``` ```typescript TypeScript Auriko const response = await client.messages.create({ model: "claude-sonnet-4-6", max_tokens: 1024, messages: [{ role: "user", content: "Hello" }], // @ts-expect-error -- gateway is an Auriko extension, not in Anthropic SDK types gateway: { routing: { optimize: "cost" }, }, }); ``` ```bash cURL curl https://api.auriko.ai/v1/messages \ -H "x-api-key: $AURIKO_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello"}], "gateway": {"routing": {"optimize": "cost"}} }' ``` For Claude Code, configure routing at the workspace level in the [Auriko dashboard](https://auriko.ai/dashboard). See [routing options](/guides/routing-options) for details. --- ## Page: Claude Agent SDK > Section: Troubleshoot | Symptom | Cause | Fix | |---------|-------|-----| | "model may not exist or you may not have access" | Model ID isn't exact (e.g., `claude-haiku-4-5` instead of `claude-haiku-4-5-20251001`) | Use the full model ID from `GET /v1/models` | | Requests go to Anthropic directly, not Auriko | `ANTHROPIC_API_KEY` contains a value | Set `ANTHROPIC_API_KEY=""` (empty string, not unset) | | "Invalid API Key" or auth errors | Cached Anthropic OAuth credentials | Run `claude auth logout`, then verify env vars are set | | Requests hang or timeout | `ANTHROPIC_BASE_URL` includes `/v1` | Use `https://api.auriko.ai` only | | "does not support reasoning/extended thinking" | Claude Code requires reasoning support but this model doesn't have it | Use a reasoning-capable model (see "Use different models" above) | | `apiKeySource: none` in session events | Claude Code doesn't classify `ANTHROPIC_AUTH_TOKEN` as a key source | Expected behavior. Requests authenticate correctly | --- ## Page: Google ADK Use Auriko as your LLM provider in Google's Agent Development Kit (ADK). This integration is Python-only. For TypeScript, use the [Vercel AI SDK](/frameworks/vercel-ai-sdk) integration. --- ## Page: Google ADK > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) --- ## Page: Google ADK > Section: Install ```bash pip install "auriko[adk]" ``` --- ## Page: Google ADK > Section: Use SDK adapter Use the `AurikoLlm` adapter: ```python from auriko.frameworks.adk import AurikoLlm llm = AurikoLlm(model="gpt-5.4") ``` `AurikoLlm` is a native `BaseLlm` implementation that supports text and function calling. It maps OpenAI API errors to typed Auriko error classes. ```python import asyncio from auriko.frameworks.adk import AurikoLlm from google.adk import Agent, Runner from google.adk.sessions import InMemorySessionService from google.genai import types llm = AurikoLlm(model="gpt-5.4") agent = Agent( model=llm, name="assistant", instruction="You are a helpful assistant.", ) session_service = InMemorySessionService() runner = Runner(agent=agent, app_name="my_app", session_service=session_service, auto_create_session=True) user_message = types.Content( role="user", parts=[types.Part(text="What is 2+2?")] ) async def main(): async for event in runner.run_async(user_id="user-1", session_id="session-1", new_message=user_message): if event.content and event.content.parts: for part in event.content.parts: if part.text: print(part.text, end="", flush=True) asyncio.run(main()) ``` `inline_data` and `file_data` parts raise `NotImplementedError`. --- ## Page: Google ADK > Section: Configure options | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `model` | `str` | (required) | Model ID | | `api_key` | `str` | `AURIKO_API_KEY` env | API key | | `routing` | `RoutingOptions \| None` | `None` | Routing configuration | | `base_url` | `str` | `"https://api.auriko.ai/v1"` | API base URL | --- ## Page: Google ADK > Section: Configure routing Pass a `RoutingOptions` instance to control routing: ```python from auriko.frameworks.adk import AurikoLlm from auriko.route_types import RoutingOptions llm = AurikoLlm( model="gpt-5.4", routing=RoutingOptions(optimize="cost"), ) ``` --- ## Page: Google ADK > Section: Configure manually If you prefer to use Google's `LiteLlm` class directly: ```python import os from google.adk.models.lite_llm import LiteLlm llm = LiteLlm( model="openai/gpt-5.4", api_key=os.environ["AURIKO_API_KEY"], api_base="https://api.auriko.ai/v1", custom_llm_provider="openai", ) ``` LiteLLM ignores `api_base` for model names containing provider keywords (like `gpt` or `claude`). Always include `custom_llm_provider="openai"` to force LiteLLM to respect your custom base URL. For routing options and typed error mapping, use `AurikoLlm`. --- ## Page: CrewAI Use Auriko as your LLM provider in CrewAI for cost-effective multi-agent workflows. This integration is Python-only. For TypeScript, use the [Vercel AI SDK](/frameworks/vercel-ai-sdk) integration. --- ## Page: CrewAI > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) --- ## Page: CrewAI > Section: Install ```bash pip install "auriko[crewai]" ``` --- ## Page: CrewAI > Section: Use SDK adapter Use the `AurikoCrewAILLM` adapter: ```python from auriko.frameworks.crewai import AurikoCrewAILLM auriko_llm = AurikoCrewAILLM(model="gpt-5.4") ``` `AurikoCrewAILLM` routes models through Auriko's OpenAI-compatible endpoint. It passes `provider="openai"` to CrewAI, which prevents CrewAI from routing to native provider SDKs. ```python from crewai import Agent, Task, Crew researcher = Agent( role="Researcher", goal="Find accurate and comprehensive information", backstory="You are an expert researcher with attention to detail.", llm=auriko_llm.llm, verbose=True, ) writer = Agent( role="Writer", goal="Write clear, engaging content based on research", backstory="You are a skilled technical writer.", llm=auriko_llm.llm, verbose=True, ) research_task = Task( description="Research the latest trends in AI agents", agent=researcher, expected_output="A detailed summary of AI agent trends with sources", ) writing_task = Task( description="Write a blog post based on the research findings", agent=writer, expected_output="A 500-word blog post about AI agent trends", context=[research_task], ) crew = Crew( agents=[researcher, writer], tasks=[research_task, writing_task], verbose=True, ) result = crew.kickoff() print(result) ``` Pass `auriko_llm.llm` to `Agent`, not the `AurikoCrewAILLM` instance. --- ## Page: CrewAI > Section: Configure options | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `model` | `str` | (required) | Model ID (e.g., `"gpt-5.4"`, `"claude-sonnet-4-20250514"`) | | `api_key` | `str \| None` | `AURIKO_API_KEY` env | API key | | `routing` | `RoutingOptions \| None` | `None` | Routing configuration | | `base_url` | `str` | `"https://api.auriko.ai/v1"` | API base URL | | `reasoning_effort` | `str \| None` | `None` | Reasoning effort: `"low"`, `"medium"`, `"high"`, `"xhigh"`, `"max"`, `"off"` | | `stop` | `str \| list[str] \| None` | `None` | Stop sequences | | `**kwargs` | | | Passed through to `crewai.LLM` | --- ## Page: CrewAI > Section: Configure routing Pass a `RoutingOptions` instance to control routing: ```python from auriko.frameworks.crewai import AurikoCrewAILLM from auriko.route_types import RoutingOptions auriko_llm = AurikoCrewAILLM( model="gpt-5.4", routing=RoutingOptions(optimize="cost"), ) metadata = auriko_llm.last_routing_metadata if metadata: print(f"Provider: {metadata.provider}") ``` `last_routing_metadata` returns metadata from the most recent non-streaming response. Different agents can use different models and routing strategies: ```python fast_llm = AurikoCrewAILLM(model="gpt-4o", routing=RoutingOptions(optimize="ttft-focus")) smart_llm = AurikoCrewAILLM(model="gpt-5.4", routing=RoutingOptions(optimize="balanced")) researcher = Agent(role="Researcher", goal="Find information", backstory="Expert", llm=smart_llm.llm) writer = Agent(role="Writer", goal="Write content", backstory="Skilled writer", llm=fast_llm.llm) ``` --- ## Page: CrewAI > Section: Configure manually If you prefer to use CrewAI's `LLM` class directly, pass `provider="openai"` to route models through Auriko: ```python import os from crewai import LLM llm = LLM( model="gpt-5.4", provider="openai", # routes models through Auriko base_url="https://api.auriko.ai/v1", api_key=os.environ["AURIKO_API_KEY"], ) ``` For routing options and metadata access, use `AurikoCrewAILLM`. --- ## Page: LlamaIndex Use Auriko as your LLM provider in LlamaIndex. This integration is Python-only. For TypeScript, use the [Vercel AI SDK](/frameworks/vercel-ai-sdk) integration. --- ## Page: LlamaIndex > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) --- ## Page: LlamaIndex > Section: Install ```bash pip install "auriko[llamaindex]" ``` --- ## Page: LlamaIndex > Section: Use SDK adapter Use the `AurikoLlamaIndexLLM` adapter: ```python from auriko.frameworks.llamaindex import AurikoLlamaIndexLLM llm = AurikoLlamaIndexLLM(model="gpt-5.4") ``` `AurikoLlamaIndexLLM` supports chat, completion, streaming, async, per-call routing overrides, and Auriko error mapping. ```python from auriko.frameworks.llamaindex import AurikoLlamaIndexLLM from llama_index.core.llms import ChatMessage llm = AurikoLlamaIndexLLM(model="gpt-5.4") response = llm.chat([ChatMessage(role="user", content="What is 2+2?")]) print(response.message.content) for chunk in llm.stream_chat([ChatMessage(role="user", content="Count to 5")]): print(chunk.delta, end="", flush=True) ``` --- ## Page: LlamaIndex > Section: Configure options | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `model` | `str` | (required) | Model ID | | `api_key` | `str \| None` | `AURIKO_API_KEY` env | API key | | `routing` | `RoutingOptions \| None` | `None` | Default routing configuration | | `api_base` | `str` | `"https://api.auriko.ai/v1"` | API base URL | | `**kwargs` | | | Passed through to LlamaIndex's `OpenAI` (e.g., `temperature`, `max_tokens`) | --- ## Page: LlamaIndex > Section: Configure routing Pass a `RoutingOptions` instance to set default routing: ```python from auriko.frameworks.llamaindex import AurikoLlamaIndexLLM from auriko.route_types import RoutingOptions llm = AurikoLlamaIndexLLM( model="gpt-5.4", routing=RoutingOptions(optimize="cost"), ) ``` Per-call routing overrides the instance default: ```python from auriko.route_types import RoutingOptions response = llm.chat( [ChatMessage(role="user", content="Hello!")], routing=RoutingOptions(optimize="tps-focus"), ) ``` Access routing metadata from the response: ```python response = llm.chat([ChatMessage(role="user", content="Hello!")]) metadata = response.additional_kwargs.get("routing_metadata") if metadata: print(f"Provider: {metadata['provider']}") ``` --- ## Page: LlamaIndex > Section: Configure manually If you prefer to use LlamaIndex's `OpenAI` class directly: ```python import os from llama_index.llms.openai import OpenAI llm = OpenAI( model="gpt-5.4", api_key=os.environ["AURIKO_API_KEY"], api_base="https://api.auriko.ai/v1", ) ``` For routing options, per-call overrides, and Auriko error mapping, use `AurikoLlamaIndexLLM`. --- ## Page: LlamaIndex > Section: Use `AurikoAsyncOpenAI` (experimental) If your project pins a different `llama-index-llms-openai` version, pass `AurikoAsyncOpenAI` as the `async_openai_client`: ```python from llama_index.llms.openai import OpenAI from auriko import AurikoAsyncOpenAI client = AurikoAsyncOpenAI() llm = OpenAI( model="gpt-4o", async_openai_client=client, api_key="placeholder", ) ``` LlamaIndex's `OpenAI` requires an `api_key` for construction. Pass any placeholder value. Read `client.last_routing_metadata` after each call. See [`AurikoAsyncOpenAI`](/sdk/python-reference#aurikoasyncopenai-experimental) for the full class reference. === # Auriko Platform ## Page: Plans and billing Auriko offers two plans: Free and Pro. Both include access to all models with zero inference markup. For custom limits, SSO, and dedicated support, [contact us](https://auriko.ai/contact-us) about Enterprise. --- ## Page: Plans and billing > Section: Compare plans | Feature | Free | Pro | |---------|------|-----| | Price | $0 | $89/mo | | Platform RPM | 1,000 | Unlimited | | BYOK RPM | 10,000 | Unlimited | | BYOK monthly cap | 5,000,000 | Unlimited | | API keys | 2 | Unlimited | | Workspace members | 1 | Unlimited | | Priority routing | — | Yes | | Budget controls | Yes | Yes | | Support | Community | Email | --- ## Page: Plans and billing > Section: Start a free trial You can start a 14-day Pro trial from the [pricing page](https://auriko.ai/pricing) or your [dashboard](https://auriko.ai/dashboard). No credit card required. When your trial ends, your account moves to Free. Auriko doesn't charge you or upgrade your plan without your consent. --- ## Page: Plans and billing > Section: Upgrade to Pro Upgrade anytime from the [dashboard](https://auriko.ai/dashboard) billing page. Pro is a flat $89/mo subscription billed separately from inference costs. You purchase credits at provider token prices with zero markup. Credits work the same on Free and Pro. Check your balance programmatically with the [credit balance endpoint](/api-reference/get-credit-balance). --- ## Page: Plans and billing > Section: Review Pro-only features **Priority routing** — available on Pro. **Team features** — Pro removes the 1-member and 2-key limits. You can add unlimited workspace members and API keys from the dashboard. --- ## Page: Rate limits Rate limits scale with your plan. See [Plans and billing](/platform/plans) for tier thresholds, requests-per-minute (RPM) limits, and monthly caps. --- ## Page: Rate limits > Section: Prerequisites - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) --- ## Page: Rate limits > Section: Check rate limit headers Chat completion responses include request-level rate limit headers: | Header | Description | |--------|-------------| | `Retry-After` | Seconds until rate limit resets; present on 429 responses only (RFC 7231) | | `X-RateLimit-Limit-Requests` | Requests allowed per window | | `X-RateLimit-Remaining-Requests` | Requests remaining in current window | | `X-RateLimit-Reset-Requests` | ISO 8601 timestamp when the window resets | --- ## Page: Rate limits > Section: Handle 429 responses When you exceed a rate limit, the API returns a `429 Too Many Requests` response with a `Retry-After` header: ```json { "error": { "message": "Rate limit exceeded. Retry after 12 seconds.", "type": "rate_limit_error", "param": null, "code": "rate_limit_exceeded", "doc_url": "https://docs.auriko.ai/errors/rate_limit_exceeded" } } ``` The Auriko SDK ([Python](/sdk/python), [TypeScript](/sdk/typescript)) retries failed requests with exponential backoff (up to 2 retries by default). For manual handling, see [Error handling — Retry manually](/guides/error-handling#retry-manually). --- ## Page: Bring your own key *Bring Your Own Key (BYOK)* lets you use your own provider API keys instead of Auriko's shared pool. You control the key, the billing relationship, and any provider-specific quotas. --- ## Page: Bring your own key > Section: Route with BYOK To restrict a request to your own keys, set `gateway.routing.only_byok`: ```python Python OpenAI import os from openai import OpenAI client = OpenAI( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1", ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], extra_body={"gateway": {"routing": {"only_byok": True}}}, ) ``` ```typescript TypeScript OpenAI import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.AURIKO_API_KEY, baseURL: "https://api.auriko.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { only_byok: true } }, }); console.log(response.choices[0].message.content); ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) # Use only your own keys response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], gateway={"routing": {"only_byok": True}} ) # Use only platform keys (no BYOK) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], gateway={"routing": {"only_platform": True}} ) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); // Use only your own keys const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { only_byok: true } }, }); // Use only platform keys (no BYOK) const platform = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hello!" }], gateway: { routing: { only_platform: true } }, }); ``` ```bash cURL # Use only your own keys curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"only_byok": true}} }' # Use only platform keys (no BYOK) curl https://api.auriko.ai/v1/chat/completions \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}], "gateway": {"routing": {"only_platform": true}} }' ``` See [Routing options](/guides/routing-options#restrict-key-source) for the full constraint API. --- ## Page: Bring your own key > Section: Understand routing behavior Auriko prefers your BYOK key when one exists for the requested provider. You get direct billing control and your provider tier applies. Auriko falls back to platform keys in two cases: 1. **Rate-limited:** your BYOK key is rate-limited and the platform key has capacity. 2. **Unavailable:** your BYOK key is unavailable at request time and a platform key exists. Override the default with routing constraints: - `only_byok: true` — use only your BYOK key and fail the request if unavailable. - `only_platform: true` — ignore BYOK keys entirely. BYOK requests count toward a monthly cap that scales with your plan. See [Plans and billing](/platform/plans#compare-plans) for caps by tier. --- ## Page: Bring your own key > Section: Set up BYOK keys Manage your provider keys in the [Auriko Dashboard](https://auriko.ai/dashboard). 1. Go to **Settings > Provider Keys** in the dashboard. 2. Click **Add Provider Key**. 3. Select the provider and paste your API key. 4. Auriko validates the key before saving. --- ## Page: Bring your own key > Section: Manage keys via the API You can also manage BYOK keys programmatically with an API key carrying the `byok:read` / `byok:write` scopes: - [List BYOK providers](/api-reference/list-byok-providers) and [provider account tiers](/api-reference/get-byok-provider-tiers) — public discovery endpoints, the source of allowed `account_tier` values. - [Create](/api-reference/create-byok-key), [list](/api-reference/list-byok-keys), [get](/api-reference/get-byok-key), [update](/api-reference/update-byok-key), and [delete](/api-reference/delete-byok-key) BYOK keys. Submitted secrets are write-only: validated against the provider at creation, encrypted at rest, and never returned. `byok:write` changes which third-party credentials Auriko uses for future model calls. Treat it as a sensitive permission. --- ## Page: Bring your own key > Section: List supported providers `GET /v1/registry/providers` returns the current list of supported providers. The endpoint is public and doesn't require authentication: ```bash cURL curl https://api.auriko.ai/v1/registry/providers ``` See the [Provider catalog](/api-reference/provider-catalog) reference for the full response schema. --- ## Page: Bring your own key > Section: Detect provider tier Auriko detects your provider account tier on first use. The detected tier affects rate limits and routing decisions. You can override detection: - **Enterprise flag** — mark a key as enterprise tier in the dashboard for higher limits. - **Manual tier** — select a specific tier for providers that require it (e.g., Google AI Studio), in the dashboard or via `account_tier` on the [create](/api-reference/create-byok-key) and [update](/api-reference/update-byok-key) endpoints. Once you set a tier manually, Auriko stops detecting that key's tier. --- ## Page: Bring your own key > Section: Secure your keys Auriko encrypts your provider keys and isolates them per workspace. - **Encrypted at rest:** authenticated encryption with per-workspace key isolation. - **Masked in responses:** API responses show only a short prefix of each key. - **Decrypted at request time only:** Auriko decrypts your key when calling the provider, then discards it. - **Never logged:** Auriko never logs or persists decrypted keys. --- ## Page: Bring your own key > Section: Control data handling BYOK keys inherit the workspace data policy. Options: `none`, `no_training`, and `zdr` (zero data retention). A per-request `data_policy` overrides the workspace default. Providers that don't meet the required level aren't available for routing. See [Advanced routing](/guides/advanced-routing#data-policy) for more on data policies. === # Auriko Reference --- ## Page: Supported Parameters Auriko's `POST /v1/chat/completions` endpoint accepts the parameters below. The endpoint is OpenAI-compatible with Auriko-specific extensions for routing and metadata. Not all providers support every optional parameter. Auriko drops unsupported ones and includes a warning in `routing_metadata.warnings`. To require full parameter support, set [`require_parameters`](/guides/advanced-routing#filter-by-parameter-support) to `true`. Check which parameters each provider supports via the [model directory endpoint](/api-reference/model-directory). --- ## Page: Supported Parameters > Section: Supported parameters Auriko accepts and forwards 32 parameters to providers. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `extensions` | Extensions | No | | | `frequency_penalty` | number | No | Frequency penalty (-2 to 2). Not supported by all providers. | | `gateway` | object | Conditional | Auriko routing, metadata, and multi-model configuration. Omit for default single-model routing. | | `logit_bias` | object | No | Token logit bias. Not supported by all providers. | | `logprobs` | boolean | No | Return log probabilities. Not supported by all providers. | | `max_completion_tokens` | integer | No | Maximum tokens to generate. Reasoning models (o1/o3) use this field instead of `max_tokens`. | | `max_tokens` | integer | No | Maximum tokens to generate (legacy, use `max_completion_tokens`) | | `messages` | Message[] | Yes | The messages to generate a completion for | | `min_p` | number | No | Min-P sampling. Supported by some vLLM providers. | | `model` | string | Conditional | Model to route to, required for single-model requests. | | `n` | integer | No | Number of completions to generate. Not supported by all providers. | | `parallel_tool_calls` | boolean | No | Allow parallel tool calls | | `presence_penalty` | number | No | Presence penalty (-2 to 2). Not supported by all providers. | | `prompt_cache_key` | string | No | Prompt caching identifier. Supported by OpenAI. | | `reasoning_effort` | string | No | Controls reasoning effort for supported models. | | `repetition_penalty` | number | No | Repetition penalty. Supported by vLLM providers. | | `response_format` | ResponseFormat | No | | | `safety_identifier` | string | No | Safety policy identifier. Supported by OpenAI. | | `seed` | integer | No | Random seed for reproducibility | | `stop` | string \| array | No | Stop sequences. Restrictions vary by provider and model. | | `stream` | boolean | No | Enable streaming responses | | `stream_options` | StreamOptions | No | | | `temperature` | number | No | Sampling temperature (0-2). Some providers restrict this value when reasoning is enabled. | | `tool_choice` | ToolChoice | No | | | `tools` | Tool[] | No | Tools the model can call | | `top_a` | number | No | Top-A sampling. Supported by some vLLM providers. | | `top_k` | integer | No | Top-K sampling. Restricted by some providers when reasoning is enabled. Supported by Anthropic, Google, and vLLM. | | `top_logprobs` | integer | No | Number of top logprobs to return. Requires logprobs support. | | `top_p` | number | No | Nucleus sampling parameter. Some providers restrict this value when reasoning is enabled. | | `user` | string | No | User identifier for abuse detection | | `verbosity` | string | No | Output verbosity control. Supported by OpenAI. | | `web_search_options` | object | No | Web search configuration. Supported by OpenAI. | --- ## Page: Supported Parameters > Section: Deprecated with auto-transform These legacy OpenAI parameters are accepted and converted to their modern equivalents. | Parameter | Transforms To | Reason | |-----------|---------------|--------| | `function_call` | `tool_choice` | deprecated by OpenAI, converted to `tool_choice` | | `functions` | `tools` | deprecated by OpenAI, converted to `tools` | --- ## Page: Supported Parameters > Section: Accepted and dropped These parameters are accepted but not forwarded to providers. A warning is included in `routing_metadata.warnings`. | Parameter | Warning Message | |-----------|----------------| | `metadata` | 'metadata' is not supported. | | `prediction` | 'prediction' is not supported. | | `service_tier` | 'service_tier' is not supported. Use gateway.routing.optimize instead. | | `store` | 'store' is not supported. | | `system` | 'system' is not a Chat Completions parameter. Place system messages in the messages array as {role: 'system', content: '...'}. | --- ## Page: Supported Parameters > Section: Rejected with error These parameters cause the request to fail with an error response. | Parameter | Status | Error Code | Condition | Message | |-----------|--------|------------|-----------|---------| | `audio` | 400 | `unsupported_modalities` | always | audio output is not supported | | `modalities` | 400 | `unsupported_modalities` | when requesting audio output | audio output is not supported | --- ## Page: Error Codes OpenAI-compatible endpoints return the `code` field in the error envelope. Anthropic-compatible endpoints (`/v1/messages`) use a different envelope without `code` (see [Errors](/api-reference/errors#anthropic-error-envelope)). The 59 codes below apply to OpenAI-format responses. | Error Code | HTTP Status | Description | |------------|-------------|-------------| | `batch_only` | `400` | Model is batch-only and cannot serve real-time requests | | `budget_exhausted` | `429` | Account or project budget reached | | `byok_keys_required` | `400` | BYOK keys required but none are configured | | `client_disconnected` | `0` | Client closed the connection before the response completed | | `content_filtered` | `400` | The upstream provider's content filter rejects the request | | `context_length_exceeded` | `400` | Token count exceeds the model's context window | | `cost_constraint_exceeded` | `400` | No provider meets the cost constraint | | `duplicate_resource` | `409` | A resource with this identifier already exists | | `expired_api_key` | `401` | API key has expired | | `feature_disabled` | `403` | Feature is not available for this account or plan | | `field_immutable` | `400` | Field cannot be modified after resource creation | | `hosted_tool_not_supported` | `400` | Model does not support the requested hosted tool on available providers | | `idempotency_conflict` | `409` | Idempotency key reused with a different payload | | `idempotency_replay_unavailable` | `409` | Idempotency key matched a completed request but replay data was not captured | | `input_requires_responses_endpoint` | `400` | Request input contains types served only via the native Response API, and no provider for the model offers it | | `insufficient_permissions` | `403` | Caller authenticated but lacks the required scope | | `insufficient_quota` | `429` | No quota remaining on the current plan | | `internal_error` | `500` | Unexpected server error | | `invalid_api_key` | `401` | API key is missing, malformed, revoked, or unrecognized | | `invalid_parameter_value` | `400` | Field present, value outside allowed range or set | | `invalid_recovery_code` | `401` | MFA recovery code is invalid or already used | | `invalid_request` | `400` | Malformed or invalid request | | `json_mode_not_supported` | `400` | Model does not support JSON mode | | `latency_constraint_exceeded` | `400` | No provider meets the latency constraint | | `method_not_allowed` | `405` | HTTP method not supported on this endpoint | | `mfa_required` | `403` | Action requires multi-factor authentication step-up | | `mfa_verification_failed` | `401` | TOTP verification code is incorrect or expired | | `missing_required_parameter` | `400` | Required parameter not provided | | `model_not_found` | `404` | Requested model ID isn't in the catalog | | `model_unavailable` | `503` | The requested model is temporarily unavailable | | `no_compatible_endpoint` | `400` | No provider supports the required API endpoint for this model | | `no_provider_available` | `503` | No provider can serve this request right now | | `no_responses_endpoint` | `400` | Request requires the Response API but no provider supports it for this model | | `non_streaming_not_supported` | `400` | Model requires streaming | | `operation_not_allowed` | `400` | Operation not permitted for current resource state or role | | `payload_too_large` | `413` | Request body exceeds the size limit | | `platform_keys_unavailable` | `400` | No platform keys available for this model | | `provider_blocked` | `400` | All providers for this model are in the blocklist | | `provider_not_in_allowlist` | `400` | No provider in the allowlist supports this model | | `rate_limit_exceeded` | `429` | Requests-per-minute cap hit | | `reasoning_not_supported` | `400` | Model does not support reasoning/extended thinking | | `required_params_not_supported` | `400` | No provider supports all required parameters | | `resource_not_found` | `404` | Requested resource not found | | `response_api_only` | `400` | Model is only available via the Response API | | `service_unavailable` | `503` | Planned or temporary capacity shortage | | `state_precondition_failed` | `409` | Resource is not in the required state for this operation | | `streaming_not_supported` | `400` | Model does not support streaming | | `structured_output_not_supported` | `400` | Model does not support structured output | | `thinking_disable_not_supported` | `400` | Model does not support disabling thinking | | `throughput_constraint_not_met` | `400` | No provider meets the throughput constraint | | `tier_opt_in_required` | `400` | Model requires explicit tier opt-in (e.g., priority tier for fast mode) | | `tool_choice_required_not_supported` | `400` | Model does not support tool_choice="required" | | `tools_not_supported` | `400` | Model does not support tool/function calling | | `tools_with_structured_output_not_supported` | `400` | Model does not support tools with structured output | | `unknown_field` | `400` | Request contains an unrecognized field | | `unsupported_modalities` | `400` | Audio output is not supported | | `upstream_error` | `502` | An upstream provider returned an invalid or unparseable response | | `upstream_timeout` | `504` | Upstream exceeded the request deadline | | `vision_not_supported` | `400` | Model does not support vision/image input | --- ## Page: Response Metadata Every successful response includes a `routing_metadata` object with details about how the request was routed and its cost. | Field | Type | Required | Description | |-------|------|----------|-------------| | `cost` | CostInfo | No | Cost for the request | | `cost.cache_savings_percent` | integer | No | Cache savings as integer percentage (0-100). Present only when savings > 0. | | `cost.cache_savings_usd` | number | No | Cache savings in USD. Present only when savings > 0. | | `cost.usd` | number | Yes | Billable cost in USD | | `model_canonical` | string | Yes | Canonical model ID requested | | `provider` | string | Yes | Provider name (e.g., "fireworks_ai", "anthropic") | | `provider_model_id` | string | Yes | Provider's model ID | | `routing_strategy` | string | Yes | Strategy used for routing. Known values: `cost`, `cost-focus`, `ttft`, `ttft-focus`, `tps`, `tps-focus`, `balanced`, `custom`. `custom` is returned when explicit `routing.weights` are provided. Additional strategies may be added in future versions. | | `throughput_tps` | number | No | Output throughput (tokens per second) | | `ttft_ms` | number | No | Time to first token (streaming only) | | `warnings` | StructuredWarning[] | No | Structured warnings emitted when the gateway modifies or ignores part of the request (e.g., unsupported parameters, blocked fields). | --- ## Page: Response Headers Auriko defines 19 publicly stable response headers. Not every header appears on every endpoint. Headers are grouped by function. --- ## Page: Response Headers > Section: Request Tracing | Header | Type | Description | |--------|------|-------------| | `X-Request-ID` | string | Unique request identifier for debugging and support. Include this in support requests for fast resolution. | --- ## Page: Response Headers > Section: Rate Limiting | Header | Type | Description | |--------|------|-------------| | `Retry-After` | integer | Seconds until rate limit resets. Standard HTTP header for retry backoff. | | `X-Rate-Limit-Source` | string | Identifies whether the rate limit originated from an upstream provider or from Auriko's routing layer. | | `X-RateLimit-Limit-Requests` | integer | Request limit per window (present on throughput-based rate limits) | | `X-RateLimit-Remaining-Requests` | integer | Remaining requests in window (present on throughput-based rate limits) | | `X-RateLimit-Reset-Requests` | string | When limit resets (ISO 8601 format; present on throughput-based rate limits) | --- ## Page: Response Headers > Section: Billing & Credits | Header | Type | Description | |--------|------|-------------| | `X-Credits-Balance-Microdollars` | integer | Current workspace credit balance in microdollars (1 USD = 1,000,000) | --- ## Page: Response Headers > Section: Budget Budget headers appear on chat completion and `/v1/me` responses when your workspace has budget limits configured. Configure budgets in the Auriko dashboard. | Header | Type | Description | |--------|------|-------------| | `X-Budget-Daily-Limit` | string | Daily budget limit in USD (present when daily budget exists) | | `X-Budget-Daily-Spend` | string | Daily budget spend in USD (present when daily budget exists) | | `X-Budget-Exceeded` | string | Present on budget-exhaustion 429s. | | `X-Budget-Exceeded-Period` | string | Period of the exceeded budget (present on budget-exhaustion 429s). | | `X-Budget-Exceeded-Scope` | string | Scope of the exceeded budget (present on budget-exhaustion 429s). | | `X-Budget-Monthly-Limit` | string | Monthly budget limit in USD (present when monthly budget exists) | | `X-Budget-Monthly-Spend` | string | Monthly budget spend in USD (present when monthly budget exists) | | `X-Budget-Weekly-Limit` | string | Weekly budget limit in USD (present when weekly budget exists) | | `X-Budget-Weekly-Spend` | string | Weekly budget spend in USD (present when weekly budget exists) | --- ## Page: Response Headers > Section: Error Diagnostics | Header | Type | Description | |--------|------|-------------| | `X-Error-Retryable` | string | Whether the client should retry this request. 'true' for api_error and rate_limit_error types; 'false' for all others. | | `X-Error-Type` | string | Same value as the `error.type` field in the response body, exposed as a header for programmatic routing without body parsing. | --- ## Page: Response Headers > Section: Token Counting | Header | Type | Description | |--------|------|-------------| | `X-Token-Count-Model` | string | Model used for token counting when a different model was used. Only present on /v1/messages/count_tokens responses for non-Claude models. | --- ## Page: Canonical models A *canonical model ID* (a stable, provider-agnostic string like `gpt-4o-2024-08-06` or `claude-sonnet-4-6`) is the model identifier you pass in API requests. You can also pass an *alias* — a shorter name that resolves to a canonical ID, such as `claude-opus-4-5` (which resolves to `claude-opus-4-5-20251101`); each model's aliases are listed in its directory entry. Auriko maintains a live model directory. It's the authoritative source for all available models, their status, providers, and pricing. --- ## Page: Canonical models > Section: Query model directory `GET /v1/directory/models` returns every canonical model with full metadata: context window, pricing, capabilities, supported modalities, supported endpoints, and provider availability. The endpoint is public and doesn't require authentication. ```bash cURL curl https://api.auriko.ai/v1/directory/models ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) directory = client.models.list_directory() for canonical_id, model in directory.models.items(): print(canonical_id, model.display_name, len(model.providers), "providers") ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const directory = await client.models.listDirectory(); for (const [canonicalId, model] of Object.entries(directory.models)) { console.log(canonicalId, model.display_name, model.providers.length, "providers"); } ``` See the [Model directory](/api-reference/model-directory) reference for the full response schema. --- ## Page: Canonical models > Section: Query model catalog `GET /v1/registry/models` returns a leaner list of canonical ID, author, status, and provider availability, without pricing details. ```bash cURL curl https://api.auriko.ai/v1/registry/models ``` ```python Python Auriko import os from auriko import Client client = Client( api_key=os.environ["AURIKO_API_KEY"], base_url="https://api.auriko.ai/v1" ) catalog = client.models.list_registry() for model in catalog.models: print(model.id, model.providers) ``` ```typescript TypeScript Auriko import { Client } from "@auriko/sdk"; const client = new Client({ apiKey: process.env.AURIKO_API_KEY, baseUrl: "https://api.auriko.ai/v1", }); const catalog = await client.models.listRegistry(); for (const model of catalog.models) { console.log(model.id, model.providers); } ``` See the [Canonical model records](/api-reference/canonical-model-records) reference for the full response schema. === # Auriko Error Code Reference ## Page: batch_only > Section: HTTP status `400 invalid_request_error` --- ## Page: batch_only > Section: Typical cause The requested model only supports batch processing and cannot handle synchronous or streaming requests. --- ## Page: batch_only > Section: Resolution Submit the request through the batch API endpoint instead, or choose a different model that supports real-time inference. --- ## Page: budget_exhausted > Section: HTTP status `429 rate_limit_error` --- ## Page: budget_exhausted > Section: Typical cause A spending-budget rule (workspace, key, or BYOK provider) is at its limit for the current period. --- ## Page: budget_exhausted > Section: Resolution Raise the budget limit in the dashboard or wait for the next budget period. --- ## Page: byok_keys_required > Section: HTTP status `400 invalid_request_error` --- ## Page: byok_keys_required > Section: Typical cause The workspace or data policy requires bring-your-own-key (BYOK) credentials for this model, but no BYOK keys are configured. --- ## Page: byok_keys_required > Section: Resolution Add your own API key for the required provider in the dashboard under BYOK settings. --- ## Page: client_disconnected > Section: HTTP status Not returned in API responses. This code appears only in internal observability data (`request_metrics`). --- ## Page: client_disconnected > Section: Typical cause The client (browser, SDK, curl) closed the connection mid-stream. --- ## Page: client_disconnected > Section: Resolution No action needed. This is normal client behavior, not a provider or platform error. --- ## Page: content_filtered > Section: HTTP status `400 invalid_request_error` --- ## Page: content_filtered > Section: Typical cause The upstream provider's content filter rejects the request. --- ## Page: content_filtered > Section: Resolution Rephrase the request or use a different model. The specific filter criteria vary by provider. --- ## Page: context_length_exceeded > Section: HTTP status `400 invalid_request_error` --- ## Page: context_length_exceeded > Section: Typical cause The combined prompt plus requested output tokens exceeds the target model's context window. --- ## Page: context_length_exceeded > Section: Resolution Shorten the prompt, reduce `max_tokens`, or switch to a model with a larger context window. --- ## Page: cost_constraint_exceeded > Section: HTTP status `400 invalid_request_error` --- ## Page: cost_constraint_exceeded > Section: Typical cause The `max_cost_per_1m` constraint excludes all available providers for this model. --- ## Page: cost_constraint_exceeded > Section: Resolution Raise the cost threshold, remove the cost constraint, or choose a cheaper model. --- ## Page: duplicate_resource > Section: HTTP status `409 invalid_request_error` --- ## Page: duplicate_resource > Section: Typical cause A resource with the same identifier already exists. --- ## Page: duplicate_resource > Section: Resolution Use the existing resource or choose a different identifier. --- ## Page: expired_api_key > Section: HTTP status `401 authentication_error` --- ## Page: expired_api_key > Section: Typical cause The API key presented has an expiration timestamp in the past. --- ## Page: expired_api_key > Section: Resolution Rotate the key in the dashboard and update any stored credentials. --- ## Page: feature_disabled > Section: HTTP status `403 permission_error` --- ## Page: feature_disabled > Section: Error response ```json { "error": { "message": "API key limit reached (2). Upgrade to Pro for unlimited keys.", "type": "permission_error", "param": null, "code": "feature_disabled", "doc_url": "https://docs.auriko.ai/errors/feature_disabled" } } ``` --- ## Page: feature_disabled > Section: Typical cause The requested feature requires a higher plan tier or isn't available for your account. The `message` field identifies the specific limit or restriction. --- ## Page: feature_disabled > Section: Features with plan limits - **API keys**: Free plan allows 2 keys per workspace. - **Workspace members**: Free plan allows 1 member per workspace. See [Plans and billing](/platform/plans#compare-plans) for all plan limits. --- ## Page: feature_disabled > Section: Resolution Check your current plan in the [dashboard](https://auriko.ai/dashboard). To access Pro features, [upgrade your plan](/platform/plans#upgrade-to-pro). --- ## Page: field_immutable > Section: HTTP status `400 invalid_request_error` --- ## Page: field_immutable > Section: Typical cause The request attempts to modify a field that can't change after creation. --- ## Page: field_immutable > Section: Resolution Create a new resource with the desired value instead. --- ## Page: hosted_tool_not_supported > Section: HTTP status `400 invalid_request_error` --- ## Page: hosted_tool_not_supported > Section: Typical cause You included a hosted tool (e.g., `web_search_preview`, `file_search`) that the selected model doesn't support. --- ## Page: hosted_tool_not_supported > Section: Resolution Remove the hosted tool from the request, or switch to a model that supports it. Function tools (`type: "function"`) work with all models that support tool calling. --- ## Page: idempotency_conflict > Section: HTTP status `409 invalid_request_error` --- ## Page: idempotency_conflict > Section: Typical cause The `Idempotency-Key` header matches a prior request but the payload differs from what was originally submitted. --- ## Page: idempotency_conflict > Section: Resolution Either reuse the exact original payload or pick a new idempotency key. --- ## Page: idempotency_replay_unavailable > Section: HTTP status `409 invalid_request_error` --- ## Page: idempotency_replay_unavailable > Section: Typical cause The `Idempotency-Key` matches a completed request, but the server didn't capture the original response data. This happens when the response couldn't be stored after a successful mutation. --- ## Page: idempotency_replay_unavailable > Section: Resolution Use `GET` to retrieve the resource created by the original request. --- ## Page: input_requires_responses_endpoint > Section: HTTP status `400 invalid_request_error` --- ## Page: input_requires_responses_endpoint > Section: Typical cause Your conversation history contains round-tripped Response API output items (for example `custom_tool_call` items or `refusal` content parts), and no provider for the model serves the Response API natively. --- ## Page: input_requires_responses_endpoint > Section: Resolution Use a model with native Response API support. --- ## Page: insufficient_permissions > Section: HTTP status `403 permission_error` --- ## Page: insufficient_permissions > Section: Typical cause The API key is valid but doesn't have permission for the requested resource or action. This includes workspace-level policy blocks such as platform key restrictions or account-level access controls. --- ## Page: insufficient_permissions > Section: Resolution Check the key's scopes in your dashboard, or contact your workspace admin for the required permission. --- ## Page: insufficient_quota > Section: HTTP status `429 rate_limit_error` --- ## Page: insufficient_quota > Section: Typical cause The account has exhausted the included quota on its current plan. --- ## Page: insufficient_quota > Section: Resolution Top up credits or upgrade the plan. --- ## Page: internal_error > Section: HTTP status `500 api_error` --- ## Page: internal_error > Section: Typical cause An unexpected internal error. Contact support with the `request_id` to investigate. --- ## Page: internal_error > Section: Resolution Retry with backoff. Persistent failures should be reported with the `request_id` value. --- ## Page: invalid_api_key > Section: HTTP status `401 authentication_error` --- ## Page: invalid_api_key > Section: Typical cause The API key in the `Authorization: Bearer` header is missing, malformed, revoked, or doesn't match any active key. --- ## Page: invalid_api_key > Section: Resolution Check the key value in your dashboard. Generate a new key if needed and update any stored credentials. --- ## Page: invalid_parameter_value > Section: HTTP status `400 invalid_request_error` --- ## Page: invalid_parameter_value > Section: Typical cause A request field was provided but its value violates a schema constraint (enum, range, format, etc.). The `param` field names the offender. --- ## Page: invalid_parameter_value > Section: Resolution Check the schema for the allowed values and retry with a valid value. --- ## Page: invalid_recovery_code > Section: HTTP status `401 authentication_error` --- ## Page: invalid_recovery_code > Section: Typical cause The MFA recovery code is invalid or has already been used. --- ## Page: invalid_recovery_code > Section: Resolution Check the recovery code and retry. Contact support if you've lost your codes. --- ## Page: invalid_request > Section: HTTP status `400 invalid_request_error` --- ## Page: invalid_request > Section: Typical cause The request body is malformed, unparseable, or fails a generic validation check. Used when no more specific code applies. --- ## Page: invalid_request > Section: Resolution Re-check the request payload against the OpenAPI schema. For specific field errors the response carries a more specific `code`. --- ## Page: json_mode_not_supported > Section: HTTP status `400 invalid_request_error` --- ## Page: json_mode_not_supported > Section: Typical cause The request sets `response_format: { type: "json_object" }` but the selected model does not support JSON mode output. --- ## Page: json_mode_not_supported > Section: Resolution Remove the `response_format` parameter or choose a model that supports JSON mode. --- ## Page: latency_constraint_exceeded > Section: HTTP status `400 invalid_request_error` --- ## Page: latency_constraint_exceeded > Section: Typical cause The `max_ttft_ms` constraint excludes all available providers for this model. --- ## Page: latency_constraint_exceeded > Section: Resolution Raise the latency threshold, remove the latency constraint, or choose a model with faster providers. --- ## Page: method_not_allowed > Section: HTTP status `405 invalid_request_error` --- ## Page: method_not_allowed > Section: Typical cause The HTTP method used (e.g., `GET` instead of `POST`) isn't supported on this endpoint. --- ## Page: method_not_allowed > Section: Resolution Check the API reference for the correct HTTP method. --- ## Page: mfa_required > Section: HTTP status `403 permission_error` --- ## Page: mfa_required > Section: Typical cause The requested action requires the session to complete a multi-factor authentication step-up before proceeding. --- ## Page: mfa_required > Section: Resolution Complete the MFA challenge in the dashboard and retry the request with the refreshed session. --- ## Page: mfa_verification_failed > Section: HTTP status `401 authentication_error` --- ## Page: mfa_verification_failed > Section: Typical cause The TOTP code is incorrect or has expired. --- ## Page: mfa_verification_failed > Section: Resolution Check the code in your authenticator app and retry. --- ## Page: missing_required_parameter > Section: HTTP status `400 invalid_request_error` --- ## Page: missing_required_parameter > Section: Typical cause A required request field is absent. The `param` field on the response names the missing field. --- ## Page: missing_required_parameter > Section: Resolution Add the named parameter to the request payload and retry. --- ## Page: model_not_found > Section: HTTP status `404 not_found_error` --- ## Page: model_not_found > Section: Typical cause The `model` parameter references a model that doesn't exist in Auriko's canonical catalog, or the requested provider variant is not available. --- ## Page: model_not_found > Section: Model ID format Auriko resolves `model` by exact match, so the value must be: - **A bare canonical ID** — for example `gpt-4o-mini-2024-07-18`, not a vendor-prefixed form like `openai/gpt-4o-mini`. - **Case-sensitive** — `GPT-4o-mini-2024-07-18` does not match `gpt-4o-mini-2024-07-18`. Many models also accept a shorter **alias** (for example `claude-opus-4-5`, which resolves to `claude-opus-4-5-20251101`). Each model's aliases are listed in its [directory entry](https://api.auriko.ai/v1/directory/models). --- ## Page: model_not_found > Section: Resolution Check the model ID against the [models directory](https://api.auriko.ai/v1/directory/models) and retry with a valid model ID. --- ## Page: model_unavailable > Section: HTTP status `503 api_error` --- ## Page: model_unavailable > Section: Typical cause The requested model is temporarily unavailable due to an upstream outage or maintenance. --- ## Page: model_unavailable > Section: Resolution Retry with `Retry-After` or fall back to a different model. --- ## Page: no_compatible_endpoint > Section: HTTP status `400 invalid_request_error` --- ## Page: no_compatible_endpoint > Section: Typical cause The model is in the catalog, but none of its providers serve the API you're calling. Models that are only served via the Response API return [`response_api_only`](/errors/response_api_only) instead. --- ## Page: no_compatible_endpoint > Section: Resolution Check which APIs the model supports: read `supported_endpoints` from [`GET /v1/models?endpoint=responses`](/api-reference/list-callable-models) or from each provider entry in the [model directory](/api-reference/model-directory). Then call a supported endpoint, or choose a model that supports the API you need. --- ## Page: no_provider_available > Section: HTTP status `503 api_error` --- ## Page: no_provider_available > Section: Typical cause All providers capable of serving the requested model are temporarily unavailable (e.g., all are rate-limited, down, or have no valid key candidates). This is a transient condition — distinct from capability or constraint mismatches, which return specific 400-level codes. --- ## Page: no_provider_available > Section: Resolution Retry with exponential backoff, honoring the `Retry-After` header if present. --- ## Page: no_responses_endpoint > Section: HTTP status `400 invalid_request_error` --- ## Page: no_responses_endpoint > Section: Typical cause You sent a request to `/v1/responses` for a model that isn't available via the Response API. --- ## Page: no_responses_endpoint > Section: Resolution Switch to [Chat Completions](/api-reference/chat-completions) for this model, or choose a model with Response API support. --- ## Page: non_streaming_not_supported > Section: HTTP status `400 invalid_request_error` --- ## Page: non_streaming_not_supported > Section: Typical cause The request sets `stream: false` (or omits it) but the selected model only supports streaming responses. --- ## Page: non_streaming_not_supported > Section: Resolution Set `stream: true` or choose a model that supports non-streaming requests. --- ## Page: operation_not_allowed > Section: HTTP status `400 invalid_request_error` --- ## Page: operation_not_allowed > Section: Typical cause The operation isn't permitted given the resource's current state or the caller's role. --- ## Page: operation_not_allowed > Section: Resolution Check the resource state and your permissions, then retry. --- ## Page: payload_too_large > Section: HTTP status `413 invalid_request_error` --- ## Page: payload_too_large > Section: Typical cause The serialized request body is larger than the endpoint's maximum payload size. --- ## Page: payload_too_large > Section: Resolution Trim or chunk the request (fewer messages, shorter content) and retry. --- ## Page: platform_keys_unavailable > Section: HTTP status `400 invalid_request_error` --- ## Page: platform_keys_unavailable > Section: Typical cause The workspace is configured to use platform-managed keys, but no platform key is available for the requested model's provider. --- ## Page: platform_keys_unavailable > Section: Resolution Configure BYOK keys for the provider, or contact support to verify platform key availability. --- ## Page: provider_blocked > Section: HTTP status `400 invalid_request_error` --- ## Page: provider_blocked > Section: Typical cause The `exclude_providers` parameter or workspace policy blocks every provider that serves the requested model. --- ## Page: provider_blocked > Section: Resolution Remove providers from the blocklist, or choose a model available on a non-blocked provider. --- ## Page: provider_not_in_allowlist > Section: HTTP status `400 invalid_request_error` --- ## Page: provider_not_in_allowlist > Section: Typical cause The `providers` allowlist parameter restricts routing to providers that do not serve the requested model. --- ## Page: provider_not_in_allowlist > Section: Resolution Add a provider that supports the model to the allowlist, or remove the allowlist constraint. --- ## Page: rate_limit_exceeded > Section: HTTP status `429 rate_limit_error` --- ## Page: rate_limit_exceeded > Section: Typical cause The account or key hit the requests-per-minute cap on the current plan. --- ## Page: rate_limit_exceeded > Section: Resolution Back off using the `Retry-After` header or exponential backoff. Upgrade the plan to raise the cap if the ceiling is persistent. --- ## Page: reasoning_not_supported > Section: HTTP status `400 invalid_request_error` --- ## Page: reasoning_not_supported > Section: Typical cause The request enables reasoning or extended thinking (`thinking: { type: "enabled" }`) but the selected model does not support it. --- ## Page: reasoning_not_supported > Section: Resolution Remove the thinking/reasoning parameter or choose a model that supports extended thinking. --- ## Page: required_params_not_supported > Section: HTTP status `400 invalid_request_error` --- ## Page: required_params_not_supported > Section: Typical cause The `require_parameters` constraint lists parameters that no single available provider supports for the requested model. --- ## Page: required_params_not_supported > Section: Resolution Relax the required parameters list, or choose a model whose providers support the needed parameters. --- ## Page: resource_not_found > Section: HTTP status `404 not_found_error` --- ## Page: resource_not_found > Section: Typical cause The resource referenced by the request (workspace, key, invite, budget, etc.) does not exist or isn't visible to the caller. --- ## Page: resource_not_found > Section: Resolution Check the resource ID and the caller's permissions. 404 is also returned when the caller lacks permission to view the resource. --- ## Page: response_api_only > Section: HTTP status `400 invalid_request_error` --- ## Page: response_api_only > Section: Typical cause The model is only served via the [Response API](/response-api/overview) — OpenAI pro-tier models such as `gpt-5.5-pro` — and the request uses `/v1/chat/completions` or `/v1/messages`. `/v1/messages` returns this error in the [Anthropic error envelope](/api-reference/errors#anthropic-error-envelope), which carries the same message and `suggestion` but no `code` field. --- ## Page: response_api_only > Section: Resolution Call `POST /v1/responses` with a Response API body (`input`, not `messages`): ```bash curl https://api.auriko.ai/v1/responses \ -H "Authorization: Bearer $AURIKO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "gpt-5.5-pro", "input": "Hello"}' ``` To check endpoint support before sending, read `supported_endpoints` from [`GET /v1/models?endpoint=responses`](/api-reference/list-callable-models) or from each provider entry in the [model directory](/api-reference/model-directory). If your integration only speaks Chat Completions, choose a chat-capable model instead. --- ## Page: service_unavailable > Section: HTTP status `503 api_error` --- ## Page: service_unavailable > Section: Typical cause A platform-level capacity shortage or planned maintenance window is blocking the request. --- ## Page: service_unavailable > Section: Resolution Retry with `Retry-After`. Check the status page for active incidents. --- ## Page: state_precondition_failed > Section: HTTP status `409 invalid_request_error` --- ## Page: state_precondition_failed > Section: Typical cause The resource isn't in the required state for this operation. --- ## Page: state_precondition_failed > Section: Resolution Complete the prerequisite step before retrying. --- ## Page: streaming_not_supported > Section: HTTP status `400 invalid_request_error` --- ## Page: streaming_not_supported > Section: Typical cause The request sets `stream: true` but the selected model does not support streaming responses. --- ## Page: streaming_not_supported > Section: Resolution Set `stream: false` or choose a model that supports streaming. Some models, such as `gpt-5.5-pro` and `o3-pro`, never stream and respond only via the [Response API](/response-api/overview). --- ## Page: structured_output_not_supported > Section: HTTP status `400 invalid_request_error` --- ## Page: structured_output_not_supported > Section: Typical cause The request sets `response_format: { type: "json_schema", ... }` but the selected model does not support structured output with a JSON schema. --- ## Page: structured_output_not_supported > Section: Resolution Remove the structured output format, use JSON mode instead if the model supports it, or choose a model that supports structured output. --- ## Page: thinking_disable_not_supported > Section: HTTP status `400 invalid_request_error` --- ## Page: thinking_disable_not_supported > Section: Typical cause The request sets `reasoning_effort: "off"` but the selected model requires thinking/reasoning and does not allow disabling it. --- ## Page: thinking_disable_not_supported > Section: Resolution Remove `reasoning_effort: "off"` from the request, or choose a model that supports disabling thinking. --- ## Page: throughput_constraint_not_met > Section: HTTP status `400 invalid_request_error` --- ## Page: throughput_constraint_not_met > Section: Typical cause The `min_throughput_tps` constraint exceeds the throughput offered by all available providers for this model. --- ## Page: throughput_constraint_not_met > Section: Resolution Lower the throughput requirement, remove the constraint, or choose a model with higher-throughput providers. --- ## Page: tier_opt_in_required > Section: HTTP status `400 invalid_request_error` --- ## Page: tier_opt_in_required > Section: Typical cause The requested model is available under a premium pricing tier (e.g., Anthropic fast mode at 6x cost). To prevent accidental cost escalation, these offerings require explicit opt-in. Note: Auriko's "priority" tier refers to Anthropic Fast Mode (2.5x speed), not Anthropic's separate Priority Tier (committed capacity SLA). --- ## Page: tier_opt_in_required > Section: Resolution Set `gateway.routing.tier` to `"priority"` in your request to opt in to premium-tier routing. --- ## Page: tool_choice_required_not_supported > Section: HTTP status `400 invalid_request_error` --- ## Page: tool_choice_required_not_supported > Section: Typical cause The request sets `tool_choice: "required"` but the selected model doesn't reliably guarantee a tool call in the response. Auriko rejects this combination to prevent silent failures in agentic pipelines. --- ## Page: tool_choice_required_not_supported > Section: Resolution Use `tool_choice: "auto"` instead. Models still call tools when prompted appropriately. Alternatively, remove any provider constraint to allow routing to a capable provider. --- ## Page: tools_not_supported > Section: HTTP status `400 invalid_request_error` --- ## Page: tools_not_supported > Section: Typical cause The request includes a `tools` array but the selected model does not support tool or function calling. --- ## Page: tools_not_supported > Section: Resolution Remove the `tools` parameter or choose a model that supports function calling. --- ## Page: tools_with_structured_output_not_supported > Section: HTTP status `400 invalid_request_error` --- ## Page: tools_with_structured_output_not_supported > Section: Typical cause The request combines `tools` with `response_format` (`json_object` or `json_schema`), but the selected model does not support using both together. --- ## Page: tools_with_structured_output_not_supported > Section: Resolution Remove `response_format` from the request, or choose a model that supports tools with structured output (e.g., GPT-4o, Claude 4.5+). --- ## Page: unknown_field > Section: HTTP status `400 invalid_request_error` --- ## Page: unknown_field > Section: Typical cause The request body contains a field the endpoint doesn't recognize. --- ## Page: unknown_field > Section: Resolution Remove the unrecognized field from the request. --- ## Page: unsupported_modalities > Section: HTTP status `400 invalid_request_error` --- ## Page: unsupported_modalities > Section: Typical cause The request includes `audio` or `modalities: ["audio"]`, requesting audio output. Auriko does not support audio generation. --- ## Page: unsupported_modalities > Section: Resolution Remove the `audio` field from the request, or remove `"audio"` from the `modalities` array. --- ## Page: upstream_error > Section: HTTP status `502 api_error` --- ## Page: upstream_error > Section: Typical cause An upstream provider returned an unexpected error. --- ## Page: upstream_error > Section: Resolution Retry with backoff. Upstream is the source of truth; the failure may clear on its own. --- ## Page: upstream_timeout > Section: HTTP status `504 api_error` --- ## Page: upstream_timeout > Section: Typical cause The upstream provider did not respond within the deadline for this request. --- ## Page: upstream_timeout > Section: Resolution Set `gateway.routing.timeout_ms` to allow more time per attempt. For fallback chains, also set `gateway.routing.deadline_ms` to extend the overall request budget. You can also retry with backoff, reduce prompt or output size, or try a faster model. --- ## Page: vision_not_supported > Section: HTTP status `400 invalid_request_error` --- ## Page: vision_not_supported > Section: Typical cause The request includes image content (base64 or URL) in a message but the selected model does not support vision input. --- ## Page: vision_not_supported > Section: Resolution Remove image content from the messages or choose a model that supports vision. === # Auriko Changelog ## Page: Changelog Track changes to the Auriko API, SDKs, and supported models. --- ## Page: Changelog > Section: 1.1.0 — 2026-07-12 ### API - Usage field rename: `prompt_tokens_details.cache_creation_tokens` → `cache_write_tokens` (chat completions); `input_tokens_details.cache_write_tokens` added to Response API usage — now populated for Anthropic-routed Response API requests - `tool_usage` field on Response objects: OpenAI built-in tool consumption metrics (image generation tokens, web search request counts) ### Routing & Providers - claude-sonnet-5 (anthropic), gemma-4-12b-it (siliconflow), ornith-1.0-35b (deepinfra), gemini-3.1-flash-lite-image (google), deepinfra provider for minimax-m3 - gpt-5.6-luna, gpt-5.6-terra, gpt-5.6-sol (openai), grok-4.5 (xai), lfm2.5-8b-a1b (together_ai); native alias `gpt-5.6`; `grok-4.5-latest`/`grok-build-latest` resolve dynamically from xAI provider metadata - `reasoning_effort` on grok-4.5: low/medium/high native, higher tiers clamped - gpt-5.6 function tools: `reasoning_effort: "none"` injected when no effort is set (OpenAI rejects tools at any other effort) - Accepted parameters verified for ornith-1.0-35b, gemma-4-12b-it, and minimax-m3 on deepinfra/siliconflow - GLM-5.2 capability and parameter support for together_ai and siliconflow - Fireworks `context_length=0` fallback fixed for closed-source models (qwen-3.7-plus, glm-5.2) ### Parameters & Request Handling - Round-tripped OpenAI input items: Preserve custom item and content types (e.g. Codex CLI's `custom_tool_call` items and `refusal` parts) — multi-turn Codex sessions on GPT models work end-to-end - Unknown hosted tool types accepted: Requests containing tool types like `tool_search` and `custom` no longer rejected ### Error Handling - `input_requires_responses_endpoint` error code ### SDKs - Python SDK [`auriko`](https://pypi.org/project/auriko/) 1.1.0 - TypeScript SDK [`@auriko/sdk`](https://www.npmjs.com/package/@auriko/sdk) 1.1.0 --- --- ## Page: Changelog > Section: 1.0.1 — 2026-06-27 ### API - API key identity restructure: `GET /v1/me` returns full credential introspection — `credential_type`, `status`, `profile`, `key_id`, `key_prefix`, `key_name`, nested `workspace`, `scopes`, structured `rate_limits`, `expires_at`, `created_at`. The `object`, `user_id`, and `tier` fields are removed - API key list default: `GET /v1/workspaces/{workspace_id}/api-keys` excludes revoked keys by default; pass `include_revoked=true` to include them - Credit balance fields removed: `current_tier`, `platform_fee_rate`, `tier_volume_usd`, `next_tier_threshold_usd` from the credit balance API response - `/api/v1/pricing/tiers` endpoint removed - Management API: Programmatic workspace management via API key — BYOK key lifecycle (`byok:read`/`byok:write`), BYOK discovery, workspace settings (`workspace:write`), routing defaults (`routing:read`/`routing:write`), billing and subscription status (`billing:read`), member listing (`members:read`), invite lifecycle (`members:read`/`invites:write`), audit events (`audit:read`), budget management (`budgets:write`), API key lifecycle (`keys:write`). All write operations return `202` - Analytics dashboard API: Summary, performance trend, routing evidence, paginated request log, and CSV export with truncation headers (`X-Export-Row-Count`, `X-Export-Row-Limit`, `X-Export-Truncated`) - `/v1/models` contract: `endpoint` query parameter and `supported_endpoints`/`context_window` response fields - Model directory aliases: Each entry in `/v1/directory/models` includes an `aliases` list - Budget headers on `/v1/me`: `X-Budget-*` headers when budgets are configured - Response API `phase` field: `output[].phase` (`"commentary" | "final_answer"`) typed in both SDKs and the API reference schema - Usage dashboard access: All workspace members can view usage analytics ### Error Handling - `response_api_only` error code: Response-API-only models return this dedicated code with an actionable `suggestion` field - `client_disconnected` error code: Error code for client-initiated disconnections - `idempotency_replay_unavailable`: Idempotent endpoints return 409 when replay data is unavailable - Model-not-found suggestions: `model_not_found` errors include an optional `suggestion` field with similar model names - `X-Rate-Limit-Source` header: Rate limit errors distinguish between provider and Auriko limits (`provider` or `routing`) - Double `/v1/` path detection: Requests to `/v1/v1/*` return 400 naming the duplicate prefix - `Retry-After` minimum: Rate-limited responses return `Retry-After: 1` minimum ### Parameters & Request Handling - Response API parameters: `frequency_penalty`, `presence_penalty`, `max_tool_calls` on `responses.create()` - Response API echo-back fields: `temperature`, `top_p`, `frequency_penalty`, `presence_penalty`, `top_logprobs`, `truncation` reflect request values in responses - `max_tool_calls` clarification: Limits built-in tool calls only (e.g., `web_search`, `code_interpreter`), not user-defined function tools - `reasoning.effort` on Claude: Enables thinking and produces reasoning output through the Response API. Values exceeding a model's range are clamped with an `unsupported_parameter` warning - Forced `tool_choice` with reasoning on Anthropic: `tool_choice` (`any`/`tool`) coexists with `reasoning_effort`; incompatible parameters are dropped with a warning ### Routing & Providers - Per-model rate limit isolation: Rate limits track each model independently; custom per-key `rate_limit_rpm` values are enforced - Provider error messages: Include the provider name in `error.message` with status-appropriate messages. Provider 403s are retryable - Timeout reporting: Stream first-byte timeouts report `reason: "timeout"` with `provider_timeout` warnings - `tool_choice="required"` on GLM models: Targets capable providers; models with no capable provider return `tool_choice_required_not_supported` ### Model Compatibility - Claude Opus 4.8: Tools, structured output, vision, web search, code execution, prompt caching (1,024-token minimum). Adaptive thinking only - Claude Fable 5: Adaptive thinking, 1M context, 128K output. Tools, structured output, vision, web search, code execution, computer use, prompt caching (512-token minimum) - Gemini 3 Pro Image and Gemini 3.1 Flash Image: Image generation with text-and-image I/O, tool calling, web search, reasoning - MiniMax M3: Tiered pricing, tool calling, reasoning, vision, prompt caching. Additional provider via SiliconFlow - Nemotron 3 Ultra 550B: Reasoning, tool calling, JSON mode via Together AI. Additional provider via DeepInfra - Nex N2 Pro: 262K context, adaptive thinking, vision via SiliconFlow (free tier) - Undated Claude model aliases resolve to dated counterparts - 12 responses-only models (o1-pro, o3-pro, gpt-5/5.2/5.4/5.5-pro, codex family, deep-research) discoverable in `/v1/directory/models` - Qwen 3.7 Max via DeepInfra; MiniMax M2.7 via DeepInfra - Structured output works with array-typed schemas (e.g. `["string", "null"]`) on Gemini - SiliconFlow Qwen 3 VL 32B/8B does not support `response_format: json_schema` - `reasoning_effort` with low `max_tokens` on Gemini 3.x returns `finish_reason: "length"` - Response API streaming `usage` includes token counts for all providers ### SDKs - Python SDK [`auriko`](https://pypi.org/project/auriko/) 1.0.1 - TypeScript SDK [`@auriko/sdk`](https://www.npmjs.com/package/@auriko/sdk) 1.0.1 - Vercel AI SDK provider [`@auriko/ai-sdk-provider`](https://www.npmjs.com/package/@auriko/ai-sdk-provider) 1.0.1 ### Documentation - Response API guides: overview, streaming, tool calling, structured output, reasoning, and routing --- --- ## Page: Changelog > Section: 0.3.0 — 2026-05-24 ### API - Response API: `POST /v1/responses` creates model responses using the OpenAI Response API format, with streaming, tool calling, reasoning summaries, and multi-model routing - Gemini image generation: Image-capable Gemini models return generated images inline as `images[]` in chat completions, with `image_tokens` in usage - Gemini 3.5 Flash: Routing to `gemini-3.5-flash` on Google AI Studio with function calling, structured output, streaming, vision, reasoning, prompt caching, and code execution ### Routing & Providers - Streaming-only provider routing: Providers that only support streaming are excluded from non-streaming requests. Requests targeting a specific provider return `non_streaming_not_supported` with guidance to set `stream: true` - kimi-k2.5, kimi-k2.6, gpt-oss, and glm-5.1 support the Response API - `tool_choice: "required"` downgrades to `"auto"` on reasoning models with an `unsupported_parameter` warning. Models with no capable provider return `tool_choice_required_not_supported` - `tool_choice: "none"` is honored on all endpoints ### Parameters & Request Handling - Default timeouts: 120s streaming first-byte, 5-minute non-streaming total, 18-minute non-streaming deadline. User-set `timeout_ms` / `deadline_ms` override defaults - Unsupported parameter warnings: Human-readable format (e.g., `'metadata' is not supported.`) - `system` as top-level parameter: Returns a structured warning directing to the `messages` array ### Model Compatibility - `claude-opus-4-7` sampling parameters: `temperature`, `top_p`, and `top_k` are accepted with an `unsupported_parameter` warning on all endpoints - Anthropic reasoning with low `max_tokens`: Requests with `reasoning_effort` and low `max_tokens` return successful responses with an `unsupported_parameter` warning - Response API returns structured reasoning output for DeepSeek R1 0528, Qwen 3 30B A3B, and Qwen 3 32B ### SDKs - Python SDK [`auriko`](https://pypi.org/project/auriko/) 0.3.0 - TypeScript SDK [`@auriko/sdk`](https://www.npmjs.com/package/@auriko/sdk) 0.3.0 --- --- ## Page: Changelog > Section: 0.2.0 — 2026-05-18 ### Routing & Providers - Priority tier routing: `gateway.routing.tier: "priority"` enables faster inference (2.5x speed, 6x cost). Premium-tier offerings not included by default - Requests with `tool_choice="required"` target providers that honor the constraint. Returns `tool_choice_required_not_supported` if no capable provider is available - SiliconFlow-hosted models are supported, including 13 exclusive models and reasoning variants (DeepSeek-R1, Qwen3 thinking) - `qwen-3.6-plus` available through additional providers - 9 Qwen 3 models are available through alternative providers ### Parameters & Request Handling - Default timeouts support reasoning models with longer first-token times. `deadline_ms` is opt-in only ### Model Compatibility - Anthropic `response_format` supports type arrays (e.g., `type: ["string", "null"]`) with nullable enum and object handling - Zero-parameter tools validate on Anthropic and Google models - `reasoning_effort` coexists with `temperature`, `top_p`, and `top_k` on Claude models. Incompatible sampling parameters are omitted with a warning - `reasoning_effort` mapping across providers: - Claude: `"xhigh"` preserved on Opus 4.7, clamped to `"high"` on Opus 4.5, mapped to `"max"` on other models - Gemini 3.x: `"xhigh"` and `"max"` map to `"high"` - OpenAI and xAI: `"max"` maps to each model's ceiling - All mappings include an `unsupported_parameter` warning - `reasoning_effort` with extreme `max_tokens` on Gemini clamps to each model's accepted range - DeepInfra Gemma 3, Qwen3 VL, and MiMo return tool call responses, streaming and non-streaming - MiniMax models return structured reasoning output - `output_tokens` in Anthropic-format responses includes reasoning tokens - `llama-3.1-8b-instruct-turbo` returns a capability error for unsupported structured output ### Error Handling - Streaming errors return structured messages with `message`, `type`, `code`, and `provider` fields - Multi-provider fallback errors include per-provider failure details ### SDKs - Python SDK [`auriko`](https://pypi.org/project/auriko/) 0.2.0 - TypeScript SDK [`@auriko/sdk`](https://www.npmjs.com/package/@auriko/sdk) 0.2.0 - Vercel AI SDK provider [`@auriko/ai-sdk-provider`](https://www.npmjs.com/package/@auriko/ai-sdk-provider) 0.2.0 ### Integrations - [Kilo Code](/integrations/kilo-code) integration guide --- --- ## Page: Changelog > Section: 0.1.1 — 2026-05-12 ### Parameters & Request Handling - User-configurable timeouts via `gateway.routing.timeout_ms` (per-attempt) and `gateway.routing.deadline_ms` (request deadline) - Streaming uses `timeout_ms` as time-to-first-byte - Defaults: 10s streaming first-byte / 120s streaming deadline; non-streaming 120s / 180s - `reasoning_effort` accepts `"xhigh"` and `"max"` levels. Anthropic models use adaptive thinking with automatic effort-to-budget translation - Timeout errors include actionable guidance naming the configurable parameters - `provider_timeout` warning emitted when fallback succeeds after a timeout - `timeout_ms` and `deadline_ms` apply to all execution paths including `allow_fallbacks: false` ### Error Handling - Error codes `unknown_parameter`, `capability_mismatch`, `routing_constraint_unsatisfiable`, `tokens_per_min_exceeded`, `concurrent_requests_exceeded`, and `routing_failed` — capability-specific codes listed below - Inference validation returns specific error codes — `missing_required_parameter`, `invalid_parameter_value`, `unknown_field`, `operation_not_allowed` — for precise programmatic error handling - `invalid_request` is reserved for malformed request bodies. HTTP status codes unchanged - Platform operations return `state_precondition_failed`, `duplicate_resource`, and `operation_not_allowed`. 405 responses return `method_not_allowed` - New platform codes: `field_immutable`, `invalid_recovery_code`, `mfa_verification_failed` ### Model Compatibility - Gemini tool calls return `finish_reason: tool_calls` for client-side tool execution - Gemini accepts non-object tool results (numbers, booleans, arrays) - `reasoning_effort` works on Gemma-4, qwen-3-vl-thinking, and gpt-5.4+ (when combined with tools) - Explicit `thinking.budget_tokens` requests (e.g. from Claude Code) work on models that support them ### SDKs - Python SDK [`auriko`](https://pypi.org/project/auriko/) 0.1.1 - TypeScript SDK [`@auriko/sdk`](https://www.npmjs.com/package/@auriko/sdk) 0.1.1 - Response models include `service_tier`, `annotations`, and prediction token fields ### Integrations - [OpenCode](/integrations/opencode) models.dev definitions (15 curated models) - [Claude Code](/integrations/claude-code) integration guide --- --- ## Page: Changelog > Section: 0.1.0 — 2026-05-04 ### API - OpenAI-compatible chat completions with multi-provider routing and automatic failover - Anthropic Messages API compatibility — `POST /v1/messages` and token counting - Streaming and non-streaming responses across all supported providers - Model directory with pricing, capability metadata, and provider health status - Workspace billing and credit balance (Preview) - Structured reasoning support with cryptographic signatures for multi-turn round-trip - Structured error responses with machine-readable `code` and `type` fields for programmatic error handling ### SDKs - Python SDK [`auriko`](https://pypi.org/project/auriko/) 0.1.0 — with OpenAI Agents SDK compatibility via `auriko[openai-compat]` - TypeScript SDK [`@auriko/sdk`](https://www.npmjs.com/package/@auriko/sdk) 0.1.0 - Vercel AI SDK provider [`@auriko/ai-sdk-provider`](https://www.npmjs.com/package/@auriko/ai-sdk-provider) 0.1.0 ### Frameworks - [LangChain](/frameworks/langchain), [LlamaIndex](/frameworks/llamaindex), [CrewAI](/frameworks/crewai), [Google ADK](/frameworks/google-adk), [OpenAI Agents SDK](/frameworks/openai-agents-sdk) - [Claude Agent SDK](/frameworks/claude-agent-sdk) via `ANTHROPIC_BASE_URL` - [Vercel AI SDK](/frameworks/vercel-ai-sdk) ### Models - GPT-4o, Claude 3.5 Sonnet, Gemini 1.5 Pro, Llama 3.1, Mistral Large, DeepSeek V4, and more — see [Models](https://www.auriko.ai/models) for the complete list --- === # Auriko Integrations ## Page: Claude Code Auriko connects to Claude Code through environment variables, giving you access to multiple models through a single API key. --- ## Page: Claude Code > Section: Prerequisites - [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed and working - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) --- ## Page: Claude Code > Section: Clear existing Anthropic login If you've previously logged in to Claude Code with an Anthropic account, cached credentials take precedence over environment variables. Clear them first: ```bash claude auth logout ``` If you haven't logged in with Anthropic directly, skip this step. --- ## Page: Claude Code > Section: Set your API key Add these three lines to your shell profile (`~/.zshrc` on macOS, `~/.bashrc` on Linux): ```bash export ANTHROPIC_BASE_URL="https://api.auriko.ai" export ANTHROPIC_AUTH_TOKEN="ak_live_..." # your Auriko API key export ANTHROPIC_API_KEY="" ``` `ANTHROPIC_API_KEY=""` must be an empty string. If it contains any value, Claude Code uses it directly against Anthropic, bypassing Auriko. The base URL must not include `/v1`. Claude Code appends `/v1/messages` itself. Reload your shell after saving: ```bash source ~/.zshrc # or: source ~/.bashrc ``` Or open a new terminal. To keep `claude` connected to your Anthropic account, create a wrapper command instead of modifying your shell profile. Add your Auriko API key to your shell profile (`~/.zshrc` or `~/.bashrc`): ```bash export AURIKO_API_KEY="ak_live_..." ``` Create the wrapper script: ```bash mkdir -p ~/.local/bin cat > ~/.local/bin/claude-auriko << 'EOF' #!/usr/bin/env bash export ANTHROPIC_BASE_URL="https://api.auriko.ai" export ANTHROPIC_AUTH_TOKEN="${AURIKO_API_KEY}" export ANTHROPIC_API_KEY="" exec claude "$@" EOF chmod +x ~/.local/bin/claude-auriko ``` Add `~/.local/bin` to your `PATH` if it isn't already: ```bash export PATH="$HOME/.local/bin:$PATH" ``` Reload your shell, then verify: ```bash claude-auriko -p "Say exactly: setup-ok" --model claude-haiku-4-5-20251001 ``` Use `claude-auriko` for Auriko sessions and `claude` for your Anthropic subscription. You don't need to clear your Anthropic login. **Model defaults are shared.** The `/model` command saves your choice to `~/.claude/settings.json`, which both `claude` and `claude-auriko` read. Setting `/model deepseek-v4-pro` in an Auriko session changes the default for your Anthropic subscription too. To avoid this, set a default model in the wrapper script so Auriko sessions start with the right model without touching `settings.json`: ```bash #!/usr/bin/env bash export ANTHROPIC_BASE_URL="https://api.auriko.ai" export ANTHROPIC_AUTH_TOKEN="${AURIKO_API_KEY}" export ANTHROPIC_API_KEY="" # Default to deepseek-v4-pro unless --model is passed explicitly has_model=false for arg in "$@"; do [[ "$arg" == "--model" ]] && has_model=true done if $has_model; then exec claude "$@" else exec claude --model deepseek-v4-pro "$@" fi ``` The `--model` flag is session-only and does not persist. You can still switch models mid-session with `/model`, but be aware it saves globally. --- ## Page: Claude Code > Section: Verify ```bash claude -p "Say exactly: setup-ok" --model claude-haiku-4-5-20251001 ``` Claude Code includes a system prompt on every request. The first request in a session costs more than follow-ups due to [prompt caching](/guides/prompt-caching). Factor this into your cost estimates. --- ## Page: Claude Code > Section: Use different models Pass any Auriko model ID with the `--model` flag: ```bash claude --model deepseek-v4-flash claude --model gemini-2.5-flash claude --model grok-4.3 ``` Model IDs must be exact. Claude Code requires reasoning support from every model. Models that don't support reasoning return a `400` error. Browse per-model capabilities in the [directory API](https://api.auriko.ai/v1/directory/models). These are some of the models available through Auriko: | Model | Author | Context | |-------|--------|---------| | `claude-sonnet-4-6` | Anthropic | 1M | | `claude-opus-4-6` | Anthropic | 1M | | `claude-opus-4-7` | Anthropic | 1M | | `deepseek-v4-flash` | DeepSeek | 1M | | `deepseek-v4-pro` | DeepSeek | 1M | | `gemini-2.5-flash` | Google | 1M | | `gemini-2.5-pro` | Google | 1M | | `gemini-3.1-pro-preview` | Google | 1M | | `glm-5.1` | Z.AI | 200K | | `grok-4.3` | xAI | 1M | | `kimi-k2.5` | Moonshot | 262K | | `kimi-k2.6` | Moonshot | 262K | | `minimax-m2-7` | MiniMax | 205K | | `minimax-m2-7-highspeed` | MiniMax | 205K | | `qwen-3.6-plus` | Alibaba | 1M | To list available models: ```bash curl -s -H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \ https://api.auriko.ai/v1/models | jq '.data[].id' ``` The `/model` picker in interactive sessions lists only Claude tier names (Opus, Sonnet, and Haiku). To switch to a non-Claude model mid-session, type the full ID: `/model deepseek-v4-flash`. You can also remap the tier aliases (next section) so picker entries route through Auriko. --- ## Page: Claude Code > Section: Override model tier aliases Claude Code uses three model tiers (sonnet, opus, haiku). You can override which model each tier maps to: ```bash export ANTHROPIC_DEFAULT_SONNET_MODEL="deepseek-v4-flash" export ANTHROPIC_DEFAULT_OPUS_MODEL="claude-opus-4-7" export ANTHROPIC_DEFAULT_HAIKU_MODEL="claude-haiku-4-5-20251001" ``` Add these to your shell profile alongside the other environment variables. If you're using the `claude-auriko` wrapper, add these exports to the wrapper script instead. --- ## Page: Claude Code > Section: Switch back to direct Anthropic Remove or comment out the three environment variables from your shell profile, then reload and re-authenticate: ```bash source ~/.zshrc claude auth login ``` --- ## Page: Claude Code > Section: Troubleshoot | Symptom | Cause | Fix | |---------|-------|-----| | "model may not exist or you may not have access" | Model ID isn't exact (e.g., `claude-haiku-4-5` instead of `claude-haiku-4-5-20251001`) | Use the full model ID from `GET /v1/models` | | Requests go to Anthropic directly, not Auriko | `ANTHROPIC_API_KEY` contains a value | Set `ANTHROPIC_API_KEY=""` (empty string, not unset) | | "Invalid API Key" or auth errors | Cached Anthropic OAuth credentials | Run `claude auth logout`, then verify env vars are set | | Requests hang or timeout | `ANTHROPIC_BASE_URL` includes `/v1` | Use `https://api.auriko.ai` only | | "does not support reasoning/extended thinking" | Claude Code requires reasoning support but this model doesn't have it | Use a reasoning-capable model (see "Use different models" above) | | `apiKeySource: none` in session events | Claude Code doesn't classify `ANTHROPIC_AUTH_TOKEN` as a key source | Expected behavior. Requests authenticate correctly | | `/model` in `claude-auriko` changes the default for `claude` too | Both commands share `~/.claude/settings.json` and `/model` saves globally | Use `--model` in the wrapper script (see wrapper section above) | --- ## Page: Codex Auriko connects to Codex through two environment variables, routing your completions through Auriko instead of OpenAI. --- ## Page: Codex > Section: Prerequisites - [Codex CLI](https://github.com/openai/codex) installed - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) --- ## Page: Codex > Section: Install Codex Run the official install script: ```bash curl -fsSL https://chatgpt.com/codex/install.sh | sh ``` Or install via npm: ```bash npm install -g @openai/codex ``` Or via Homebrew (macOS): ```bash brew install --cask codex ``` Open a new terminal after installing, then confirm Codex is available: ```bash codex --help ``` The install script writes PATH to `~/.bashrc`, which non-interactive shells don't source. In CI, GitHub Actions, or agent-driven sessions, add this before invoking `codex`: ```bash export PATH="$HOME/.codex/bin:$PATH" ``` --- ## Page: Codex > Section: Set your API key Add these two lines to your shell profile (`~/.zshrc` on macOS, `~/.bashrc` on Linux): ```bash export OPENAI_API_KEY="ak_live_..." # your Auriko API key export OPENAI_BASE_URL="https://api.auriko.ai/v1" ``` The variable names are `OPENAI_*` because Codex reads OpenAI-standard environment variables. Your Auriko API key goes in `OPENAI_API_KEY`. Your key starts with `ak_live_` (production) or `ak_test_` (testing). The base URL must include `/v1`. Codex appends `/responses` directly. If you've logged in to Codex with a ChatGPT account, these environment variables override the ChatGPT login. Codex routes completions through `OPENAI_BASE_URL` instead of `chatgpt.com`. Reload your shell after saving: ```bash source ~/.zshrc # or: source ~/.bashrc ``` Or open a new terminal. To keep your default `codex` connected to ChatGPT, create a wrapper command instead of modifying your shell profile. Add your Auriko API key to your shell profile (`~/.zshrc` or `~/.bashrc`): ```bash export AURIKO_API_KEY="ak_live_..." ``` Create the wrapper script: ```bash mkdir -p ~/.local/bin cat > ~/.local/bin/codex-auriko << 'EOF' #!/usr/bin/env bash export OPENAI_API_KEY="${AURIKO_API_KEY}" export OPENAI_BASE_URL="https://api.auriko.ai/v1" exec codex "$@" EOF chmod +x ~/.local/bin/codex-auriko ``` Add `~/.local/bin` to your `PATH` if it isn't already: ```bash export PATH="$HOME/.local/bin:$PATH" ``` Reload your shell, then verify: ```bash codex-auriko exec -m gpt-5.4-mini "Say exactly: setup-ok" ``` Use `codex-auriko` for Auriko sessions and `codex` for your ChatGPT subscription. --- ## Page: Codex > Section: Verify Run a test completion: ```bash codex exec -m gpt-5.4-mini "Say exactly: setup-ok" ``` Codex prints sandbox setup output on first run. The model response follows. Expect two cosmetic warnings: - `Model metadata for '...' not found`: metadata isn't available for models served through custom base URLs. - `missing field 'models'`: the response uses the standard `data` field instead of a `models` field. --- ## Page: Codex > Section: Use different models Pass any model ID with the `-m` flag: ```bash codex -m gpt-5.5 "Summarize this repo" codex -m gpt-5.4 "Review my code" ``` To switch models mid-session, type `/model `. Codex uses the Response API with hosted tools, which only GPT models support. Non-GPT models (Claude, DeepSeek, Gemini) return a `hosted_tool_not_supported` error. These are the models available through Auriko that work with Codex: | Model | Author | Context | |-------|--------|---------| | `gpt-5.5` | OpenAI | 1M | | `gpt-5.4` | OpenAI | 1M | | `gpt-5.4-mini` | OpenAI | 400K | | `gpt-5.4-nano` | OpenAI | 400K | | `gpt-5-codex` | OpenAI | 400K | | `o4-mini` | OpenAI | 200K | | `o3` | OpenAI | 200K | | `gpt-4o` | OpenAI | 128K | `gpt-5.4-nano` works for basic tasks but doesn't support all Codex tools (e.g., `tool_search`). Complex multi-tool sessions may require a larger model. To list all Auriko models (only GPT models with Response API support work with Codex): ```bash curl -s -H "Authorization: Bearer $OPENAI_API_KEY" \ https://api.auriko.ai/v1/models | jq '.data[].id' ``` --- ## Page: Codex > Section: Control routing Configure routing in the [Auriko dashboard](https://auriko.ai/dashboard). See [routing options](/guides/routing-options) for details. --- ## Page: Codex > Section: Uninstall Remove `OPENAI_API_KEY` and `OPENAI_BASE_URL` from your shell profile (`~/.zshrc` or `~/.bashrc`). If you created a `codex-auriko` wrapper, delete `~/.local/bin/codex-auriko`. --- ## Page: Codex > Section: Troubleshoot | Symptom | Cause | Fix | |---------|-------|-----| | Completions go to ChatGPT, not Auriko | `OPENAI_API_KEY` not set; ChatGPT auth takes over | Export `OPENAI_API_KEY` with your Auriko key | | `hosted_tool_not_supported` | Non-GPT model used | Use a GPT model (see model table above) | | 404 on requests | `OPENAI_BASE_URL` missing `/v1` | Use exactly `https://api.auriko.ai/v1` | | "Model metadata not found" warning | Built-in metadata not available for custom-provider models | Cosmetic. No action needed | | "missing field `models`" warning | Auriko's model list format differs slightly from OpenAI's | Cosmetic. No action needed | | 401 or "API key is invalid" | Wrong key value or expired key | Run `echo $OPENAI_API_KEY` to check. Verify at `curl -H "Authorization: Bearer $OPENAI_API_KEY" https://api.auriko.ai/v1/me` | | `codex: command not found` | Install script PATH not loaded | Run `export PATH="$HOME/.codex/bin:$PATH"` or open a new terminal | --- ## Page: Hermes Agent Auriko connects to Hermes Agent as a provider plugin. You access multiple models through a single API key. Auriko is a plugin provider, not built-in. --- ## Page: Hermes Agent > Section: Prerequisites - [Hermes Agent](https://hermes-agent.nousresearch.com) 0.14.0+ - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) --- ## Page: Hermes Agent > Section: Install Hermes Agent Hermes Agent requires Python 3.11+. Run the official installer: ```bash curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash ``` This installs to `~/.hermes/` with an isolated Python venv and adds `hermes` to your PATH. Open a new terminal, then confirm the installation: ```bash hermes --version ``` You need version 0.14.0 or later. --- ## Page: Hermes Agent > Section: Install the plugin Run the install script: ```bash curl -fsSL https://raw.githubusercontent.com/auriko-ai/integrations/main/hermes-agent/install.sh | bash ``` This copies two files (`__init__.py` and `plugin.yaml`) to `~/.hermes/plugins/model-providers/auriko/`. Download both files from the [integrations repo](https://github.com/auriko-ai/integrations/tree/main/hermes-agent/plugin) into the plugin directory: ```bash mkdir -p ~/.hermes/plugins/model-providers/auriko curl -fsSL https://raw.githubusercontent.com/auriko-ai/integrations/main/hermes-agent/plugin/__init__.py \ -o ~/.hermes/plugins/model-providers/auriko/__init__.py curl -fsSL https://raw.githubusercontent.com/auriko-ai/integrations/main/hermes-agent/plugin/plugin.yaml \ -o ~/.hermes/plugins/model-providers/auriko/plugin.yaml ``` --- ## Page: Hermes Agent > Section: Set your API key The plugin install script adds an empty placeholder to `~/.hermes/.env`. Open the file and set your key: ``` AURIKO_API_KEY=ak_live_... ``` Your key starts with `ak_live_` (production) or `ak_test_` (testing). Hermes also reads `AURIKO_API_KEY` from your shell environment (`~/.zshrc` or `~/.bashrc`). --- ## Page: Hermes Agent > Section: Configure Hermes Set Auriko as the default provider: ```bash hermes config set model.provider auriko hermes config set model.default claude-haiku-4-5-20251001 hermes config set model.base_url https://api.auriko.ai/v1 hermes config set providers.auriko.base_url https://api.auriko.ai/v1 hermes config set providers.auriko.key_env AURIKO_API_KEY ``` Use `auriko` as the provider value, not `custom`. If you have an interactive terminal: ```bash hermes model ``` Select **Auriko** from the provider list, confirm your API key, and pick a default model. With Hermes 0.14.x, the model list may be empty for plugin providers. Use the config commands above instead. --- ## Page: Hermes Agent > Section: Verify Run a quick test to confirm the connection: ```bash hermes chat -q "Say exactly: setup-ok" -Q --model claude-haiku-4-5-20251001 ``` --- ## Page: Hermes Agent > Section: Use different models Pass any Auriko model ID: ```bash hermes chat --model deepseek-v3.2 hermes chat --model gemini-2.5-flash hermes chat --model claude-opus-4-7 ``` These are some of the models available through Auriko: | Model | Author | Context | |-------|--------|---------| | `claude-sonnet-4-6` | Anthropic | 1M | | `claude-opus-4-6` | Anthropic | 1M | | `claude-opus-4-7` | Anthropic | 1M | | `deepseek-v4-flash` | DeepSeek | 1M | | `deepseek-v4-pro` | DeepSeek | 1M | | `gemini-2.5-flash` | Google | 1M | | `gemini-2.5-pro` | Google | 1M | | `gemini-3.1-pro-preview` | Google | 1M | | `glm-5.1` | Z.AI | 200K | | `grok-4.3` | xAI | 1M | | `kimi-k2.5` | Moonshot | 262K | | `minimax-m2-7` | MiniMax | 205K | | `minimax-m2-7-highspeed` | MiniMax | 205K | | `qwen-3.6-plus` | Alibaba | 1M | To list available models: ```bash curl -s -H "Authorization: Bearer $AURIKO_API_KEY" \ https://api.auriko.ai/v1/models | jq '.data[].id' ``` Auriko model IDs use bare names with hyphens: | Wrong (404) | Correct | |---|---| | `anthropic/claude-opus-4.6` | `claude-opus-4-6` | | `openai/gpt-4o` | `gpt-4o-2024-11-20` | | `deepseek/deepseek-chat` | `deepseek-v3.2` | Hermes reads available models from `/v1/models`. --- ## Page: Hermes Agent > Section: Control routing Configure routing in the [Auriko dashboard](https://auriko.ai/dashboard). See [routing options](/guides/routing-options) for details. --- ## Page: Hermes Agent > Section: Uninstall Remove the plugin directory: ```bash rm -rf ~/.hermes/plugins/model-providers/auriko/ ``` Open `~/.hermes/config.yaml` and remove the `auriko` block under `providers:`. You can also run `hermes config edit` to open the file in your editor. Then run `hermes model` to select a different provider. If you no longer need the API key, remove `AURIKO_API_KEY` from `~/.hermes/.env` and your shell profile. --- ## Page: Hermes Agent > Section: Troubleshoot | Symptom | Cause | Fix | |---------|-------|-----| | 401 / Missing Authentication | `AURIKO_API_KEY` not set or wrong | Add your key to `~/.hermes/.env` or export it in your shell profile | | Requests go to wrong provider | Stale `model.base_url` from a previous provider | Run `hermes config set model.base_url https://api.auriko.ai/v1` | | Auriko not in `hermes model` list | Plugin not installed or wrong path | Re-run the install script. Verify `~/.hermes/plugins/model-providers/auriko/__init__.py` exists. | | Model not found (404) | Wrong model ID format | Use bare Auriko IDs (`claude-opus-4-6`, not `anthropic/claude-opus-4.6`) | | `No auxiliary LLM provider configured` | Hermes auxiliary model not set | Safe to ignore | | `Unknown provider 'auriko'` | Provider not registered in `config.yaml` | Re-run the install script. If you installed manually, run `hermes config set providers.auriko.base_url https://api.auriko.ai/v1` and `hermes config set providers.auriko.key_env AURIKO_API_KEY` | | Timeout fetching models | API unreachable | Check your connection. Hermes falls back to a built-in model list. | | Model picker shows 0 Auriko models | Model list not populated for plugin providers | Use `hermes config set model.default ` to set a model directly | | qwen-3.6-plus compresses context too early | Context length not detected correctly for some large-context models | Run `hermes config set model.context_length 1000000`. Remove this override when switching to a different model. | --- ## Page: OpenClaw Auriko connects to OpenClaw as a custom model provider, giving you access to 15 models through a single API key. --- ## Page: OpenClaw > Section: Prerequisites - Node.js 22.16+ - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) --- ## Page: OpenClaw > Section: Install OpenClaw Install via npm: ```bash npm install -g openclaw@latest ``` Confirm the version: ```bash openclaw --version ``` You need `v2026.1.30` or later. Set up the workspace and default agent: ```bash openclaw onboard --non-interactive --accept-risk --auth-choice skip --skip-health ``` This creates `~/.openclaw/openclaw.json` with a gateway token, a default workspace, and an agent named `main`. It doesn't install a background daemon. Daemon setup is optional and covered below. The flags: - `--non-interactive` skips the wizard's TUI prompts. Without it, the security disclaimer defaults to **No** and the command silently exits without configuring anything. - `--accept-risk` acknowledges OpenClaw's security disclaimer. Required by `--non-interactive`. - `--auth-choice skip` skips upstream-provider onboarding. You configure Auriko in the next step. - `--skip-health` skips the gateway-reachability check at the end. Without it, onboard exits **1** on Linux and Codespaces because the health check fails. Setup itself succeeds. This breaks `set -e` scripts. --- ## Page: OpenClaw > Section: Set your API key Export your API key: ```bash export AURIKO_API_KEY="ak_live_..." ``` Your key starts with `ak_live_` (production) or `ak_test_` (testing). To persist across terminal sessions, add the same line to `~/.zshrc` or `~/.bashrc`, then reload: ```bash source ~/.zshrc # or: source ~/.bashrc ``` Or open a new terminal. --- ## Page: OpenClaw > Section: Add Auriko as a provider `openclaw onboard` created `~/.openclaw/openclaw.json`. Merge the two top-level keys below into that file. Don't overwrite it. The wizard-generated `gateway`, `session`, `tools`, `wizard`, and `meta` blocks must stay. Inside `agents.defaults`, keep the existing `workspace` key and add `model` and `models` alongside it. ```json { "models": { "mode": "merge", "providers": { "auriko": { "baseUrl": "https://api.auriko.ai/v1", "apiKey": "${AURIKO_API_KEY}", "api": "openai-completions", "agentRuntime": { "id": "pi" }, "models": [ { "id": "claude-sonnet-4-6", "name": "Claude Sonnet 4.6", "contextWindow": 1000000, "maxTokens": 64000 }, { "id": "claude-opus-4-6", "name": "Claude Opus 4.6", "contextWindow": 1000000, "maxTokens": 128000 }, { "id": "claude-opus-4-7", "name": "Claude Opus 4.7", "contextWindow": 1000000, "maxTokens": 128000 }, { "id": "deepseek-v4-flash", "name": "DeepSeek V4 Flash", "contextWindow": 1000000, "maxTokens": 8192 }, { "id": "deepseek-v4-pro", "name": "DeepSeek V4 Pro", "contextWindow": 1000000, "maxTokens": 8192 }, { "id": "gemini-2.5-flash", "name": "Gemini 2.5 Flash", "contextWindow": 1000000, "maxTokens": 65536 }, { "id": "gemini-2.5-pro", "name": "Gemini 2.5 Pro", "contextWindow": 1000000, "maxTokens": 65536 }, { "id": "gemini-3.1-pro-preview", "name": "Gemini 3.1 Pro Preview", "contextWindow": 1000000, "maxTokens": 65536 }, { "id": "glm-5.1", "name": "GLM-5.1", "contextWindow": 200000, "maxTokens": 8192 }, { "id": "grok-4.3", "name": "Grok 4.3", "contextWindow": 1000000, "maxTokens": 8192 }, { "id": "kimi-k2.5", "name": "Kimi K2.5", "contextWindow": 262144, "maxTokens": 8192 }, { "id": "kimi-k2.6", "name": "Kimi K2.6", "contextWindow": 262144, "maxTokens": 8192 }, { "id": "minimax-m2-7", "name": "MiniMax M2.7", "contextWindow": 205000, "maxTokens": 8192 }, { "id": "minimax-m2-7-highspeed", "name": "MiniMax M2.7 Highspeed", "contextWindow": 205000, "maxTokens": 8192 }, { "id": "qwen-3.6-plus", "name": "Qwen 3.6 Plus", "contextWindow": 1000000, "maxTokens": 8192 } ] } } }, "agents": { "defaults": { "model": { "primary": "auriko/claude-sonnet-4-6" }, "models": { "auriko/claude-sonnet-4-6": {}, "auriko/claude-opus-4-6": {}, "auriko/claude-opus-4-7": {}, "auriko/deepseek-v4-flash": {}, "auriko/deepseek-v4-pro": {}, "auriko/gemini-2.5-flash": {}, "auriko/gemini-2.5-pro": {}, "auriko/gemini-3.1-pro-preview": {}, "auriko/glm-5.1": {}, "auriko/grok-4.3": {}, "auriko/kimi-k2.5": {}, "auriko/kimi-k2.6": {}, "auriko/minimax-m2-7": {}, "auriko/minimax-m2-7-highspeed": {}, "auriko/qwen-3.6-plus": {} } } } } ``` `${AURIKO_API_KEY}` reads from your shell environment at runtime. Don't paste the literal key into the config. `"agentRuntime": { "id": "pi" }` selects the embedded agent harness. Without it, OpenClaw defaults to `codex`, which requires the OpenAI Codex CLI as a separate binary. Each model ID appears **twice**: bare (`claude-sonnet-4-6`) inside the provider's `models` array, prefixed (`auriko/claude-sonnet-4-6`) inside `agents.defaults.models`. OpenClaw doesn't route to a model unless it's in both places. `"mode": "merge"` keeps OpenClaw's bundled providers alongside Auriko. Without it, the bundled providers are replaced. Verify the config parsed and providers loaded: ```bash openclaw models list ``` You should see all `auriko/*` entries listed with `Auth: yes` and tag `configured`. --- ## Page: OpenClaw > Section: Verify Test that the connection works: ```bash openclaw agent --local --agent main --message "Say exactly: setup-ok" ``` Expected output: `setup-ok` (exit 0, 10-25 seconds typical). `--local` runs the agent in this process. Without it, OpenClaw tries the gateway daemon first. If no daemon is running, it prints a wall of `EMBEDDED FALLBACK` text that looks like a crash before the answer. `--agent main` targets the default agent from `openclaw onboard`. Without it: `Error: No target session selected.` On a cold provider, the first response takes 30-80 seconds (cold start plus large system prompt). Subsequent calls to the same model run 10-25 seconds. Raise the limit with `--timeout ` if it exceeds 90s. Some models echo the response (`setup-oksetup-ok`). See Troubleshoot for details. --- ## Page: OpenClaw > Section: Use different models You can override the model per-command or change the default. One-off override: ```bash openclaw agent --local --agent main --model auriko/deepseek-v4-flash --message "..." ``` Change the default: ```bash openclaw models set auriko/deepseek-v4-flash ``` Inside an interactive session, type `/model auriko/`. Models in the config above: | Model | Author | Context | |-------|--------|---------| | `claude-sonnet-4-6` | Anthropic | 1M | | `claude-opus-4-6` | Anthropic | 1M | | `claude-opus-4-7` | Anthropic | 1M | | `deepseek-v4-flash` | DeepSeek | 1M | | `deepseek-v4-pro` | DeepSeek | 1M | | `gemini-2.5-flash` | Google | 1M | | `gemini-2.5-pro` | Google | 1M | | `gemini-3.1-pro-preview` | Google | 1M | | `glm-5.1` | Z.AI | 200K | | `grok-4.3` | xAI | 1M | | `kimi-k2.5` | Moonshot | 262K | | `kimi-k2.6` | Moonshot | 262K | | `minimax-m2-7` | MiniMax | 205K | | `minimax-m2-7-highspeed` | MiniMax | 205K | | `qwen-3.6-plus` | Alibaba | 1M | To use a model not in this list, add its bare ID to `models.providers.auriko.models` and its prefixed ID to `agents.defaults.models`. To list every model Auriko exposes: ```bash curl -s -H "Authorization: Bearer $AURIKO_API_KEY" \ https://api.auriko.ai/v1/models | jq '.data[].id' ``` --- ## Page: OpenClaw > Section: Control routing Configure routing in the [Auriko dashboard](https://auriko.ai/dashboard). See [routing options](/guides/routing-options) for details. --- ## Page: OpenClaw > Section: Run as a background service (optional) `--local` mode runs the agent in your shell and covers interactive use. For a persistent gateway, install the daemon. **macOS** (LaunchAgent): ```bash openclaw onboard --install-daemon --non-interactive --accept-risk openclaw config set env.vars.AURIKO_API_KEY "$AURIKO_API_KEY" ``` The daemon doesn't inherit your shell environment. The `config set` step bakes the key into the daemon's config so `${AURIKO_API_KEY}` resolves at runtime. **Linux** (user-systemd required): ```bash loginctl enable-linger $(whoami) openclaw onboard --install-daemon --non-interactive --accept-risk openclaw config set env.vars.AURIKO_API_KEY "$AURIKO_API_KEY" ``` Without `enable-linger`, `--install-daemon` fails with `Systemd user services are unavailable`. **Containers without systemd** (GitHub Codespaces, plain Docker): Run the gateway in the foreground in a separate shell: ```bash openclaw gateway run ``` After any install path, confirm your Auriko models are still registered: ```bash openclaw models list ``` Re-running onboard with `--install-daemon` adds the daemon service without overwriting the rest of your config. Once the daemon is running, you can drop `--local` from `openclaw agent` calls. OpenClaw routes through the gateway instead. ```bash openclaw doctor ``` Checks daemon connectivity, provider configuration, and model availability. Add `--fix` to resolve common problems. --- ## Page: OpenClaw > Section: Uninstall 1. Remove the `models.providers.auriko` block and `auriko/*` entries from `agents.defaults.models` in `~/.openclaw/openclaw.json`. 2. Remove `env.vars.AURIKO_API_KEY` from the same file (only if you ran the daemon `config set` step). 3. Remove `AURIKO_API_KEY` from your shell profile (`~/.zshrc` or `~/.bashrc`). --- ## Page: OpenClaw > Section: Troubleshoot | Symptom | Cause | Fix | |---------|-------|-----| | `Error: No target session selected. Use --agent , --session-id , or --to .` | `openclaw agent` invoked without a target | Add `--agent main`, or run `openclaw agents list` to find another agent ID | | Wall of `EMBEDDED FALLBACK: Gateway agent failed...` before the answer | No daemon running, but OpenClaw tried the gateway first | Add `--local` to run the agent in your shell | | `Systemd user services are unavailable` | `--install-daemon` on Linux or container without user-systemd | Run `loginctl enable-linger $(whoami)` first, use `openclaw gateway run` in foreground, or stay on `--local` | | First call to a model takes 30-80s | Cold start on the provider | Normal. Subsequent calls run 10-25s. Raise `--timeout ` if it exceeds 90s | | `Error: API key is invalid.` | `AURIKO_API_KEY` not set in current shell or daemon env | Run `echo $AURIKO_API_KEY`. If empty, export it. For daemon use: `openclaw config set env.vars.AURIKO_API_KEY "$AURIKO_API_KEY"`, then restart the gateway | | `Error: Model not found: auriko/` | Model missing from `agents.defaults.models` or provider's `models` array | Add the model to **both** places in `~/.openclaw/openclaw.json` | | Connection refused on port 18789 | Daemon isn't running | Run `openclaw gateway status`. Start it with `openclaw gateway run`, or pass `--local` to `openclaw agent` | | `openclaw: command not found` | npm global bin not on PATH | `export PATH="$(npm prefix -g)/bin:$PATH"` | | Config parse error on startup | Invalid JSON in `openclaw.json` | Run `openclaw config show` to see the parse error. Common cause: missing comma after a merged key | | Empty response from agent | Model missing `contextWindow` or `maxTokens` in provider config | Add both fields to the model definition | | Duplicated response content (`setup-oksetup-ok`) | OpenClaw `pi` harness concatenation bug | Known upstream issue. Response content is correct but echoed. Doesn't affect all models equally | | API key valid in shell but daemon requests fail | LaunchAgent or systemd-user services don't inherit shell env | `openclaw config set env.vars.AURIKO_API_KEY "$AURIKO_API_KEY"`, then restart the gateway | --- ## Page: OpenCode Auriko is in OpenCode's provider registry, giving you access to 15 models through a single API key. --- ## Page: OpenCode > Section: Prerequisites - [OpenCode](https://opencode.ai) 1.4.10+ - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) --- ## Page: OpenCode > Section: Install OpenCode Run the official install script: ```bash curl -fsSL https://opencode.ai/install | bash ``` Or install via npm: ```bash npm i -g opencode-ai ``` Or via Homebrew (macOS): ```bash brew install opencode ``` Open a new terminal if you used the install script, then confirm: ```bash opencode --version ``` You need version 1.4.10 or later. The install script writes PATH to `~/.bashrc`, which non-interactive shells don't source. In CI, GitHub Actions, or agent-driven sessions, add this before invoking `opencode`: ```bash export PATH="$HOME/.opencode/bin:$PATH" ``` --- ## Page: OpenCode > Section: Set your API key Export your API key: ```bash export AURIKO_API_KEY="ak_live_..." ``` Your key starts with `ak_live_` (production) or `ak_test_` (testing). Add this line to `~/.zshrc` or `~/.bashrc` to persist it across terminal sessions. Reload your shell after saving: ```bash source ~/.zshrc # or: source ~/.bashrc ``` Or open a new terminal. --- ## Page: OpenCode > Section: Verify Run this from inside a git repository. OpenCode requires a git working tree. ```bash opencode run "Say exactly: setup-ok" --model auriko/claude-sonnet-4-6 --pure ``` OpenCode prints extra setup output on first run. The model response follows. --- ## Page: OpenCode > Section: Set default model To avoid passing `--model` on every run, create an `opencode.json` in your project root: ```json { "$schema": "https://opencode.ai/config.json", "model": "auriko/claude-sonnet-4-6" } ``` You can also place this at `~/.config/opencode/opencode.json` to set a global default. A project-root config takes precedence. You don't need a provider block. OpenCode resolves Auriko's models automatically. --- ## Page: OpenCode > Section: Use different models Pass any registered model with the `--model` flag: ```bash opencode run "Summarize this repo" --model auriko/deepseek-v4-flash opencode run "Review my code" --model auriko/gemini-2.5-pro ``` To start an interactive session: ```bash opencode ``` Use `/models` to browse and switch models during a session. These models are registered in OpenCode's provider registry and work without configuration: | Model | Author | Context | |-------|--------|---------| | `claude-sonnet-4-6` | Anthropic | 1M | | `claude-opus-4-6` | Anthropic | 1M | | `claude-opus-4-7` | Anthropic | 1M | | `deepseek-v4-flash` | DeepSeek | 1M | | `deepseek-v4-pro` | DeepSeek | 1M | | `gemini-2.5-flash` | Google | 1M | | `gemini-2.5-pro` | Google | 1M | | `gemini-3.1-pro-preview` | Google | 1M | | `glm-5.1` | Z.AI | 200K | | `grok-4.3` | xAI | 1M | | `kimi-k2.5` | Moonshot | 262K | | `kimi-k2.6` | Moonshot | 262K | | `minimax-m2-7` | MiniMax | 205K | | `minimax-m2-7-highspeed` | MiniMax | 205K | | `qwen-3.6-plus` | Alibaba | 1M | To list all Auriko models (not just the 15 registered in OpenCode): ```bash curl -s -H "Authorization: Bearer $AURIKO_API_KEY" \ https://api.auriko.ai/v1/models | jq '.data[].id' ``` --- ## Page: OpenCode > Section: Add unregistered models The 15 registered models work without configuration. To use any of the 250+ other models Auriko serves, add a provider and model definition to your `opencode.json`: ```json { "$schema": "https://opencode.ai/config.json", "model": "auriko/deepseek-v3.2", "provider": { "auriko": { "npm": "@ai-sdk/openai-compatible", "name": "Auriko", "env": ["AURIKO_API_KEY"], "options": { "baseURL": "https://api.auriko.ai/v1", "apiKey": "{env:AURIKO_API_KEY}" }, "models": { "deepseek-v3.2": { "name": "DeepSeek V3.2", "reasoning": true, "tool_call": true, "temperature": true, "limit": { "context": 163840, "output": 8192 }, "modalities": { "input": ["text"], "output": ["text"] } } } } } } ``` Browse model capabilities via the [directory API](https://api.auriko.ai/v1/directory/models) to fill in the model definition fields. --- ## Page: OpenCode > Section: Control routing Configure routing in the [Auriko dashboard](https://auriko.ai/dashboard). See [routing options](/guides/routing-options) for details. --- ## Page: OpenCode > Section: Uninstall Remove `AURIKO_API_KEY` from your shell profile (`~/.zshrc` or `~/.bashrc`). If you created an `opencode.json`, remove the auriko-related lines or delete the file. --- ## Page: OpenCode > Section: Troubleshoot | Symptom | Cause | Fix | |---------|-------|-----| | `Error: API key is invalid.` | `AURIKO_API_KEY` not set or key is wrong | Run `echo $AURIKO_API_KEY`. If empty, export it. If set, verify at `https://api.auriko.ai/v1/me` | | `Error: Model not found: auriko/` | Model not in registry and no local definition | Add a model definition to `opencode.json` (see "Add unregistered models") | | `opencode: command not found` | Install script PATH not loaded in non-interactive shell | Run `export PATH="$HOME/.opencode/bin:$PATH"` before invoking `opencode` | | Connection error | `baseURL` wrong or has trailing slash | Use exactly `https://api.auriko.ai/v1` | | Config not taking effect | Local config overriding global | Check both `~/.config/opencode/opencode.json` (global) and `./opencode.json` (project root) | | Empty response from `opencode run` | Not inside a git repository | Run from inside a git repo, or run `git init` first | --- ## Page: Kilo Code Auriko connects to Kilo Code as a provider, giving you access to multiple models through a single API key. --- ## Page: Kilo Code > Section: Prerequisites - [Kilo Code](https://kilocode.ai) VS Code extension or CLI - An [Auriko API key](https://auriko.ai/signup?redirectTo=%2Fdashboard%3Ftab%3Dapi-keys) — your key starts with `ak_live_` (production) or `ak_test_` (testing) --- ## Page: Kilo Code > Section: Install Kilo Code Open Extensions in VS Code (`Cmd+Shift+X` on macOS, `Ctrl+Shift+X` on Windows/Linux), search "Kilo Code", and install. Install via npm: ```bash npm install -g @kilocode/cli ``` Or via Homebrew (macOS): ```bash brew install Kilo-Org/tap/kilo ``` Confirm the installation: ```bash kilo --version ``` --- ## Page: Kilo Code > Section: Set your API key Export your API key in your shell: ```bash export AURIKO_API_KEY="ak_live_..." ``` To persist across sessions, add the same line to `~/.zshrc` or `~/.bashrc`, then either run `source ~/.zshrc` (or `source ~/.bashrc`) or open a new terminal. **Non-interactive shells (CI, Codespaces `bash -c`, scripts) don't source `~/.bashrc`.** For those, export `AURIKO_API_KEY` directly in the environment or source the file explicitly at the top of your script. ### Sanity-check the key Confirm the key works before installing anything else: ```bash curl -s -o /dev/null -w "%{http_code}\n" \ -H "Authorization: Bearer $AURIKO_API_KEY" \ https://api.auriko.ai/v1/me ``` Expected: `200`. If you get `401`, the key is missing, mistyped, or revoked. --- ## Page: Kilo Code > Section: Add Auriko as a provider ### VS Code 1. Open Kilo Code settings (gear icon in the Kilo Code panel). 2. Go to the **Providers** tab. 3. Click **Custom provider**. 4. Fill in: - **Provider ID:** `auriko` - **Display name:** `Auriko` - **Base URL:** `https://api.auriko.ai/v1` - **API key:** your `ak_live_...` key 5. Models auto-populate. Select a model. 6. Click **Submit**. ### CLI No registration step is needed — `kilo run` resolves any `auriko/` ID from your API key. Skip to **Verify**. If `kilo run --model auriko/` returns "model not found" (rare; usually means Kilo's registry cache didn't refresh), define the provider locally in a `kilo.json` at your project root: ```json { "$schema": "https://app.kilo.ai/config.json", "model": "auriko/claude-sonnet-4-6", "provider": { "auriko": { "env": ["AURIKO_API_KEY"], "models": { "claude-sonnet-4-6": { "name": "Claude Sonnet 4.6", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 16384 } } }, "options": { "baseURL": "https://api.auriko.ai/v1" } } } } ``` This overrides Kilo's provider registry with a local definition. --- ## Page: Kilo Code > Section: Verify **VS Code:** Start a chat in the Kilo Code panel and send a message. You should see a response from the model you selected. **CLI:** ```bash kilo run "Say exactly: setup-ok" --model auriko/claude-sonnet-4-6 ``` Expected output: `setup-ok`. **First run only:** Kilo performs a one-time SQLite migration (`Performing one time database migration, may take a few minutes...`). The first call can take up to a minute. Subsequent calls return in seconds. --- ## Page: Kilo Code > Section: Set default model To avoid passing `--model` on every run, create a `kilo.json` at your project root: ```json { "$schema": "https://app.kilo.ai/config.json", "model": "auriko/claude-sonnet-4-6" } ``` Place it at `~/.config/kilo/kilo.json` for a global default instead. A project-root config takes precedence. VS Code users set their default model in the model picker (top of the Kilo Code panel). **Migrating from opencode?** Kilo Code is an opencode fork and reads `./opencode.json` in addition to `./kilo.json`. If both files exist and define `model`, the opencode.json value wins silently. Either delete `opencode.json` or keep its `model` field in sync. --- ## Page: Kilo Code > Section: Use different models Pass any registered model with the `--model` flag: ```bash kilo run "Summarize this repo" --model auriko/deepseek-v4-flash kilo run "Review my code" --model auriko/gemini-2.5-pro ``` VS Code: switch models in the model picker. These models are available through Auriko: | Model | Author | Context | |-------|--------|---------| | `claude-sonnet-4-6` | Anthropic | 1M | | `claude-opus-4-6` | Anthropic | 1M | | `claude-opus-4-7` | Anthropic | 1M | | `deepseek-v4-flash` | DeepSeek | 1M | | `deepseek-v4-pro` | DeepSeek | 1M | | `gemini-2.5-flash` | Google | 1M | | `gemini-2.5-pro` | Google | 1M | | `gemini-3.1-pro-preview` | Google | 1M | | `glm-5.1` | Z.AI | 200K | | `grok-4.3` | xAI | 1M | | `kimi-k2.5` | Moonshot | 262K | | `kimi-k2.6` | Moonshot | 262K | | `minimax-m2-7` | MiniMax | 205K | | `minimax-m2-7-highspeed` | MiniMax | 205K | | `qwen-3.6-plus` | Alibaba | 1M | ### Known model quirks on Kilo - **`gemini-2.5-flash` on tool-call prompts:** returns an empty response (no tool invocation, no text). Use `gemini-2.5-pro` or `gemini-3.1-pro-preview` for tool-calling workflows on the Gemini family. - **`gemini-2.5-pro` on arithmetic:** can produce wrong answers when asked for an exact number without showing work. Prompt it to use a tool or show its reasoning for math-critical tasks. To list every model available on your key (not only the 15 above): ```bash curl -s -H "Authorization: Bearer $AURIKO_API_KEY" \ https://api.auriko.ai/v1/models | jq '.data[].id' ``` --- ## Page: Kilo Code > Section: Add unregistered models (advanced) If you need a model that isn't in the 15 above and `auriko/` doesn't resolve, define it locally in `kilo.json`: ```json { "$schema": "https://app.kilo.ai/config.json", "model": "auriko/deepseek-v3.2", "provider": { "auriko": { "env": ["AURIKO_API_KEY"], "models": { "deepseek-v3.2": { "name": "DeepSeek V3.2", "reasoning": true, "tool_call": true, "limit": { "context": 163840, "output": 8192 } } }, "options": { "baseURL": "https://api.auriko.ai/v1" } } } } ``` Browse model capabilities via the [directory API](https://api.auriko.ai/v1/directory/models) to fill in the model definition fields. VS Code custom provider auto-fetches all models from the API, so this section applies to CLI users only. --- ## Page: Kilo Code > Section: Control routing Configure routing in the [Auriko dashboard](https://auriko.ai/dashboard). See [routing options](/guides/routing-options) for details. --- ## Page: Kilo Code > Section: Uninstall **VS Code:** Remove the Auriko custom provider from Settings > Providers. Uninstall the Kilo Code extension. **CLI:** Remove `AURIKO_API_KEY` from your shell profile (`~/.zshrc` or `~/.bashrc`). If you created a `kilo.json`, remove the auriko-related lines or delete the file. --- ## Page: Kilo Code > Section: Troubleshoot | Symptom | Cause | Fix | |---------|-------|-----| | `Error: API key is invalid.` or `Error: Model not found: auriko/.` | `AURIKO_API_KEY` missing in this shell, or revoked | Run `echo $AURIKO_API_KEY`. If empty, export it. If set, verify at `https://api.auriko.ai/v1/me` (expect HTTP 200) | | No models in custom provider dialog (VS Code) | Base URL wrong or API unreachable | Use exactly `https://api.auriko.ai/v1`. Enter the API key first — auto-detection requires both | | Model not found (404) | Wrong model ID format | VS Code: bare IDs (`claude-sonnet-4-6`). CLI: `auriko/` prefix (`auriko/claude-sonnet-4-6`) | | `auriko/*` models not resolved (CLI) | Provider registry cache hasn't refreshed | Add a provider block to `kilo.json` (see "If auriko/* models aren't found") | | `kilo.json` model field is ignored | Sibling `opencode.json` overrides it (Kilo reads both since it's an opencode fork), or `~/.config/kilo/kilo.json` is loaded after the project file | Check `./kilo.json`, `./opencode.json` (if present), and `~/.config/kilo/kilo.json` — keep them in sync or remove the ones you don't use | | `Performing one time database migration...` on first run | Kilo's one-time SQLite migration | Wait — typically completes in under a minute. Only happens on the very first call | | `kilo run` returns exit code 0 even when output starts with `Error:` | Kilo CLI doesn't propagate failures via exit code | For CI: grep stdout/stderr for `^Error:`, or check for a non-empty `text` event in `--format json` output. Don't rely on `$?` | | `gemini-2.5-flash` returns nothing on a tool-using prompt | Known Kilo host-side integration quirk | Use `gemini-2.5-pro` or `gemini-3.1-pro-preview` for tool calls |