# UniGateway LLM + Agent Guide UniGateway is an OpenAI-compatible unified AI gateway. Single endpoint, multiple providers/models. Brand positioning: unified AI gateway, OpenAI-compatible multi-provider gateway. Base API URL: https://api.unigateway.ai/v1 Docs Home: https://unigateway.ai/docs Models: https://unigateway.ai/models Model pricing: https://unigateway.ai/models LLM Full Context: https://unigateway.ai/llms-full.txt ## Published docs index - [概览](https://unigateway.ai/docs/overview) - [快速开始](https://unigateway.ai/docs/quickstart) - [账户与 API Keys](https://unigateway.ai/docs/account-and-api-keys) - [管理 API Key](https://unigateway.ai/docs/management-key) - [UniGateway 组织用户与子用户](https://unigateway.ai/docs/organization-users) - [UniGateway 多因素验证(MFA)](https://unigateway.ai/docs/mfa) - [UniGateway 组织安全与额度通知](https://unigateway.ai/docs/organization-security-notifications) - [套餐与定价](https://unigateway.ai/docs/usage-plans-and-pricing) - [Authentication](https://unigateway.ai/docs/authentication) - [OpenAI 兼容 API](https://unigateway.ai/docs/openai-compatible-api) - [Chat Completions](https://unigateway.ai/docs/chat-completions) - [OpenAI Responses API](https://unigateway.ai/docs/openai-responses-api) - [Embeddings](https://unigateway.ai/docs/embeddings) - [Models](https://unigateway.ai/docs/models) - [Audio](https://unigateway.ai/docs/audio) - [UniGateway Kimi K3 模型支持](https://unigateway.ai/docs/kimi-k3) - [Anthropic 兼容 API](https://unigateway.ai/docs/anthropic-compatible-api) - [Anthropic Messages API](https://unigateway.ai/docs/anthropic-messages-api) - [Gemini 原生 API](https://unigateway.ai/docs/gemini-native-api) - [Gemini generateContent API](https://unigateway.ai/docs/gemini-generate-content) - [模型选择与回退](https://unigateway.ai/docs/model-selection-and-fallback) - [错误处理与重试](https://unigateway.ai/docs/error-handling-and-retries) - [Claude Prompt Caching](https://unigateway.ai/docs/prompt-caching) - [Streaming](https://unigateway.ai/docs/streaming) - [图像生成概览](https://unigateway.ai/docs/images-overview) - [Image Generation and Editing](https://unigateway.ai/docs/image-generation) - [OpenAI Images API](https://unigateway.ai/docs/openai-images-api) - [Gemini Images API](https://unigateway.ai/docs/gemini-images-api) - [视频生成概览](https://unigateway.ai/docs/video-generation-landing) - [Sora Video Generation](https://unigateway.ai/docs/sora-overview) - [Seedance 概览](https://unigateway.ai/docs/seedance-overview) - [Seedance 创建任务](https://unigateway.ai/docs/seedance-create-task) - [Seedance 查询任务](https://unigateway.ai/docs/seedance-task-query) - [Seedance 素材库](https://unigateway.ai/docs/seedance-asset-libraries) - [OpenAI SDK 接入](https://unigateway.ai/docs/openai-sdk) - [Dify 接入](https://unigateway.ai/docs/dify) - [OpenWebUI 接入](https://unigateway.ai/docs/openwebui) - [AI 编程工具](https://unigateway.ai/docs/coding-tools-and-agents) - [Codex CLI 接入](https://unigateway.ai/docs/codex-cli) - [Codex 桌面版接入](https://unigateway.ai/docs/codex-desktop) - [OpenCode 接入](https://unigateway.ai/docs/opencode) - [GitHub Copilot 接入](https://unigateway.ai/docs/copilot) - [WorkBuddy 接入](https://unigateway.ai/docs/workbuddy) - [pi-dev 接入](https://unigateway.ai/docs/pi) - [CC-Switch 接入](https://unigateway.ai/docs/cc-switch) - [OpenClaw 接入](https://unigateway.ai/docs/openclaw) - [Claude Code 接入](https://unigateway.ai/docs/claude-code) - [LobeChat 接入](https://unigateway.ai/docs/lobechat) - [Claude Desktop 接入](https://unigateway.ai/docs/claude-desktop) - [Hermes Agent 接入](https://unigateway.ai/docs/hermes-agent) - [n8n 接入](https://unigateway.ai/docs/n8n) - [LangChain 接入](https://unigateway.ai/docs/langchain) - [Cherry Studio 接入](https://unigateway.ai/docs/cherry-studio) - [Flowise 接入](https://unigateway.ai/docs/flowise) - [Continue 接入](https://unigateway.ai/docs/continue) - [Cline 接入](https://unigateway.ai/docs/cline) - [API Key 用量查询](https://unigateway.ai/docs/usage-analytics) - [API Key 额度与限制查询](https://unigateway.ai/docs/api-key-quotas-and-restrictions) - [账户管理 API](https://unigateway.ai/docs/account-management-api) - [UniGateway 账户用量与计费 API](https://unigateway.ai/docs/account-usage-billing-api) - [API 兼容性矩阵](https://unigateway.ai/docs/endpoint-compatibility) - [错误码参考](https://unigateway.ai/docs/error-codes-reference) ## LLM-friendly markdown endpoints - Canonical docs URL: https://unigateway.ai/docs/{slug} - Markdown mirror URL: https://unigateway.ai/docs/{slug}.md ## Agent maintenance protocol (JWT) Use Admin JWT for create/update/delete operations. ### 1) Login and get JWT POST /api/auth/login Content-Type: application/json { "email": "", "password": "" } Read token from response field `token`. Then use header: Authorization: Bearer ### 2) Read documentation - GET /api/docs - GET /api/docs/{slug} ### 3) Manage categories (Admin JWT required) - GET /api/admin/doc-categories - POST /api/admin/doc-categories - PUT /api/admin/doc-categories/{id} - DELETE /api/admin/doc-categories/{id} ### 4) Manage docs (Admin JWT required) - POST /api/docs - PUT /api/docs/{slug} - DELETE /api/docs/{slug} ### 5) Update this llms.txt through API (Admin JWT required) - GET /api/admin/llms-txt - PUT /api/admin/llms-txt PUT payload: { "content": "# UniGateway LLM + Agent Guide ..." } ## Authoring conventions for agents 1) Slug: lowercase + hyphen only (`^[a-z0-9-]+$`). 2) Keep both EN/ZH content synchronized. 3) Keep code examples runnable and OpenAI-compatible. 4) Prefer additive updates; avoid deleting existing docs unless explicitly requested. 5) Keep the first 160 chars concise for metadata extraction. --- # Full Documentation Content ## Getting Started # Overview > Category: Getting Started | Last updated: 2026-08-09 Task-oriented entry point for UniGateway endpoints, model IDs, and first integration paths. # Overview UniGateway is a unified AI gateway. API root: `https://api.unigateway.ai`. One integration point gives access to OpenAI, Anthropic, and Google model families. ## Start by Task | Goal | Use this endpoint | Start here | |---|---|---| | Chat or text generation | `POST /v1/chat/completions` | [Quickstart](/docs/quickstart) | | Stateful agent workflow | `POST /v1/responses` | [OpenAI Responses API](/docs/openai-responses-api) | | Claude-native request shape | `POST /v1/messages` | [Anthropic Messages API](/docs/anthropic-messages-api) | | Gemini-native request shape | `POST /v1beta/models/{model}:generateContent` | [Gemini Text Chat](/docs/gemini-generate-content) | | Image generation or editing | `POST /v1/images/generations` or Gemini `generateContent` | [Image Generation and Editing](/docs/image-generation) | | Audio transcription or translation | `POST /v1/audio/transcriptions` or `/v1/audio/translations` | [Audio](/docs/audio) | | Video generation | `https://api.unigateway.ai` or `/v1/videos` | [Video Generation](/docs/video-generation-landing) | | Third-party app integration | OpenAI-compatible base URL | [OpenAI SDK](/docs/openai-sdk) | ## Model Families - OpenAI (GPT) - Anthropic (Claude) - Google (Gemini) ## Base URLs - API root: `https://api.unigateway.ai` - Compatible paths: `/v1`, `/v1beta` ## Core Endpoints ## Code examples ### curl ```curl curl https://api.unigateway.ai/v1/chat/completions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [ {"role": "user", "content": "Say hello from UniGateway."} ] }' ``` ### python ```python from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.unigateway.ai/v1", ) resp = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Say hello from UniGateway."}], ) print(resp.choices[0].message.content) ``` ### typescript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1", }); const resp = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Say hello from UniGateway." }], }); console.log(resp.choices[0]?.message?.content); ``` - `GET /v1/models` — list available models - `POST /v1/chat/completions` — chat and text generation ## Supported Endpoints | Path | Purpose | |---|---| | `GET /v1/models` | list available models | | `POST /v1/chat/completions` | chat and text generation | | `POST /v1/responses` | enhanced conversation interface | | `POST /v1/embeddings` | vector embeddings | | `POST /v1/images/*` | image generation and editing | | `POST /v1/audio/transcriptions` | audio transcription | | `POST /v1/audio/translations` | audio translation | | `POST /v1/messages` | Anthropic-compatible requests | | `POST /v1beta/models/{model}:generateContent` | Gemini-compatible requests | Parameter support varies by model family. Check capability against your target model. ## Model IDs Use the exact model ID returned by `GET /v1/models`. Use the exact model ID from the API response. Model library display names (such as Nano Banana Pro, Nano Banana 2) are product nicknames for readability. For example, Nano Banana names map to Gemini image model IDs such as `gemini-3-pro-image-preview`. Example IDs — verify with a live query: - `gpt-5.4` - `claude-sonnet-4-6` - `gemini-3-pro-preview` See [Quickstart](/docs/quickstart) for an integration walkthrough. # Quickstart > Category: Getting Started | Last updated: 2026-08-09 Create an API key, choose the right endpoint by task, and make the first successful request. # Quickstart From zero to your first working API response in four steps. ## Step 1 — Sign Up 1. Go to the [UniGateway login page](https://unigateway.ai/login) 2. Sign in with your email address After signing in you land on the dashboard where you can manage keys, view usage, and configure routing. ## Step 2 — Top Up Your Balance Go to **Settings → Billing** to top up your balance. See [Usage Plans & Pricing](/docs/usage-plans-and-pricing) for details. ## Step 3 — Get an API Key 1. Open **Settings → API Keys** in the UniGateway console 2. Click **Create Key** 3. Copy the key immediately — it is shown only once ```bash export UNIGATEWAY_API_KEY="" ``` > Store API keys in environment variables or `.env` files. Do not commit them to version control. For key rotation and multi-key strategies, see [Account & API Keys](/docs/account-and-api-keys). ## Step 4 — Make Your First Request ## Code examples ### curl ```curl curl https://api.unigateway.ai/v1/chat/completions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [ {"role": "system", "content": "You are a concise assistant."}, {"role": "user", "content": "Write a 1-line product tagline for UniGateway."} ], "temperature": 0.3 }' ``` ### python ```python from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.unigateway.ai/v1", ) resp = client.chat.completions.create( model="gpt-5.4", messages=[ {"role": "system", "content": "You are a concise assistant."}, {"role": "user", "content": "Write a 1-line product tagline for UniGateway."}, ], temperature=0.3, ) print(resp.choices[0].message.content) ``` ### typescript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1", }); const resp = await client.chat.completions.create({ model: "gpt-5.4", messages: [ { role: "system", content: "You are a concise assistant." }, { role: "user", content: "Write a 1-line product tagline for UniGateway." } ], temperature: 0.3, }); console.log(resp.choices[0]?.message?.content); ``` UniGateway supports three API protocols. Pick the one you are most familiar with. If you already know what you want to build, use this shortcut table: | Goal | Endpoint | Recommended first model | |---|---|---| | Chat or text generation | `/v1/chat/completions` | `gpt-5.4` | | Claude-native messages | `/v1/messages` | `claude-sonnet-4-6` | | Gemini-native text | `/v1beta/models/gemini-3-pro-preview:generateContent` | `gemini-3-pro-preview` | | Image generation | `/v1/images/generations` | `gpt-image-2` | | Gemini image generation | `/v1beta/models/gemini-3-pro-image-preview:generateContent` | `gemini-3-pro-image-preview` | | Audio transcription | `/v1/audio/transcriptions` | `whisper-1` | | Audio translation | `/v1/audio/translations` | `whisper-1` | Always confirm the model ID with `GET /v1/models` before putting it in production. ### Protocol 1: OpenAI Chat Completions Base URL: `https://api.unigateway.ai/v1` ```bash curl https://api.unigateway.ai/v1/chat/completions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [ {"role": "user", "content": "What is the meaning of life?"} ] }' ``` **Python (OpenAI SDK)** ```bash pip install openai ``` ```python from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.unigateway.ai/v1", ) completion = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "What is the meaning of life?"}], ) print(completion.choices[0].message.content) ``` **TypeScript (OpenAI SDK)** ```bash npm install openai ``` ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1", }); const completion = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "What is the meaning of life?" }], }); console.log(completion.choices[0].message.content); ``` ### Protocol 2: Anthropic Messages Base URL: `https://api.unigateway.ai/v1` ```bash curl https://api.unigateway.ai/v1/messages \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -H "Anthropic-Version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [ {"role": "user", "content": "What is the meaning of life?"} ] }' ``` **Python (Anthropic SDK)** ```bash pip install anthropic ``` ```python import anthropic client = anthropic.Anthropic( api_key="", base_url="https://api.unigateway.ai/v1", ) message = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[{"role": "user", "content": "What is the meaning of life?"}], ) print(message.content[0].text) ``` **TypeScript (Anthropic SDK)** ```bash npm install @anthropic-ai/sdk ``` ```typescript import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1", }); const message = await client.messages.create({ model: "claude-sonnet-4-6", max_tokens: 1024, messages: [{ role: "user", content: "What is the meaning of life?" }], }); console.log(message.content[0].text); ``` ### Protocol 3: Google Gemini Base URL: `https://api.unigateway.ai/v1beta` ```bash curl https://api.unigateway.ai/v1beta/models/gemini-3-pro-preview:generateContent \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contents": [ {"parts": [{"text": "What is the meaning of life?"}]} ] }' ``` **Python (plain requests)** ```bash pip install requests ``` ```python import requests api_key = "" resp = requests.post( "https://api.unigateway.ai/v1beta/models/gemini-3-pro-preview:generateContent", headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, json={ "contents": [ {"parts": [{"text": "What is the meaning of life?"}]} ] }, ) resp.raise_for_status() print(resp.json()["candidates"][0]["content"]["parts"][0]["text"]) ``` ```typescript const resp = await fetch( "https://api.unigateway.ai/v1beta/models/gemini-3-pro-preview:generateContent", { method: "POST", headers: { Authorization: `Bearer ${process.env.UNIGATEWAY_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ contents: [{ parts: [{ text: "What is the meaning of life?" }] }], }), }, ); if (!resp.ok) { throw new Error(await resp.text()); } const data = await resp.json(); console.log(data.candidates[0].content.parts[0].text); ``` ## Find Model IDs Every model on UniGateway has a unique ID. You can browse available models in the console or query the API: ```bash curl https://api.unigateway.ai/v1/models \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` Pick a model ID from the response. Example IDs — verify with a live query: | Family | Example ID | |---|---| | OpenAI | `gpt-5.4` | | Anthropic | `claude-sonnet-4-6` | | Google | `gemini-3-pro-preview` | Use the exact model ID returned by `GET /v1/models`. Use the exact model ID from the API response. Model library display names are not always requestable model IDs. Use the `id` field from the API response in your requests. ## Next Steps - [Authentication](/docs/authentication) — header format, key management, troubleshooting - [Model Selection and Fallback](/docs/model-selection-and-fallback) — build production fallback chains - [Streaming](/docs/streaming) — enable incremental output with SSE - [Audio](/docs/audio) — speech transcription and translation - [Error Codes](/docs/error-codes-reference) — complete error reference with resolution steps # Account & API Keys > Category: Getting Started | Last updated: 2026-08-09 Registration, API Key creation, quotas, model and IP allowlists, usage statistics, security, rotation, and multi-key strategy. # Account & API Keys This page explains how to register and manage your UniGateway account, create API Keys, and configure API Key quotas, model allowlists, IP allowlists, usage statistics, and request logs. ## Registration and Sign-In UniGateway supports email-based registration and sign-in. Go to the [UniGateway login page](https://unigateway.ai/login), complete registration or sign-in, and enter the console. Create at least one API Key before your first API call. Account-level endpoints require a separate Management API Key. See [Management API Key](/docs/management-key) for creation, one-time copy, configuration, and rotation. ## API Key Management ### Create an API Key Follow these steps to create an API Key: 1. Sign in to the [UniGateway Dashboard](https://unigateway.ai/dashboard). 2. Click **API Key** in the left navigation. 3. Click **New API Key** in the top-right corner of the API Key page. 4. Enter an API Key name. The name distinguishes environments, applications, or services, such as `production-chat` or `staging-rag`; description, daily limit, weekly limit, and monthly limit can be configured as needed. 5. Optionally configure a model allowlist so the API Key can only call the selected models. 6. Optionally configure an IP allowlist. Enter one IP or CIDR range per line to allow only matching client egress IPs. 7. After completing the configuration, scroll to the bottom of the page and click **Create API Key**. 8. Immediately save the complete Key value after creation. The complete Key is shown only once at creation time. If you did not save it or lost it, delete the original Key and create a new one; the system cannot display the original Key content again. ### Verify an API Key After creation, request `https://api.unigateway.ai/v1/models` to verify the API Key works. ## Code examples ### curl ```curl curl https://api.unigateway.ai/v1/chat/completions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Hello"}] }' ``` ### python ```python from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.unigateway.ai/v1", ) resp = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello"}], ) ``` ### typescript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1", }); ``` **cURL** ```bash curl https://api.unigateway.ai/v1/models \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` **OpenAI SDK** Install the dependency: ```bash pip install openai ``` ```python import os from openai import OpenAI client = OpenAI( api_key=os.environ["UNIGATEWAY_API_KEY"], base_url="https://api.unigateway.ai/v1", ) models = client.models.list() for model in models.data: print(model.id) ``` A successful model list response means the Key can be used for the corresponding model calls. See [Quickstart](/docs/quickstart#query-available-models) for model ID selection. Before using paid models, top up your balance first and make sure the account balance is sufficient. See [Plans & Pricing](/docs/usage-plans-and-pricing). ### Disable and Delete a Key Users with API Key management permissions can disable Keys that are no longer used, at risk, or need temporary isolation on the API Key page. A disabled Key cannot be used for new API requests. When a Key is no longer needed, delete it. Deletion is irreversible; confirm that related applications have switched to a valid new Key before deleting. ## API Key Configuration and Usage Management Users with API Key management permissions can centrally manage API Key access scope, spending limits, and usage. ### API Key Quotas Optional spending limits can be configured per API Key. Amounts are in USD: | Quota period | Use case | | --- | --- | | Daily limit | Control single-day spending and reduce the impact of abnormal traffic | | Weekly limit | Manage short-cycle budgets and phased test costs | | Monthly limit | Control long-term budgets and production cost | When spending in any period reaches the configured threshold, the system automatically disables that API Key; leaving it blank means no limit for that period. Set different amounts for different environments and service purposes, such as a low daily and weekly limit for the test environment. ### Model Allowlist When the model allowlist is enabled for an API Key, the Key can only call the selected models; without an allowlist, the Key can access all models available to the current account. Search models by name, provider, or deployment type, and select from provider, commonly used, and available model lists. The model list changes with the current account's available scope. ### IP Allowlist When an IP allowlist is configured for an API Key, only requests from trusted client IPs or CIDR ranges can use the Key. Enter one IP or CIDR range per line; comma separation is not supported. The system validates the client IP of each request. Leave it blank for no IP restriction. Before configuring an allowlist, confirm the egress IP that your application actually uses for outbound requests. With cloud services, NAT gateways, or CI platforms, the egress IP may differ from local development. ### Usage Statistics and Request Logs The console shows usage statistics and request logs per API Key, including: - Seven-day spending trends, to identify cost changes and abnormal fluctuations. - Recent request logs, to troubleshoot call failures, model selection, or traffic anomalies. - API Key-level usage information, to distinguish spending across environments, services, or team members. For aggregated statistics, conditional aggregation, per-request details, and async task logs, see the usage statistics, usage analysis, usage details, and task logs documentation. Create separate API Keys for production, testing, and each independent service so statistics have clear attribution and actionable insight. ## Key Security ### Do - Store Keys in server-side environment variables or a secrets management service. - Create separate Keys for different environments such as production and testing. - Create a separate Key for each application, service, or team member. - Rotate Keys on a regular schedule and delete old Keys after rotation. - Disable or delete a Key immediately when exposure risk is found. ### Do Not - Commit Keys to code repositories, logs, or configuration templates. - Embed Keys in frontend JavaScript, mobile applications, or public pages. - Let multiple applications or team members share one Key long term. - Send complete Keys through chat, email, Issues, or tickets. - Continue using a Key after suspected exposure. ## Key Rotation To rotate a Key with zero downtime, proceed in this order: 1. Create a new Key in the console. 2. Deploy the new Key to your application's secure configuration. 3. Verify API requests succeed with the new Key. 4. Disable or delete the old Key. The old Key remains usable until disabled or deleted, so completing verification before revoking the old Key avoids business request interruption. ## Multi-Key Strategy | Strategy | Use case | | --- | --- | | One Key per environment | Isolate test and production traffic, quotas, and billing | | One Key per service | Isolate usage, errors, and access policies per microservice | | One Key per team member | Audit individual API usage and access behavior | | API Key management for admins only | Separate Key, quota, and statistics management from daily model calls | ## Change Password Click **Settings** in the console left navigation to open the password change page. ## Common Issues | Symptom | Possible cause | Resolution | | --- | --- | --- | | Every request returns `401 Unauthorized` | Key missing, invalid, or disabled | Check the Key status in the console; create a new Key and update application configuration if needed | | Valid Key returns `403 Forbidden` | Request does not satisfy model or IP access policy | Check the model allowlist, IP allowlist, and the endpoint being called | | Works locally but not in CI | CI environment variable not set, or CI egress IP not in the allowlist | Configure the variable with CI's secret management and check allowlist rules | | Request rate limited | API Key daily, weekly, or monthly limit reached | Check the Key's quota configuration and current usage in the console | | Requests hit the wrong provider | `base_url` not set correctly | Use `https://api.unigateway.ai/v1` with the OpenAI SDK | # Management API Key > Category: Getting Started | Last updated: 2026-08-09 Create, configure, verify, rotate, and secure the account-level Management API Key used by account endpoints. # Management API Key A Management API Key calls account-level endpoints to query the API Keys in your account, usage, quota, and billing data. It is not a model-calling credential and cannot be used to send model requests. Management API Keys use the `mk-` prefix; regular API Keys usually use the `sk-` prefix. The two key types have different purposes, Base URLs, and callable endpoints. Do not mix them. ## Create a Management API Key Create a Management API Key in the UniGateway console: 1. Sign in to the [UniGateway Dashboard](https://unigateway.ai/dashboard). 2. Click **Management Keys** in the left navigation to open the Management API Key page. 3. Click **New Management Key** in the top-right corner of the page. 4. Enter a name for the Management API Key on the creation page. Use a name that identifies its purpose or environment, such as `billing-export` or `production-audit`. 5. Click **Create Key**. 6. After creation, use the **Copy** button to copy the complete Key and save it immediately to a controlled secrets store or server-side environment variable. > **Important: the complete Management API Key value is shown only once on the creation success page.** After you leave the page or click **Done**, the console does not display or provide the original Key again. If you did not copy, save, or have lost the complete Key, the original value cannot be recovered; create a new Management API Key instead, and delete the unused Key after confirming the new Key works. ## Configure a Management API Key Store the Management API Key only in server-side environment variables or a controlled secrets management service. Do not write a real Key into browser frontend code, logs, public repositories, screenshots, tickets, or export files. Bash or compatible shells: ```bash export UNIGATEWAY_MANAGEMENT_KEY="" ``` Windows PowerShell: ```powershell $env:UNIGATEWAY_MANAGEMENT_KEY = "" ``` Use the variable as the Bearer token when calling account-level endpoints: ```http Authorization: Bearer $UNIGATEWAY_MANAGEMENT_KEY ``` Account-level endpoints use `https://unigateway.ai` as the Base URL, not the model API Base URL `https://api.unigateway.ai`. ## Management API Key vs. Regular API Key | Key type | Main purpose | Base URL | Example environment variable | | --- | --- | --- | --- | | Management API Key (`mk-`) | Query account API Keys, usage, quota, and billing data | `https://unigateway.ai` | `UNIGATEWAY_MANAGEMENT_KEY` | | Regular API Key (`sk-`) | Call model endpoints; query the current Key's own quota, limits, and usage | Model endpoints: `https://api.unigateway.ai/v1`; self-query: `https://unigateway.ai` | `UNIGATEWAY_API_KEY` | A Management API Key cannot call `GET /v1/models` or other model endpoints. A regular API Key can call model endpoints, and can query its own data via `GET /v1/api-key` and `GET /v1/usage`, but cannot query other API Keys in the same account or account-wide data. Use a Management API Key when you need data for all API Keys. ## Verify a Management API Key After creating and configuring a Management API Key, run an account API Key list request in the server-side environment that loads the environment variable: ## Code examples ### bash ```bash curl -G https://unigateway.ai/v1/account/api-keys \ -H "Authorization: Bearer $UNIGATEWAY_MANAGEMENT_KEY" \ --data-urlencode "limit=1" \ --data-urlencode "offset=0" ``` ```bash curl -G https://unigateway.ai/v1/account/api-keys \ -H "Authorization: Bearer $UNIGATEWAY_MANAGEMENT_KEY" \ --data-urlencode "limit=1" \ --data-urlencode "offset=0" ``` A `200 OK` response with `items`, `total`, and pagination fields means the Management API Key works with account-level endpoints. The response does not return any complete API Key or Key hash. ## Rotation and Deletion To rotate a Management API Key, or if you suspect it is exposed, proceed in this order: 1. Create a new Management API Key on the Management API Key page and copy the complete value on the creation success page. 2. Update the value in server-side environment variables or the secrets management service. 3. Restart or reload the service processes that read the variable, then re-run the verification request from the previous section. 4. After confirming all account-level tasks use the new Key, delete the old Key in the console. After the old Key is deleted, account-level requests using it fail. Before deleting, check scheduled tasks, report exports, and monitoring services to make sure they load the new Key. ## Security Limits - A Management API Key can read account-wide API Key metadata, usage, and billing data. Manage it as a high-sensitivity credential. - Do not hand a Management API Key to browsers, mobile clients, or untrusted third-party services. - Create a separate Management API Key for each environment or automated task to support audit and rotation. - Grant the Management API Key only to services that need account-wide queries; daily model calls and single-Key self-usage queries should use the corresponding regular API Key. - If a Key appears in code repositories, logs, screenshots, or shared documents, immediately create a new Key, update service configuration, and delete the old Key. ## Frequently Asked Questions ### Q: What if I did not copy the Management API Key after creation? The complete Key is shown only once on the creation success page; the console cannot restore the original value. Re-enter the Management API Key page to create a new Key, click **Copy** on the success page, save it to the server-side environment variable `UNIGATEWAY_MANAGEMENT_KEY` or a controlled secrets management service, then re-request with [Verify a Management API Key](#verify-a-management-api-key). After confirming the old Key is unused, delete it. ### Q: Why does calling model endpoints with a Management API Key fail? This is a credential-purpose mismatch. Confirm model requests use a regular API Key and set the Base URL to `https://api.unigateway.ai/v1`; a Management API Key is only for account-level endpoints and cannot call model endpoints. ### Q: A regular API Key returns `401` on account management endpoints. How do I proceed? If you need to query all API Keys in the account, other Keys' usage, or account billing data, check that the `Authorization` header uses `UNIGATEWAY_MANAGEMENT_KEY` and the variable holds a valid `mk-` Key. Confirm the request URL starts with `https://unigateway.ai`, fix the environment variable, restart or reload the service process, and re-run `GET /v1/account/api-keys?limit=1&offset=0` to verify. A regular `sk-` API Key can still use `GET /v1/api-key` or `GET /v1/usage` to query its own data, but cannot call account-scope management endpoints. ### Q: A Management API Key has been exposed. What should I do? Immediately create a new Management API Key in the console, copy and save the new value, update every service that uses `UNIGATEWAY_MANAGEMENT_KEY`, restart or reload those services, and verify with an account API Key list request. After verification succeeds, delete the old Key and clean up any public logs, screenshots, export files, and shared document copies. ## Related UniGateway Docs - [Account & API Keys](/docs/account-and-api-keys) - [API Key Quotas](/docs/api-key-quotas-and-restrictions) - [API Key Usage](/docs/usage-analytics) - [Account Management API](/docs/account-management-api) - [Account Usage & Billing API](/docs/account-usage-billing-api) - [UniGateway API Keys](https://unigateway.ai/dashboard/api-keys) # UniGateway Organization Users and Sub-users > Category: Getting Started | Last updated: 2026-08-10 Understand UniGateway account roles: legacy user main accounts, organization accounts, organization admins, and organization sub-users — registration, migration, people management, permissions, API Keys, and usage attribution. # UniGateway Organization Users and Sub-users This page explains the four account roles in UniGateway: legacy user main accounts, organization accounts, organization admins, and organization sub-users. It covers default registration, legacy user migration, sign-in entries, people management, permissions, API Keys and usage attribution, and common troubleshooting. An organization is an independent scope of administration and billing. People, model permissions, balance, and usage all belong to an organization; API Keys belong to the account that created them. ## Account Modes and Default Registration The current registration flow creates an organization account by default. After registration, the system creates the first organization and sets the account associated with the registered email as that organization's organization account. In this page, "organization account" means the organization user. The organization user is the highest-privilege role within an organization. The sign-in page provides two entries: - **Main account**: for legacy user main accounts and organization accounts. Both can use UniGateway on their own; organization accounts also belong to an organization. - **Sub-account**: for organization admins and organization sub-users who have been added as people by an organization. Sign-in requires the organization ID, sign-in email, and password. Legacy user main accounts and organization accounts both enter the console through the **Main account** entry. Organization accounts and organization admins can see and use **People**; organization sub-users do not see this entry, and the resources available to them are determined by the settings of the organization account and organization admins. ## Account Types and Permissions | Account type | Sign-in method | Operations available | | --- | --- | --- | | **Legacy user main account** | Select **Main account** on the sign-in page | Can use UniGateway on its own, manage its own API Keys, balance, and usage; does not belong to an organization until migrated. | | **Organization account** | Select **Main account** on the sign-in page | Can use UniGateway on its own, and manage the organization, its people, API Keys, balance, and organization settings. | | **Organization admin** | Select **Sub-account** on the sign-in page and enter the organization ID | Manages the people, API Keys, and organization settings of the current organization. | | **Organization sub-user** | Select **Sub-account** on the sign-in page and enter the organization ID | Creates and uses their own API Keys and views their own usage; API Keys are subject to the permission controls of the organization account and organization admins. | Organization admins and organization sub-users both sign in through the sub-account entry. Organization admins are specifically responsible for managing the current organization; organization sub-users can only use the resources assigned to them. ### Account Relationships - A legacy user main account can use UniGateway on its own; after migration, it becomes an organization account. - An organization account belongs to one organization and can manage that organization's people and resources. - Each organization can have multiple organization sub-users. - An organization admin is also a member of the organization. - An API Key belongs to the account that created it. - Organization sub-users can view and manage the API Keys they create, but their usable scope is subject to the permission controls of the organization account and organization admins. ### Permission Scope | Operation | Legacy user main account | Organization account | Organization admin | Organization sub-user | | --- | --- | --- | --- | --- | | Use UniGateway on its own | Yes | Yes | N/A | N/A | | Manage organization people | Yes after migration | Yes | Yes | No | | Deactivate or restore people | Yes after migration | Yes | Yes | No | | Revoke an organization sub-user's admin permission | Yes after migration | Yes | Yes, but cannot revoke self | No | | Deny an organization sub-user access | Yes after migration | Yes | Yes, but cannot deny self | No | | Reset member passwords | Yes after migration | Yes | Yes | No | | View all organization API Keys | Yes after migration | Yes | Yes | No | | Change the assignee of an API Key | Yes after migration | Yes | Yes | No | | View organization balance and billing settings | Yes | Yes | No, viewing organization balance is not available | Cannot manage | | Set model rules for all API Keys in the organization | Yes after migration | Yes | Yes | No | | Set model rules for own API Keys | Yes | Yes | Yes | Yes | | View other members' usage | Yes after migration | Yes | Yes | No | ## Migrating a Legacy User to an Organization Legacy users can migrate to organization users by default. Legacy users always sign in through the **Main account** entry of the [UniGateway sign-in page](https://unigateway.ai/login); they do not use the **Sub-account** entry and do not need to enter an organization ID. 1. From the sign-in page, select **Main account** and sign in to the console. 2. On the **People** page of the console, click **Add Sub-account**, fill in the person's information, and submit. 3. When you add the first person, the system also creates the default organization and migrates the legacy user to an organization account. 4. After migration, sign out and sign in again through the **Main account** entry. 5. After re-entering the console, confirm the organization name, organization ID, and the left navigation are correct. When a legacy user creates an organization for the first time, the first default organization created inherits all of the legacy user's history, API Keys, and related account data. After migration, the user still signs in through the main account entry and manages the organization and its people as an organization account. New organizations created later do not inherit data from the legacy user or the first organization. Their billing, balance, API Keys, usage, and people are independent new data that must be configured and managed separately within that organization. ## Adding a Sub-user An organization account or organization admin can create sub-users in the current organization. New sub-users default to the organization sub-user role. 1. Open [Organization People](https://unigateway.ai/dashboard/people). 2. Confirm the page shows the target organization and its organization ID. 3. Click **Add Sub-account**. 4. Enter the member's sign-in email; the username is optional. 5. After submitting, copy the temporary password immediately. 6. Send the organization ID, sign-in email, and temporary password to the member over a secure channel. 7. The member selects **Sub-account** on the [UniGateway sign-in page](https://unigateway.ai/login) and enters the information above. 8. After the member's first sign-in, they set a permanent password. When there are no sub-users, the People page shows the **Add Sub-account** entry; after submitting, admins can continue to manage roles, status, and access permissions in the people list. The temporary password is shown only once. If the temporary password is lost, used, or expired, regenerate it on the People page. ## Setting Up and Managing Organization Admins New sub-users default to organization sub-users. A sub-user must first complete their first sign-in using the organization ID, sign-in email, and temporary password, and set a permanent password; only after the first sign-in can an organization account or organization admin set them as an organization admin. 1. Open [Organization People](https://unigateway.ai/dashboard/people). 2. Find the target member and click **Set as Organization Admin**. 3. Confirm the role in the people list has changed to **Organization Admin**. 4. Have the member sign out and sign in again, and confirm **People** appears in the left navigation. Role or organization changes only take effect for new sessions. After a role change, the system signs out the affected organization sub-user by default; the user must sign in again before the new permissions and left navigation entries load. The organization user is the highest-privilege role within an organization. It can revoke any organization sub-user's admin permission and deny any organization sub-user access to the organization. An organization admin cannot revoke their own admin permission or deny themselves access; when either operation is needed, the organization user performs it on the People page. | Operation | Description | | --- | --- | | View people | View member email, role, status, last sign-in time, and creation time. | | Deactivate a person | After an organization sub-user is deactivated, the user can no longer perform any operations or sign in. | | Restore a person | Restore the sign-in permission of a deactivated member. | | Reset password | Generate a new temporary password for a member who cannot sign in. | | Manage model permissions | Adjust the model scope available to members according to organization rules. | | Set admin | Grant or revoke organization admin permission. | After signing in, organization sub-users do not see **Add Sub-account** or the people management entry; organization accounts and organization admins can deactivate, reset passwords, and adjust roles in the people list. After an organization sub-user is deactivated, the user can no longer perform any operations or sign in. After restoration, the user can sign in again; role changes sign the user out by default, and the user must sign in again to use the updated permissions. ## API Keys and Usage Go to [UniGateway API Keys](https://unigateway.ai/dashboard/api-keys) to create or obtain API Keys. - An API Key belongs to the account that created it. - Organization sub-users can create their own API Keys; the API Key belongs to the organization sub-user. - Organization accounts and organization admins can change the permissions of API Keys within the organization, and organization sub-users cannot bypass the permission scope set by the organization. - Organization sub-users can set model rules for the API Keys they create; organization accounts, organization admins, and migrated legacy user main accounts can set model rules for all API Keys in the organization. - When an organization admin creates an API Key, the API Key belongs to the organization admin. Later revoking the user's organization admin role does not change the ownership of that API Key. - API Key permission changes only affect subsequent requests; historical usage records retain their original organization and member attribution. ## Passwords, Email, and Security Rules - The temporary password is shown only once; do not write it into code, logs, or public documentation. - Send temporary passwords through a company password manager or another controlled channel. - Members must set a permanent password after their first sign-in; after a password reset, the previous password immediately becomes invalid. - After an organization account or organization admin resets a sub-user's password, the sub-user must sign in again with the latest temporary password and set a new permanent password on that first sign-in. - Do not re-register or re-add the same email. When a legacy user migrates, adding a person through **Add Sub-account** triggers default organization creation; after migration, legacy users continue to use the main account entry. - The same organization sub-user cannot be added to multiple organizations. - Deactivate members who are no longer needed in a timely manner, and revoke API Key access they no longer need. ## Console Entries | Task | Entry | | --- | --- | | Main account registration or sign-in | [UniGateway sign-in page](https://unigateway.ai/login) | | Sub-account sign-in | The **Sub-account** entry on the [UniGateway sign-in page](https://unigateway.ai/login) | | Organization overview | [UniGateway console](https://unigateway.ai/dashboard) | | People management | [Organization People](https://unigateway.ai/dashboard/people) | | API Keys | [UniGateway API Keys](https://unigateway.ai/dashboard/api-keys) | | Usage details | The **Usage Details** page in the console | ## FAQ ### Q: A sub-user cannot sign in. How do I handle this? Check the organization ID, sign-in email, temporary password, and account status. Confirm **Sub-account** is selected on the sign-in page, not **Main account**. If the temporary password has been used or expired, have an organization account or organization admin regenerate it on the People page, then the user signs out and signs in again. ### Q: Who can top up for an organization? Both legacy user main accounts and organization accounts can top up, and both operate after signing in to the console through the **Main account** entry. A legacy user main account can top up its own independent account; an organization account can top up its organization. Organization admins and organization sub-users cannot top up in their accounts. ### Q: How does a legacy user migrate to an organization user? The legacy user signs in to the console by selecting **Main account** on the [UniGateway sign-in page](https://unigateway.ai/login), clicks **Add Sub-account** on the **People** page, and submits the person's information. When the first person is added, the system also creates the default organization and migrates the legacy user to an organization account. After migration, sign out and sign in again through the **Main account** entry. Legacy users do not use the **Sub-account** entry and do not need to enter an organization ID. ### Q: After an organization admin changes a member's role, why is "People" still not visible? Confirm the role in the people list shows **Organization Admin**. Role changes only take effect in new sessions, so the member must sign out and sign in again; if it still does not appear, refresh the console and confirm the correct organization ID was used to sign in. # UniGateway Multi-Factor Authentication (MFA) > Category: Getting Started | Last updated: 2026-08-20 Enable, use, and manage MFA for UniGateway sub-accounts with TOTP authenticator apps and one-time recovery codes. # UniGateway Multi-Factor Authentication (MFA) Multi-factor authentication (MFA) adds a time-based one-time code generated by an authenticator app on top of the account password, reducing the risk of unauthorized account access after a password leak. After MFA is enabled, a sub-account must provide an authenticator code or a recovery code in addition to the organization ID, sign-in email, and password before entering the console. This page explains MFA setup, sign-in, and maintenance for the current sub-account. The main account and sub-account are separate sign-in entries; for sub-account creation and first sign-in, see the organization users and sub-users documentation. ## Verification Methods and Preparation UniGateway MFA uses time-based one-time passwords (TOTP). During setup or sign-in, use one of the following methods to complete verification: | Verification method | Use case | Input | | --- | --- | --- | | Authenticator app | Daily use, sign-in, and security settings | The 6-digit code currently shown in the app | | Recovery code | Sign-in when the authenticator app is unavailable, or security settings verification | One unused recovery code | Before enabling MFA, prepare: - A sub-account that can sign in, and its current password. - An authenticator app that can add TOTP accounts. The setup page shows a QR code and a manual setup key; both are account verification credentials. - A location to store recovery codes offline and securely, such as an access-controlled password manager or offline secure storage. Do not screenshot, forward, or share the QR code, manual setup key, dynamic verification codes, or recovery codes. The authenticator app, recovery codes, and account password should be kept individually by the user of the same account. ## Enabling MFA 1. Sign in to the [UniGateway Dashboard](https://unigateway.ai/dashboard) with the sub-account, and go to **Settings** at the bottom of the left navigation bar. 2. In the **Two-Factor Authentication (MFA)** area, confirm the status shows "Not enabled", then click **Enable MFA**. 3. Enter the current account password and click **Start Setup**. This step confirms that the account owner is enabling MFA. 4. Add the account in the authenticator app and scan the QR code shown on the page. If the QR code cannot be scanned, choose manual entry in the authenticator app and enter the setup key shown on the page; do not save the key in chats, email, code repositories, or screenshots. 5. Enter the 6-digit code currently shown in the authenticator app and click **Complete Setup**. 6. On the recovery codes page, download or save the recovery codes one by one, and click **Done**. Each recovery code can only be used once; do not close the page before saving is complete. After setup, return to the **Settings** page and confirm the MFA status shows "Enabled". Then sign out, sign in once more, and enter a new code from the authenticator app to confirm MFA is active. ## Signing In with MFA After enabling MFA, sign in to the sub-account as follows: 1. Go to the [UniGateway sign-in page](https://unigateway.ai/login) and select the **Sub-account** entry. 2. Enter the organization ID, sign-in email, and password, then submit the sign-in. 3. On the "**Two-Factor Authentication**" page, enter the 6-digit code currently shown in the authenticator app and click **Verify and Sign In**. 4. After verification succeeds, you enter the console of the corresponding organization. The dynamic code refreshes according to the authenticator app's time period. If the code has expired, wait for the app to show a new 6-digit code and enter it again. Entering incorrect codes or recovery codes repeatedly consumes the remaining attempts shown on the page; stop guessing, check the current authenticator account, device time, and code, then retry. ## Signing In with a Recovery Code When the authenticator app is unavailable, an unused recovery code can be used to complete a single sign-in: 1. On the MFA verification page, click **Use Recovery Code**. 2. Enter a complete recovery code, then click **Verify and Sign In**. 3. After signing in, check the MFA status in **Settings** as soon as possible; if the device is lost, the authenticator is not working, or the recovery codes may have leaked, replace the authenticator or regenerate the recovery codes immediately. A recovery code can only be used once. An incorrect recovery code is rejected and consumes an attempt; use another unused recovery code or a currently valid authenticator code instead. Recovery codes are not account passwords and cannot be used for API calls or in place of an API Key. ## Managing MFA All MFA management operations are done in the **Settings** > **Two-Factor Authentication (MFA)** area of the sub-account console. Before performing any of the following operations, prepare the current account password, and the current authenticator's 6-digit code or one unused recovery code. ### Replacing the Authenticator Use **Replace Authenticator** when changing phones, migrating to another authenticator app, or suspecting the current device is no longer secure: 1. Click **Replace Authenticator**, enter the current account password and the current authenticator code or recovery code to complete identity verification. 2. Scan the new QR code with the new authenticator app; if the QR code cannot be scanned, manually add the account using the new setup key shown on the page. 3. Enter the 6-digit code generated by the new authenticator app and complete verification. 4. Sign out and sign in again, and verify the sign-in with a code generated by the new authenticator. After replacement, codes generated by the old authenticator are no longer used for MFA verification. The new QR code and setup key are only for the current replacement process and must be treated as sensitive credentials. ### Regenerating Recovery Codes Click **Regenerate Recovery Codes** when the recovery codes are used up, lost, or may have leaked: 1. Enter the current account password and the current authenticator code or recovery code to complete identity verification. 2. On the new recovery codes page, download or save the new recovery codes one by one, confirm they are saved, and click **Done**. 3. Update the protected offline storage with the new recovery codes and delete paper or electronic copies of the old ones. Regenerating invalidates the old recovery codes. Each new recovery code can still only be used once; do not record their plaintext in download filenames, notes, shared spaces, or tickets. ### Disabling MFA Click **Disable MFA** only when you have confirmed MFA is no longer needed or the account is being migrated following an established security process. Complete the identity verification required on the page and confirm the disable. After it succeeds, the MFA status on the **Settings** page changes to "Not enabled", and sub-account sign-in no longer shows the MFA verification page. Disabling MFA lowers account sign-in protection. If you only need to change devices or the recovery codes are lost, prefer **Replace Authenticator** or **Regenerate Recovery Codes** instead of disabling MFA. ## FAQ ### Q: The code in the authenticator app is always invalid. What should I do? First confirm you are signed in to the same sub-account that enabled MFA, and select the corresponding UniGateway entry in the authenticator app. Wait for the app to generate a new 6-digit code and enter it immediately; also enable automatic date and time in the device system settings to avoid local time deviation. If it still fails, sign in with an unused recovery code on the MFA page, go to **Settings** > **Two-Factor Authentication (MFA)**, use **Replace Authenticator** to rebind, then sign out and sign in again to verify. ### Q: My phone is lost or I cannot access the authenticator app. What should I do? Click **Use Recovery Code** on the MFA verification page and sign in with an unused recovery code. Immediately after signing in, click **Replace Authenticator** in **Settings** > **Two-Factor Authentication (MFA)**, complete identity verification with the current password and a recovery code, bind the new device, sign out, and sign in again with the new code. If the recovery codes are lost or used up, do not keep guessing; request assistance through the established account support channel. ### Q: A recovery code was rejected. What should I do? Check that the input is a complete, unused, and most recently generated recovery code, and avoid copying spaces or newlines. Confirm and resubmit; an incorrect recovery code consumes an attempt shown on the page. If a valid code is still available, click **Use Authenticator Code** and sign in with the current 6-digit code, then regenerate the recovery codes in **Settings**. Old recovery codes become invalid after regeneration and cannot be used. ### Q: After replacing the authenticator, the old device can still generate codes. Can they still be used to sign in? No. After the replacement process completes, codes generated by the old authenticator are no longer used for MFA verification. Sign out of the current session and verify with a 6-digit code from the new authenticator on the sign-in page; if verification fails on the new device, sign in with an unused recovery code and run **Replace Authenticator** again. ### Q: After disabling MFA, the verification page still appears. What should I do? First confirm in the **Settings** page of the signed-in session that the status is "Not enabled". Then sign out of all current sessions, reopen the [UniGateway sign-in page](https://unigateway.ai/login), and start a new sign-in with the sub-account's organization ID, sign-in email, and password. If the new sign-in still shows the MFA page, stop entering codes repeatedly and request assistance through the established account support channel. # UniGateway Organization Security and Quota Notifications > Category: Getting Started | Last updated: 2026-08-20 Understand daily consumption threshold emails and security emails in UniGateway organizations, including quota alerts, API Key events, and password changes. # UniGateway Organization Security and Quota Notifications This page explains the daily consumption threshold notifications and security emails in a UniGateway organization. Notifications help organization admins handle sub-user quota usage and account security changes in a timely manner. ## Applicable Scope This page applies to UniGateway accounts with organization management enabled. Quota notifications target organization sub-users with a daily consumption threshold set; security emails cover API Key, access policy, and password changes within the organization. Organization admins are the recipients of organization security and quota event notifications. Password reset and password change notifications are sent to the email of the user who performed the password operation. ## Daily Consumption Threshold Notifications The system calculates the day's consumption ratio based on the daily consumption threshold set by the organization sub-user, and sends email notifications when the following thresholds are reached. | Consumption ratio | System behavior | Notification recipient | | --- | --- | --- | | `80%` | Sends a quota warning email. The sub-user's API Key can still be used for calls. | Organization admin | | `100%` or above | Sends a quota alert email and disables the API Key of the sub-user that reached the threshold. | Organization admin | After reaching `100%`, the affected API Key cannot be used for subsequent requests. Organization admins should first verify the sub-user's usage, threshold configuration, and business needs, then handle it according to the organization's quota management process. If no threshold is set, the threshold notifications and automatic disabling rules in this section do not apply. ### Handling a Quota Alert 1. Sign in to the [UniGateway console](https://unigateway.ai/dashboard), go to [Organization People](https://unigateway.ai/dashboard/people), and confirm the sub-user in the alert belongs to the current organization. 2. In the console, verify the daily consumption threshold and current day's usage of the sub-user's related API Keys, and confirm whether there is abnormal usage or an expected business peak. 3. If continued use is needed, adjust the configuration according to the organization's access control and quota management requirements, then send a new test request with the sub-user's API Key to verify. 4. If abnormal usage, requests from unknown sources, or signs of key leakage are found, keep the API Key disabled, rotate the affected key, and check the IP allowlist, model allowlist, and spending limits. Go to [UniGateway API Keys](https://unigateway.ai/dashboard/api-keys) to create or obtain an API Key. Save API Keys only in server-side environment variables or a controlled key management service; after rotating or disabling a key, update the configuration of services using that key promptly. ## Security Emails The table below lists the organization security events that currently send emails. Emails do not contain raw API Key values, raw passwords, or other sensitive information that can be used directly for sign-in. | Event | Trigger condition | Email recipient | Suggested handling | | --- | --- | --- | --- | | API Key created | An organization sub-user creates an API Key. | Organization admin | Confirm the creator, purpose, and permission scope. | | API Key deleted | An organization sub-user deletes an API Key. | Organization admin | Confirm whether the deletion was expected, and check services that depend on the key. | | IP allowlist changed | An organization sub-user updates an API Key's IP allowlist. | Organization admin | Verify the added or removed IPs, CIDR ranges, and actual egress IPs. | | API Key model allowlist changed | An organization sub-user updates the model scope a Key can call. | Organization admin | Confirm the newly added models meet the organization's permission and cost requirements. | | Sub-user model permission changed | The model scope available to an organization sub-user is modified in people management. | Organization admin | Confirm the changed target, granted scope, and business impact after it takes effect. | | Admin resets sub-user password | An organization admin initiates a password reset for a sub-user. | The sub-user whose password is reset | Complete password setup with the reset link in the email; if the link is abnormal or not from the user themselves, stop and contact the organization admin. | | Password reset succeeded | A sub-user successfully completes a password reset. | The sub-user | If not performed by the user themselves, change the password immediately and contact the organization admin. | | User changed password | A user changes their password in the console. | The user | If not performed by the user themselves, change the password immediately and contact the organization admin. | ## Management Recommendations - Configure a valid email that can receive notifications for each organization admin, and check spam folders and corporate email inbound rules regularly. - After receiving an API Key or permission change email, organization admins should verify the operator, the changed target, and actual business needs; for changes that cannot be confirmed, restrict the corresponding API Key's access scope first. - Use separate API Keys for production, testing, and different services, and set appropriate quota and access policies for each, so alert sources are easier to locate. - Do not send API Keys, temporary passwords, password reset links, or other sensitive information in email replies, tickets, chats, or screenshots. ## FAQ ### Q: After daily consumption reaches `80%`, will the API Key be disabled? No. When `80%` is reached, the system sends a warning email to the organization admin, and the sub-user's API Key can still be used for calls. Check the current day's consumption and threshold in the console, and adjust the quota or restrict abnormal traffic before `100%` is reached if necessary. ### Q: Why do requests fail after daily consumption reaches `100%`? After reaching or exceeding `100%` of the daily consumption threshold, the system disables the API Key of the sub-user that reached the threshold. Organization admins should confirm the sub-user's daily consumption threshold, current day's usage, and business needs in the console; after completing quota or access policy adjustments, verify with a new request. ### Q: An organization admin did not receive security or quota emails. What should I do? First confirm the account is still an organization admin, and check the registered email, spam folder, and corporate email blocking rules. Then verify in the console whether the operation matches an event listed on this page, or whether the sub-user has a daily consumption threshold set and has reached `80%` or `100%`. After configuration or role changes, run a controlled test operation again to verify the notification. ### Q: I received a password email that was not initiated by me. What should I do? Do not open the password reset link in the email. Sign in to the [UniGateway console](https://unigateway.ai/dashboard) immediately and change the password, and notify the organization admin to review the people management records and recent security changes. If you cannot sign in, request assistance through the established account support channel. # Usage Plans & Pricing > Category: Getting Started | Last updated: 2026-08-09 Pay As You Go vs Subscription, plan comparison, billing, quotas, top-up, invoices, and FAQ. # Usage Plans & Pricing Understand how billing works for the Pay As You Go model. ## Pay As You Go ### How It Works 1. Top up your balance in **Settings → Billing** 2. API calls consume tokens and deduct from your balance in real time 3. Each model has a per-token price — see the [Models page](https://unigateway.ai/models) for current rates 4. When your balance reaches zero, API calls return `402` ### Advantages - No rate limits — handle any concurrency level - Per-token billing — pay only for what you use - Access to all models on the platform - Suitable for production and commercial workloads ### Top Up - Supported payment methods: credit card (Stripe), Alipay - Minimum top-up amount: $5 - Balance does not expire ### Monitor Balance Check your balance via the console or the Platform API: ```bash curl https://api.unigateway.ai/v1/management/balance \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY (management key type)" ``` ## Billing ### Token Pricing Each model has a per-million-token rate split into input and output tokens. Visit the [Models page](https://unigateway.ai/models) for live pricing. ### Invoice Download Go to **Settings → Billing → Invoices** to download past invoices. ### Billing Alerts Configure low-balance alerts in **Settings → Notifications**. Recommended thresholds: | Alert | Threshold | |---|---| | Low balance warning | 20% of last top-up | | Critical balance warning | 5% of last top-up | ## FAQ **Q: What happens when my Pay As You Go balance hits zero?** A: API calls return `402 insufficient_credit`. Top up to resume service. **Q: Are there free models?** A: Some models offer a free tier with rate limits. Check the [Models page](https://unigateway.ai/models) for details. **Q: How do I check my current usage?** A: Use the [Observability](/docs/usage-analytics) section in the console, or query the Platform API. # Authentication > Category: Getting Started | Last updated: 2026-08-09 How to send API keys, set headers, and avoid common authentication mistakes. # Authentication All API requests require an API key in the `Authorization` header. ## Auth Header ```http Authorization: Bearer ``` ## Verify ```bash curl https://api.unigateway.ai/v1/models \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` A model list response means authentication is working. > Keep API keys in server-side environment variables. Do not expose them in front-end code or public repositories. ## JSON POST Requests ## Code examples ### curl ```curl curl https://api.unigateway.ai/v1/models \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` ### python ```python from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.unigateway.ai/v1", ) ``` ### typescript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1", }); ``` POST requests also require the `Content-Type` header: ```bash curl https://api.unigateway.ai/v1/chat/completions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "Hello"}] }' ``` ## Common Errors | Status | Cause | Resolution | |---|---|---| | `401` | Key missing, invalid, or expired | Check `Authorization` header format | | `403` | Key exists but lacks permission | Check account permissions | | `429` | Rate limit or quota exceeded | Add backoff and retry logic | ## Troubleshooting 1. `Authorization` header must be exactly `Bearer ` 2. Base URL must be `https://api.unigateway.ai/v1` 3. `model` value must come from `GET /v1/models` ## Key Management - Use separate keys for test and production environments - Rotate keys if leakage is suspected ## API Reference # OpenAI Compatible API > Category: API Reference | Last updated: 2026-08-09 Use OpenAI-compatible endpoints through UniGateway for chat, responses, embeddings, model discovery, and audio. # OpenAI Compatible API Use OpenAI-compatible endpoints through UniGateway with OpenAI SDKs or direct HTTP requests. ## Base URL ```text https://api.unigateway.ai/v1 ``` ## API reference | API | Endpoint | Documentation | |---|---|---| | Chat Completions | `POST /chat/completions` | [Chat Completions](/docs/chat-completions) | | Responses | `POST /responses` | [Responses](/docs/openai-responses-api) | | Embeddings | `POST /embeddings` | [Embeddings](/docs/embeddings) | | Models | `GET /models` | [Models](/docs/models) | | Audio | `POST /audio/transcriptions` and `POST /audio/translations` | [Audio](/docs/audio) | ## Authentication Send the UniGateway API Key as a Bearer token: ```http Authorization: Bearer ``` Use an exact model ID returned by `GET /v1/models`. # Chat Completions > Category: API Reference | Last updated: 2026-08-09 Send text prompts through UniGateway's unified chat completions API across mainstream model families. # Chat Completions Text generation and multi-turn conversation through the OpenAI-compatible chat completions API. ## Endpoint | Item | Value | |---|---| | Method | `POST` | | Path | `/v1/chat/completions` | | Base URL | `https://api.unigateway.ai/v1` | | Auth | `Authorization: Bearer $UNIGATEWAY_API_KEY` | | Content-Type | `application/json` | ## Minimal Request ## Code examples ### curl ```curl curl https://api.unigateway.ai/v1/chat/completions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [ {"role": "user", "content": "Summarize the benefits of a unified AI gateway in 3 bullets."} ], "temperature": 0.2 }' ``` ### python ```python from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.unigateway.ai/v1", ) resp = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Summarize the benefits of a unified AI gateway in 3 bullets."}], temperature=0.2, ) print(resp.choices[0].message.content) ``` ### typescript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1", }); const resp = await client.chat.completions.create({ model: "gpt-5.4", messages: [ { role: "user", content: "Summarize the benefits of a unified AI gateway in 3 bullets." } ], temperature: 0.2, }); console.log(resp.choices[0]?.message?.content); ``` ```bash curl https://api.unigateway.ai/v1/chat/completions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [ {"role": "user", "content": "Explain AI gateway benefits in 3 bullets."} ] }' ``` ## Python (OpenAI SDK) ```python from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.unigateway.ai/v1", ) resp = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Summarize in 3 bullets."}], ) print(resp.choices[0].message.content) ``` ## TypeScript (OpenAI SDK) ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1", }); const resp = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Summarize in 3 bullets." }], }); console.log(resp.choices[0].message.content); ``` ## Parameters | Field | Type | Required | Description | |---|---|---|---| | `model` | string | Yes | Exact model ID from `GET /v1/models` | | `messages` | array | Yes | Chat message array; each element has `role` and `content` | | `temperature` | number | No | Creativity control; range varies by model | | `max_tokens` | number | No | Upper bound of generated tokens | | `stream` | boolean | No | Set `true` to enable SSE streaming | | `top_p` | number | No | Nucleus sampling parameter | | `frequency_penalty` | number | No | Reduce word repetition | | `presence_penalty` | number | No | Encourage topic diversity | | `stop` | string/array | No | Stop sequence(s) | | `user` | string | No | End-user identifier for abuse monitoring | ## Multi-Model Examples ### GPT ```bash curl https://api.unigateway.ai/v1/chat/completions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "messages": [ {"role": "system", "content": "You are a concise assistant."}, {"role": "user", "content": "Write a migration note from single-provider to UniGateway."} ], "temperature": 0.3 }' ``` ### Claude (via OpenAI-compatible) ```bash curl https://api.unigateway.ai/v1/chat/completions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "messages": [ {"role": "user", "content": "Explain quantum computing in one sentence."} ], "max_tokens": 200 }' ``` ### Gemini (via OpenAI-compatible) ```bash curl https://api.unigateway.ai/v1/chat/completions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gemini-3-pro-preview", "messages": [ {"role": "user", "content": "Explain AI gateway routing in 3 bullets."} ] }' ``` > For Claude-native features like `thinking` blocks and `cache_control`, use `POST /v1/messages`. For Gemini-native features like `responseModalities` and `imageConfig`, use `POST /v1beta/models/{model}:generateContent`. ## Response ```json { "id": "chatcmpl-xxx", "object": "chat.completion", "created": 1760000000, "model": "gpt-5.4", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "An AI gateway provides unified model access, simplifies billing, and enables intelligent routing." }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 24, "completion_tokens": 18, "total_tokens": 42 } } ``` | Response field | Description | |---|---| | `choices[].message.content` | Generated text | | `choices[].finish_reason` | `stop`, `length`, etc. | | `model` | Actual model that handled the request | | `usage` | Token consumption | ## Errors | Status | Cause | Resolution | |---|---|---| | `400` | Invalid request body | Check JSON structure and parameter types | | `401` | Invalid or missing API key | Verify `Authorization` header | | `404` | Model ID not found or not in plan | Re-check `GET /v1/models` | | `429` | Rate limit exceeded | Add backoff and retry | | `5xx` | Server or upstream error | Retry with exponential backoff; switch model if persistent | # OpenAI Responses API > Category: API Reference | Last updated: 2026-08-09 Use the OpenAI Responses endpoint through UniGateway for stateful agent interactions with built-in tools. # OpenAI Responses API Use OpenAI's stateful Responses API through UniGateway for agent-oriented workflows, built-in tools, and server-side conversation state. ## Overview The **Responses API** is OpenAI's next-generation interface for agent-oriented interaction with built-in tools, structured output, and server-side state management. Key capabilities include: - **Stateful conversations** via `previous_response_id` — server retains history - **Built-in tools** — web search, code interpreter, computer use, file search - **Reasoning tokens** — model outputs reasoning before final response - **Structured output** — guaranteed schema-compliant JSON UniGateway exposes this at: - `POST /v1/responses` — create a response - `GET /v1/responses/{id}` — retrieve a response - `POST /v1/responses/{id}/input_items` — append to a conversation ## Authentication All requests require your UniGateway API key: ```http Authorization: Bearer ``` ## Supported Models Available OpenAI models on UniGateway that support the Responses API: | Model ID | Description | Input / 1M | Output / 1M | |----------|-------------|------------|-------------| | `gpt-5.4` | General purpose, balanced | $2.50 | $15.00 | | `gpt-5.4-pro` | Frontier reasoning & agentic | $30.00 | $180.00 | | `gpt-5.3-codex` | Code & reasoning specialist | $1.75 | $14.00 | Query the live model list: ```bash curl https://api.unigateway.ai/v1/models \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` ## Create a Response ## Code examples ### curl ```curl curl https://api.unigateway.ai/v1/responses \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.2", "input": "What is 15 * 27?", "tools": [{"type": "code_interpreter"}] }' ``` ### python ```python from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.unigateway.ai/v1", ) resp = client.responses.create( model="gpt-5.2", input="What is 15 * 27?", tools=[{"type": "code_interpreter"}], ) print(resp.output_text) ``` ### typescript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1", }); const resp = await client.responses.create({ model: "gpt-5.2", input: "What is 15 * 27?", tools: [{ type: "code_interpreter" }], }); console.log(resp.output_text); ``` ### Basic Request ```bash curl https://api.unigateway.ai/v1/responses \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "input": "Explain quantum computing in one paragraph." }' ``` ### With Conversation History ```bash curl https://api.unigateway.ai/v1/responses \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "input": [ {"role": "user", "content": [{"type": "text", "text": "What is 2+2?"}]}, {"role": "assistant", "content": [{"type": "text", "text": "4"}]}, {"role": "user", "content": [{"type": "text", "text": "Multiply that by 10."}]} ] }' ``` ### With State (Stateful Conversations) ```bash curl https://api.unigateway.ai/v1/responses \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "input": "Multiply that by 10.", "previous_response_id": "resp_abc123" }' ``` When `previous_response_id` is set, the server manages the conversation history automatically. If omitted, state is client-managed. ## Request Parameters | Parameter | Type | Required | Description | |---|---|---|---| | `model` | string | Yes | Model ID from `GET /v1/models` | | `input` | string / array | Yes | User message text or array of message items | | `previous_response_id` | string | No | Server-managed conversation state | | `store` | boolean | No | Save response server-side; default `true` | | `tools` | array | No | Built-in tools to enable | | `tool_choice` | string/object | No | `"auto"`, `"required"`, `"none"`, or specific tool | | `instructions` | string | No | System-level instruction | | `temperature` | number | No | Sampling temperature, e.g. `0.2` | | `max_tokens` | number | No | Upper bound for output tokens | | `top_p` | number | No | Nucleus sampling | | `response_format` | object | No | Structured output schema | | `reasoning` | object | No | `reasoning_effort` control | | `parallel_tool_calls` | boolean | No | Allow multiple tool calls per turn | | `metadata` | object | No | Custom key-value pairs | ### Input Array Format Each item in the `input` array: ```json { "role": "user", // or "assistant", "system", "developer" "content": [ {"type": "text", "text": "..."}, {"type": "image_url", "image_url": {"url": "..."}} ] } ``` ## Response Format ```json { "id": "resp_abc123", "object": "response", "status": "completed", "model": "gpt-5.4", "output": [ { "type": "message", "role": "assistant", "content": [ {"type": "text", "text": "Quantum computing harnesses..."} ] } ], "usage": { "input_tokens": 12, "output_tokens": 89, "total_tokens": 101 } } ``` ### Response Fields | Field | Description | |---|---| | `id` | Response ID; use as `previous_response_id` for next turn | | `status` | `"completed"`, `"in_progress"`, `"failed"`, `"cancelled"` | | `output` | Array of output items (text, tool calls, reasoning) | | `output[].type` | `"message"`, `"reasoning"`, `"tool_call"` | ### Output Item Types | Type | Description | |---|---| | `message` | Assistant message with `role` and `content` array | | `reasoning` | Model reasoning tokens (not shown to end user) | | `tool_call` | Tool call request with `call_id`, `type`, `arguments` | ## Built-in Tools Enable by adding to the `tools` array. ### Web Search ```json { "tools": [ {"type": "web_search_preview"} ] } ``` ### Code Interpreter ```json { "tools": [ {"type": "code_interpreter"} ] } ``` ### Computer Use ```json { "tools": [ { "type": "computer_use_preview", "environment": "browser" } ] } ``` ### File Search ```json { "tools": [ {"type": "file_search"} ] } ``` > **Note:** Built-in tool availability varies by model. Verify with a test request. ## Tool Calling Flow When a tool is called, the response contains `output` items with `type: "tool_call"`: ```json { "output": [ { "type": "tool_call", "call_id": "call_xyz", "type": "web_search_preview", "arguments": "{\"query\":\"latest OpenAI model release 2026\"}" } ] } ``` Send the tool result back: ```bash curl https://api.unigateway.ai/v1/responses \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "previous_response_id": "resp_abc123", "input": [ { "type": "tool_result", "call_id": "call_xyz", "output": "OpenAI announced GPT-5.4 Pro on April 15, 2026..." } ] }' ``` ## Streaming Set `stream: true` for Server-Sent Events (SSE). ```bash curl https://api.unigateway.ai/v1/responses \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -N \ -d '{ "model": "gpt-5.4", "stream": true, "input": "Write a haiku about AI." }' ``` SSE events: - `response.created` — response object created - `response.in_progress` — generation started - `response.output_item.added` — new output item - `response.output_item.delta` — incremental content - `response.completed` — full response ready ## Structured Output Guaranteed schema-compliant JSON: ```bash curl https://api.unigateway.ai/v1/responses \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4", "input": "Give me details about a fictional AI startup.", "response_format": { "type": "json_schema", "json_schema": { "name": "startup", "schema": { "type": "object", "properties": { "name": {"type": "string"}, "founded": {"type": "integer"}, "valuation": {"type": "number"} }, "required": ["name", "founded", "valuation"] } } } }' ``` ## Reasoning Control Control reasoning depth (available on reasoning-capable models): ```json { "model": "gpt-5.4-pro", "input": "Solve x² + 5x + 6 = 0", "reasoning": { "reasoning_effort": "high" } } ``` | `reasoning_effort` | Behavior | |---|---| | `low` | Minimal reasoning, faster | | `medium` | Balanced | | `high` | Deep reasoning, more tokens | ## Error Handling | Status | Meaning | Action | |---|---|---| | 400 | Invalid request body | Fix JSON schema | | 401 | Authentication failed | Check API key | | 404 | Model not found | Verify from `GET /v1/models` | | 429 | Rate limited | Exponential backoff, then fallback | | 5xx | Server error | Retry with backoff, then switch model | ## SDK Usage ### Python ```python from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.unigateway.ai/v1", ) # Stateless resp = client.responses.create( model="gpt-5.4", input="What is the capital of France?" ) print(resp.output[0].content[0].text) # Stateful follow-up follow_up = client.responses.create( model="gpt-5.4", input="What is its population?", previous_response_id=resp.id ) print(follow_up.output[0].content[0].text) ``` ### TypeScript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1", }); const resp = await client.responses.create({ model: "gpt-5.4", input: "What is the capital of France?", }); console.log(resp.output[0]?.content[0]?.text); // Stateful const followUp = await client.responses.create({ model: "gpt-5.4", input: "What is its population?", previous_response_id: resp.id, }); console.log(followUp.output[0]?.content[0]?.text); ``` ## Migration from Chat Completions | Chat Completions | Responses API | |---|---| | `messages` array | `input` string or array | | `system` role | `instructions` top-level field | | No server state | `previous_response_id` for stateful chains | | `function_call` in choices | `tool_call` in `output` array | | `choices[0].message.content` | `output[0].content[0].text` | > **Recommendation:** Use Chat Completions for simple, stateless text generation. Use Responses API for agentic workflows, built-in tools, and stateful multi-turn conversations that benefit from server-side history management. # Embeddings > Category: API Reference | Last updated: 2026-08-09 Generate vector embeddings for text inputs with supported embedding models. # Embeddings Generate vector embeddings for text inputs through UniGateway. ## Endpoint | Item | Value | |---|---| | Method | `POST` | | Path | `/v1/embeddings` | | Base URL | `https://api.unigateway.ai/v1` | | Auth | `Authorization: Bearer $UNIGATEWAY_API_KEY` | | Content-Type | `application/json` | ## Minimal Request ## Code examples ### curl ```curl curl https://api.unigateway.ai/v1/embeddings \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "text-embedding-3-small", "input": "An AI gateway unifies access to multiple model providers." }' ``` ### python ```python from openai import OpenAI client = OpenAI(api_key="", base_url="https://api.unigateway.ai/v1") resp = client.embeddings.create(model="text-embedding-3-small", input=["Test"]) print(len(resp.data[0].embedding)) ``` ### typescript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1" }); const resp = await client.embeddings.create({ model: "text-embedding-3-small", input: ["Test"] }); console.log(resp.data[0].embedding.length); ``` ```bash curl https://api.unigateway.ai/v1/embeddings \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "text-embedding-3-small", "input": "An AI gateway unifies access to multiple model providers." }' ``` ## Python ```python from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.unigateway.ai/v1", ) resp = client.embeddings.create( model="text-embedding-3-small", input=["An AI gateway unifies access to multiple model providers."], ) print(f"Dimensions: {len(resp.data[0].embedding)}") ``` ## TypeScript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1", }); const resp = await client.embeddings.create({ model: "text-embedding-3-small", input: ["An AI gateway unifies access to multiple model providers."], }); console.log(`Dimensions: ${resp.data[0].embedding.length}`); ``` ## Parameters | Field | Type | Required | Description | |---|---|---|---| | `model` | string | Yes | Embedding model ID from `GET /v1/models` | | `input` | string/array | Yes | Text to embed; string for single, array for batch | | `dimensions` | number | No | Requested output dimensions (model-dependent) | | `encoding_format` | string | No | `float` (default) or `base64` | ## Response ```json { "object": "list", "model": "text-embedding-3-small", "data": [ { "object": "embedding", "index": 0, "embedding": [0.0123, -0.0456, ...] } ], "usage": { "prompt_tokens": 12, "total_tokens": 12 } } ``` | Response field | Description | |---|---| | `data[].embedding` | Float array of embedding values | | `data[].index` | Position in the input batch | | `usage.prompt_tokens` | Input token count | ## Finding Embedding Models Use `GET /v1/models` and filter by `supported_endpoint_types` containing embeddings-related hints. Example model: `text-embedding-3-small`. Always verify the model exists and returns embeddings with a real request before production use. ## LangChain Integration ```python from langchain_openai import OpenAIEmbeddings embeddings = OpenAIEmbeddings( model="text-embedding-3-small", base_url="https://api.unigateway.ai/v1", api_key="", ) result = embeddings.embed_query("Test text") print(f"Dimensions: {len(result)}") ``` ## Common Failures | Status | Cause | Resolution | |---|---|---| | `400` | Invalid model or input | Check `model` ID and `input` type | | `401` | Invalid API key | Verify `Authorization` header | | `404` | Model not in plan | Confirm model via `GET /v1/models` | | `413` | Input too large | Reduce text length or batch size | | `429` | Rate limit exceeded | Add backoff and retry | # Models > Category: API Reference | Last updated: 2026-08-09 List available models, inspect endpoint hints, and copy requestable API model IDs. # Models Query available models. This endpoint is the single source of truth for model IDs. - Method: `GET` - Path: `/v1/models` - URL: `https://api.unigateway.ai/v1/models` ## Query ## Code examples ### curl ```curl curl https://api.unigateway.ai/v1/models \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` ### python ```python from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.unigateway.ai/v1", ) models = client.models.list() for item in models.data: print(item.id) ``` ### typescript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1", }); const models = await client.models.list(); for (const item of models.data) { console.log(item.id); } ``` ```bash curl https://api.unigateway.ai/v1/models \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` ## Response ```json { "success": true, "object": "list", "data": [ { "id": "gpt-5.4", "object": "model", "created": 1760000000, "owned_by": "custom", "supported_endpoint_types": ["chat_completions"] }, { "id": "gemini-3-pro-image-preview", "object": "model", "created": 1760000000, "owned_by": "vertex-ai", "supported_endpoint_types": ["gemini"] } ] } ``` The `id` field is the model ID used in requests. Important fields: | Field | Description | |---|---| | `id` | Exact request model ID. Copy this value into API requests. | | `owned_by` | Provider or upstream family. | | `supported_endpoint_types` | Endpoint family hints, such as `chat_completions`, `gemini`, `images`, `embeddings`, or `rerank`. | Model availability is account-specific. Use the live response, not external screenshots or stale examples. ## Scenario-Based Selection | Scenario | Recommended family | Note | |---|---|---| | General conversation | GPT / Claude | Prefer stable, non-preview IDs | | Production traffic | GPT / Claude / Gemini mid-tier | Keep request shape conservative | | Low-latency | Faster variants in your account | Validate quality before full traffic | | Multilingual | GPT / Claude / Gemini | Re-test prompts after switching families | | Embeddings / rerank | Endpoint-specific models | Confirm endpoint support first | | Video generation | Separate video surfaces | Do not share models with chat | ## Fallback Chain Fetch the model list at startup or on a short cache interval. Pin IDs by use case and configure cross-family fallback. Example chain: 1. `gpt-5.4` 2. `claude-sonnet-4-6` 3. `gemini-3-pro-preview` Model availability changes over time. Verify capabilities with a real request, not just the model name. ## Display Names vs API IDs The model library can show product names or nicknames for readability. Always use the API `id` field for requests. Example: Nano Banana Pro is the product/display name, while `gemini-3-pro-image-preview` is the requestable API model ID. # Audio > Category: API Reference | Last updated: 2026-08-09 Transcribe audio and translate audio to English with verified OpenAI-compatible audio endpoints. # Audio Speech transcription and translation through OpenAI-compatible audio endpoints. This page covers the verified audio endpoints currently available in the production model catalog. ## Available Endpoints | Task | Method | Path | Model example | |---|---|---|---| | Transcribe audio to text | `POST` | `/v1/audio/transcriptions` | `whisper-1` | | Translate audio to English | `POST` | `/v1/audio/translations` | `whisper-1` | Confirm model availability with `GET /v1/models` before production use. ## Transcription ## Code examples ### curl ```curl curl https://api.unigateway.ai/v1/audio/transcriptions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -F model="whisper-1" \ -F file=@/path/to/audio.mp3 ``` ### python ```python from openai import OpenAI client = OpenAI(api_key="", base_url="https://api.unigateway.ai/v1") with open("audio.mp3", "rb") as f: resp = client.audio.transcriptions.create(model="whisper-1", file=f) print(resp.text) ``` ### typescript ```typescript import OpenAI from "openai"; import fs from "fs"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1" }); const resp = await client.audio.transcriptions.create({ model: "whisper-1", file: fs.createReadStream("audio.mp3") }); console.log(resp.text); ``` Transcribe audio to text. | Item | Value | |---|---| | Method | `POST` | | Path | `/v1/audio/transcriptions` | | URL | `https://api.unigateway.ai/v1/audio/transcriptions` | | Auth | `Authorization: Bearer $UNIGATEWAY_API_KEY` | | Content-Type | `multipart/form-data` | ### Request ```bash curl https://api.unigateway.ai/v1/audio/transcriptions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -F model="whisper-1" \ -F file=@/path/to/audio.mp3 ``` ### Response ```json { "text": "Hello, this is a test of the audio transcription service." } ``` ### Parameters | Field | Type | Required | Description | |---|---|---|---| | `model` | string | Yes | Model ID, e.g. `whisper-1` | | `file` | file | Yes | Audio file to transcribe | | `language` | string | No | Language code, e.g. `en`, `zh` | | `response_format` | string | No | Output format: `text`, `json`, `verbose_json`, `srt`, `vtt`, or `tsv` | | `temperature` | number | No | Sampling temperature, range `0` to `1` | ## Translation Translate audio directly to English. | Item | Value | |---|---| | Method | `POST` | | Path | `/v1/audio/translations` | | URL | `https://api.unigateway.ai/v1/audio/translations` | | Auth | `Authorization: Bearer $UNIGATEWAY_API_KEY` | | Content-Type | `multipart/form-data` | ### Request ```bash curl https://api.unigateway.ai/v1/audio/translations \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -F model="whisper-1" \ -F file=@/path/to/audio.mp3 ``` ### Response ```json { "text": "Hello, this is a test of the audio transcription service." } ``` ### Parameters | Field | Type | Required | Description | |---|---|---|---| | `model` | string | Yes | Model ID, e.g. `whisper-1` | | `file` | file | Yes | Audio file to translate | | `response_format` | string | No | Output format: `text`, `json`, `verbose_json`, `srt`, `vtt`, or `tsv` | | `temperature` | number | No | Sampling temperature, range `0` to `1` | ## Python ```python from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.unigateway.ai/v1", ) with open("audio.mp3", "rb") as f: transcription = client.audio.transcriptions.create( model="whisper-1", file=f, ) print(transcription.text) with open("audio.mp3", "rb") as f: translation = client.audio.translations.create( model="whisper-1", file=f, ) print(translation.text) ``` ## TypeScript ```typescript import OpenAI from "openai"; import fs from "fs"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1", }); const transcription = await client.audio.transcriptions.create({ model: "whisper-1", file: fs.createReadStream("audio.mp3"), }); console.log(transcription.text); const translation = await client.audio.translations.create({ model: "whisper-1", file: fs.createReadStream("audio.mp3"), }); console.log(translation.text); ``` ## Common Failures | Status | Cause | Resolution | |---|---|---| | `400` | Invalid file, unsupported format, or invalid parameter | Retry with a short MP3, WAV, M4A, or WebM file | | `401` | Invalid or missing API key | Verify the `Authorization` header | | `404` | Model not available | Confirm `whisper-1` via `GET /v1/models` | | `413` | File too large | Compress or split the audio file | | `429` | Rate limit exceeded | Back off and retry | # UniGateway Kimi K3 Model Support > Category: API Reference | Last updated: 2026-08-13 Call Kimi K3 through UniGateway's OpenAI-compatible Chat Completions API — reasoning, streaming, structured output, Partial Mode, tool calling, images, and context caching. # UniGateway Kimi K3 Model Support This page explains how to call the Kimi K3 model through UniGateway. Kimi K3's model ID is `kimi-k3`, and it uses UniGateway's OpenAI-compatible Chat Completions API. Kimi K3 always enables thinking mode and supports text and image input, streaming output, structured output, Partial Mode, context caching, and tool calling. All of these capabilities are available through this interface. Model capabilities, pricing, availability, and upstream status are subject to the [UniGateway model library](https://unigateway.ai/models) and the live API response. ## Prerequisites Go to [UniGateway API Keys](https://unigateway.ai/dashboard/api-keys) to create or obtain an API Key. Save the API Key as a server-side environment variable: ```bash export UNIGATEWAY_API_KEY="" export SESSION_ID="" ``` Windows PowerShell: ```powershell $env:UNIGATEWAY_API_KEY = "" $env:SESSION_ID = "" ``` Confirm that the API Key can use `kimi-k3`: ```bash curl https://api.unigateway.ai/v1/models \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` The exact ID `kimi-k3` must exist in the model list. Model availability is affected by account permissions, model allowlists, and upstream status. ## API Information | Item | Value | | --- | --- | | Method | `POST` | | Path | `/v1/chat/completions` | | Base URL | `https://api.unigateway.ai/v1` | | Authentication | `Authorization: Bearer $UNIGATEWAY_API_KEY` | | Content-Type | `application/json` | | Session affinity | `X-Session-Id: `, optional but recommended | UniGateway does not store Chat Completions message history. For multi-turn conversations and tool calling, the application must save the history itself and pass it in the next request. ### X-Session-Id To optimize cache hits and channel affinity, pass `X-Session-Id` in the Chat Completions request header. All requests of the same logical session reuse the same stable value; generate a new value when starting a new session. `X-Session-Id` should be a UUID or an application-generated random identifier. Do not include usernames, phone numbers, API Keys, access tokens, or other sensitive information. Stable reuse of this header improves channel affinity and cache hit rates. ## Basic Call ### Request Fields | Field | Type | Required | Description | | --- | --- | --- | --- | | `model` | string | Yes | Fixed to `kimi-k3`. | | `messages` | array | Yes | Pass system, user, assistant, or tool messages in order. | | `reasoning_effort` | string | No | `low`, `high`, or `max`; default is `max`. K3 always enables thinking. | | `max_completion_tokens` | integer | No | Default `131072`, maximum `1048576`. | | `stream` | boolean | No | Set to `true` for streaming responses. | | `tools` | array | No | Function tool definitions. | | `tool_choice` | string/object | No | Supports `auto`, `none`, `required`, or a specific function. | | `response_format` | object | No | Supports `text`, `json_object`, and `json_schema`. | Kimi K3 uses fixed values for `temperature`, `top_p`, `n`, `presence_penalty`, and `frequency_penalty`. Do not pass these parameters explicitly. ### cURL Example ```bash curl https://api.unigateway.ai/v1/chat/completions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "X-Session-Id: $SESSION_ID" \ -H "Content-Type: application/json" \ -d '{ "model": "kimi-k3", "messages": [ {"role": "user", "content": "Describe Kimi K3 in one sentence."} ] }' ``` ### Python Example ```bash pip install --upgrade "openai>=1.0" ``` ```python import os from openai import OpenAI client = OpenAI( api_key=os.environ["UNIGATEWAY_API_KEY"], base_url="https://api.unigateway.ai/v1", ) response = client.chat.completions.create( model="kimi-k3", messages=[{"role": "user", "content": "Describe Kimi K3 in one sentence."}], extra_headers={"X-Session-Id": ""}, ) print(response.choices[0].message.content) ``` ## Reasoning and Streaming Output Kimi K3 always enables thinking mode. Use the top-level `reasoning_effort` in the request to select `low`, `high`, or `max`. ```python response = client.chat.completions.create( model="kimi-k3", reasoning_effort="high", messages=[{"role": "user", "content": "Prove that the square root of 2 is irrational."}], ) print(response.choices[0].message.content) ``` Streaming responses return the reasoning delta `reasoning_content` and the answer delta `content` separately. The application should allow either field to be empty in a single delta. ```python stream = client.chat.completions.create( model="kimi-k3", messages=[{"role": "user", "content": "Explain why the sky is blue."}], stream=True, ) for chunk in stream: delta = chunk.choices[0].delta if getattr(delta, "reasoning_content", None): print(delta.reasoning_content, end="", flush=True) if delta.content: print(delta.content, end="", flush=True) ``` In multi-turn conversations, the complete assistant message returned by the API must be added to the next request as-is; do not keep only `content`. ## Model Capabilities | Capability | Support | Description | | --- | --- | --- | | Thinking and reasoning effort | Supported | `reasoning_effort=low|high|max`. | | Streaming output | Supported | Read `reasoning_content` and `content`. | | Structured output | Supported | Use `json_schema` with `strict: true`. | | Partial Mode | Supported | Set `partial: true` on the last assistant message. | | Custom tool calling | Supported | Pass `tools` at the top level of each request. | | Image input | Supported | Use a `content` array containing `image_url` and `text` objects. | | Automatic context caching | Supported | No cache ID, TTL, or extra parameters needed. | Kimi K3 has a 1M token context window; actual concurrency, RPM, TPM, TPD, and account limits are determined by the live API response. `max_completion_tokens` defaults to `131072` with a maximum of `1048576`. ## Tool Calling Declare tools in the top-level `tools` field of each request. After receiving `tool_calls`, the application executes the corresponding functions, appends the complete assistant message and the corresponding `tool` messages in order, and calls the API again. A maximum of 128 tools is supported. Available `tool_choice` values: - `auto`: default; the model decides whether to call tools. - `none`: forbids tool calling for this request. - `required`: requires at least one tool call in this turn. - `{"type":"function","function":{"name":""}}`: forces a specific function. Kimi K3 supports forcing a single function with `{"type":"function","function":{"name":"..."}}`. The function name must exactly match the definition in `tools`; the application must still execute the function and return the corresponding `tool_call_id`. ```python import json tools = [{ "type": "function", "function": { "name": "get_weather", "description": "Query the weather for a specified city.", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"], "additionalProperties": False, }, "strict": True, }, }] messages = [{"role": "user", "content": "Query the weather in Beijing."}] response = client.chat.completions.create( model="kimi-k3", messages=messages, tools=tools, tool_choice="required" ) assistant_message = response.choices[0].message messages.append(assistant_message.model_dump(exclude_none=True)) for tool_call in assistant_message.tool_calls or []: arguments = json.loads(tool_call.function.arguments) messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": json.dumps({"city": arguments["city"], "condition": "sunny"}), }) final_response = client.chat.completions.create( model="kimi-k3", messages=messages, tools=tools ) print(final_response.choices[0].message.content) ``` Put tool definitions in the top-level `tools` field of every request; for multi-turn requests, pass the complete definitions again in each turn. ## Structured Output and Partial Mode Use `response_format.type="json_schema"` with `strict: true` to constrain the final `message.content`. Parse only `message.content`; do not treat `reasoning_content` as a JSON result. ```python response = client.chat.completions.create( model="kimi-k3", messages=[{"role": "user", "content": "Extract: Li Lei is 18 years old."}], response_format={ "type": "json_schema", "json_schema": { "name": "person", "strict": True, "schema": { "type": "object", "properties": { "name": {"type": "string"}, "age": {"type": "integer"}, }, "required": ["name", "age"], "additionalProperties": False, }, }, }, ) print(response.choices[0].message.content) ``` Partial Mode continues generation from an existing assistant text prefix; the application concatenates the prefix when displaying: ```python prefix = "Conclusion: " response = client.chat.completions.create( model="kimi-k3", messages=[ {"role": "user", "content": "Explain why API compatibility matters."}, {"role": "assistant", "content": prefix, "partial": True}, ], ) print(prefix + (response.choices[0].message.content or "")) ``` ## Images and Caching Image messages use an object array for `content`. Images use `image_url`. ```python import base64 from pathlib import Path image_data = base64.b64encode(Path("architecture.png").read_bytes()).decode() response = client.chat.completions.create( model="kimi-k3", messages=[{ "role": "user", "content": [ {"type": "image_url", "image_url": { "url": f"data:image/png;base64,{image_data}" }}, {"type": "text", "text": "Explain the system components in the image."}, ], }], ) print(response.choices[0].message.content) ``` | Item | Requirement or recommendation | | --- | --- | | Image URL | Use a base64 data URL. | | Image formats | `image/jpeg`, `image/png`, `image/gif`, `image/webp`, `image/bmp`, `image/heic`, or `image/heif`. | | SVG | Pass SVG source as text. | | Request body size | No more than `100 MB`. | | Resolution | Recommended no more than 4K for images and 1080p for video. | Context caching is enabled automatically; no cache ID or TTL is needed. Reuse a stable `X-Session-Id` for the same logical session while keeping the long system prefix unchanged; when the prompt of the previous request exceeds 256 tokens, subsequent requests can attempt to hit the cache. Check the actual cache hits through `usage.prompt_tokens_details.cached_tokens`. ## Security and Billing Considerations - Vision input, reasoning tokens, and output tokens all affect usage. Limit file size, resolution, context length, and output caps on the application side. - Tool results are executed and returned by the application. Validate tool names, parameters, authorization scope, and target addresses before calling external services. - Do not write API Keys, access tokens, or other sensitive data into prompts, tool results, task logs, or error messages. - When external data is needed, the application should call an authorized service and return the result as a tool message. ## Q&A ### Q: A `kimi-k3` request returns 404. How do I handle this? Run `GET https://api.unigateway.ai/v1/models` and confirm the exact ID `kimi-k3` exists in the returned list; check the API Key's model allowlist and account permissions. After adjusting the configuration, query the model list again and request with the ID returned live. ### Q: Tool calling returns 400. How do I handle this? Check that `tools` is at the top level of the request, that the function has `name`, `description`, and JSON Schema `parameters`, and that each `tool_call_id` matches the returned tool call ID. Kimi K3 supports `auto`, `none`, `required`, and a specified single function; the function name must exactly match the tool definition. In multi-turn calls, the complete assistant message must be passed back as-is. ### Q: Images are not recognized. How do I troubleshoot? Confirm that `messages[].content` is an object array; that images use base64 data URLs; that SVG is passed as text; and that the request body is under `100 MB` and uses a supported format. Fix the issues and resend the request. ### Q: Why did the context cache not hit? Confirm that the same `X-Session-Id` is reused for the same logical session, that the long prefix of adjacent requests is exactly identical, and that the previous request's prompt exceeds 256 tokens. Do not insert dynamic timestamps, random values, or frequently changing tool definitions into the prefix; check `usage.prompt_tokens_details.cached_tokens` and re-validate. # Anthropic Compatible API > Category: API Reference | Last updated: 2026-08-09 Use the Anthropic-compatible Messages endpoint through UniGateway. # Anthropic Compatible API Use the Anthropic-compatible Messages API through UniGateway with direct HTTP requests or an Anthropic-compatible client. ## Base URL ```text https://api.unigateway.ai ``` ## API reference | API | Endpoint | Documentation | |---|---|---| | Messages | `POST /v1/messages` | [Messages](/docs/anthropic-messages-api) | ## Required headers ```http Authorization: Bearer Content-Type: application/json Anthropic-Version: 2023-06-01 ``` Use an exact Claude model ID returned by `GET /v1/models`. # Anthropic Messages API > Category: API Reference | Last updated: 2026-08-09 Use the Anthropic Messages endpoint through UniGateway for Claude-specific features like extended thinking and prompt caching. # Anthropic Messages API Access Claude's native extended capabilities through UniGateway at the Anthropic Messages API endpoint. ## Prerequisites - A UniGateway API key exported as `UNIGATEWAY_API_KEY` - Target Claude model ID confirmed available via `GET /v1/models` ## Overview - Endpoint: `POST /v1/messages` - Base URL: `https://api.unigateway.ai/v1` - Headers: - `Authorization: Bearer ` - `Content-Type: application/json` - `anthropic-version: 2023-06-01` UniGateway proxies the Anthropic Messages protocol. Claude-specific capabilities exposed through this endpoint include: - `thinking` extended reasoning blocks - `cache_control` prompt caching - Granular `stop_reason` values - PDF, image, citation, and tool use support ## Request ## Code examples ### curl ```curl curl https://api.unigateway.ai/v1/messages \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -H "Anthropic-Version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [{"role": "user", "content": "Explain quantum computing in one sentence."}] }' ``` ### python ```python import requests api_key = "" resp = requests.post( "https://api.unigateway.ai/v1/messages", headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", "Anthropic-Version": "2023-06-01", }, json={ "model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [{"role": "user", "content": "Explain quantum computing in one sentence."}], }, ) print(resp.json()["content"][0]["text"]) ``` ### typescript ```typescript const resp = await fetch("https://api.unigateway.ai/v1/messages", { method: "POST", headers: { Authorization: `Bearer ${process.env.UNIGATEWAY_API_KEY}`, "Content-Type": "application/json", "Anthropic-Version": "2023-06-01", }, body: JSON.stringify({ model: "claude-sonnet-4-6", max_tokens: 1024, messages: [{ role: "user", content: "Explain quantum computing in one sentence." }], }), }); const data = await resp.json(); console.log(data.content[0].text); ``` ### Basic request ```bash curl https://api.unigateway.ai/v1/messages \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [ {"role": "user", "content": "Explain quantum computing in one paragraph."} ] }' ``` ### Parameters | Field | Type | Required | Description | |---|---|---|---| | `model` | string | Yes | Exact model ID from `GET /v1/models` | | `messages` | array | Yes | Message objects with `role` and `content` | | `max_tokens` | number | Yes | Maximum tokens to generate | | `system` | string / array | No | System prompt; array form supports `cache_control` | | `tools` | array | No | Available tool definitions | | `tool_choice` | object / string | No | `auto`, `any`, `none`, or a specific tool | | `thinking` | object | No | Extended reasoning configuration | | `temperature` | number | No | Sampling temperature, 0–1 | | `top_p` | number | No | Nucleus sampling | | `top_k` | number | No | Top-K sampling | | `stop_sequences` | array | No | Array of stop strings | | `stream` | boolean | No | Enable SSE streaming | | `metadata` | object | No | Custom metadata | ### Message formats Single text message: ```json { "role": "user", "content": "Explain quantum computing." } ``` Multimodal content as an array: ```json { "role": "user", "content": [ {"type": "text", "text": "Describe this image."}, { "type": "image", "source": { "type": "base64", "media_type": "image/jpeg", "data": "/9j/4AAQSkZJRg..." } } ] } ``` Supported source types for images and documents: | Type | Description | |---|---| | `base64` | Base64-encoded data with `media_type` | | `url` | Publicly accessible image URL | PDF input uses the `document` type: ```json { "type": "document", "source": { "type": "base64", "media_type": "application/pdf", "data": "JVBERi0xLjQK..." } } ``` ### Extended reasoning (thinking) Enable Claude's extended reasoning: ```json { "model": "claude-sonnet-4-6", "max_tokens": 4096, "thinking": { "type": "enabled", "budget_tokens": 2048 }, "messages": [ {"role": "user", "content": "Prove that sqrt(2) is irrational."} ] } ``` | Field | Type | Description | |---|---|---| | `type` | string | `enabled` or `adaptive` | | `budget_tokens` | number | Token budget allocated to reasoning | When `thinking` is enabled, the response includes a `thinking` content block. `max_tokens` must be greater than `budget_tokens`. ### Prompt caching Add `cache_control` to messages or tool definitions to reduce repeated input costs: ```json { "role": "user", "content": [ { "type": "text", "text": "Long document or system prompt content...", "cache_control": {"type": "ephemeral"} } ] } ``` `cache_control.type` currently supports only `ephemeral`. Cache hits are reported in response `usage` as `cache_read_input_tokens` and `cache_creation_input_tokens`. ## Tool use Define tools in the `tools` array: ```json { "tools": [ { "name": "get_weather", "description": "Get current weather for a location", "input_schema": { "type": "object", "properties": { "location": { "type": "string", "description": "City name" } }, "required": ["location"] } } ] } ``` Tool choice strategies: ```json { "tool_choice": {"type": "auto"} } ``` | Strategy | Behavior | |---|---| | `auto` | Model decides whether to call a tool | | `any` | Model must call at least one tool | | `none` | Do not call tools | | `tool` | Require a specific tool by name | When returning tool results, append a `tool_result` block with `role: user`: ```json { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_01T1x1fJ34qAmk2tNTrN7Up6", "content": "22°C, sunny" } ] } ``` ## Response ```json { "id": "msg_01XgYfV9UTi2PJN", "type": "message", "role": "assistant", "model": "claude-sonnet-4-6", "content": [ { "type": "text", "text": "Quantum computing leverages superposition..." } ], "stop_reason": "end_turn", "usage": { "input_tokens": 14, "output_tokens": 128 } } ``` ### Response fields | Field | Description | |---|---| | `id` | Message ID | | `type` | Always `message` | | `role` | Always `assistant` | | `content` | Array of content blocks (`text`, `thinking`, `tool_use`) | | `stop_reason` | `end_turn`, `max_tokens`, `stop_sequence`, `tool_use` | | `usage.input_tokens` | Tokens consumed by the prompt | | `usage.output_tokens` | Tokens generated by the model | ## Streaming Enable SSE by setting `stream: true`: ```bash curl https://api.unigateway.ai/v1/messages \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello"}], "stream": true }' ``` SSE event types include `message_start`, `content_block_start`, `content_block_delta`, `content_block_stop`, `message_delta`, and `message_stop`. ## Errors | Status | Cause | Resolution | |---|---|---| | `400` | Invalid request body | Check JSON structure and parameter types | | `401` | Invalid or missing API key | Verify `Authorization` header | | `404` | Model ID not found | Re-check `GET /v1/models` | | `429` | Rate limit exceeded | Add backoff and retry | | `529` | Overloaded | Retry with exponential backoff | # Gemini Native API > Category: API Reference | Last updated: 2026-08-09 Use Gemini native generateContent through UniGateway. # Gemini Native API Use Gemini native generateContent through UniGateway for text, multimodal input, Structured Output, and Tool Calling. ## Base URL ```text https://api.unigateway.ai/v1beta ``` ## API reference | API | Endpoint | Documentation | |---|---|---| | generateContent | `POST /models/{model}:generateContent` | [generateContent](/docs/gemini-generate-content) | ## Authentication ```http Authorization: Bearer Content-Type: application/json ``` Replace `{model}` with an exact Gemini model ID returned by `GET /v1/models`. # Gemini generateContent API > Category: API Reference | Last updated: 2026-08-09 Call Gemini native generateContent through UniGateway for text generation, chat, structured output, and tool calling. # Gemini generateContent API Call Gemini's native `generateContent` interface through UniGateway for text generation, multi-turn chat, structured output, and tool calling. ## Prerequisites - A UniGateway API key stored in `UNIGATEWAY_API_KEY` - Confirm the target Gemini model is available via `GET /v1/models` ## Endpoint - Method: `POST` - Path: `/v1beta/models/{model}:generateContent` - Streaming path: `/v1beta/models/{model}:streamGenerateContent?alt=sse` - Base URL: `https://api.unigateway.ai` - Headers: - `Authorization: Bearer ` - `Content-Type: application/json` UniGateway uses Bearer Token authentication. Do not send `x-goog-api-key` or a `key=` query parameter. > This is the native Gemini format. Field names use Gemini-style `contents`, `parts`, `generationConfig`, and `systemInstruction`. If you use the OpenAI SDK, prefer `/v1/chat/completions`. For SDK integrations, first verify the endpoint with cURL or plain HTTP. Some Gemini SDK versions handle custom base URLs differently. ## Basic request ## Code examples ### curl ```curl curl "https://api.unigateway.ai/v1beta/models/gemini-3-pro-preview:generateContent" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contents": [ {"parts": [{"text": "Summarize the benefits of an AI gateway."}]} ] }' ``` ### python ```python import requests api_key = "" model = "gemini-3-pro-preview" resp = requests.post( f"https://api.unigateway.ai/v1beta/models/{model}:generateContent", headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, json={ "contents": [ {"parts": [{"text": "Summarize the benefits of an AI gateway."}]} ] }, ) print(resp.json()["candidates"][0]["content"]["parts"][0]["text"]) ``` ### typescript ```typescript const model = "gemini-3-pro-preview"; const resp = await fetch(`https://api.unigateway.ai/v1beta/models/${model}:generateContent`, { method: "POST", headers: { Authorization: `Bearer ${process.env.UNIGATEWAY_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ contents: [ { parts: [{ text: "Summarize the benefits of an AI gateway." }] }, ], }), }); const data = await resp.json(); console.log(data.candidates[0].content.parts[0].text); ``` ```bash curl "https://api.unigateway.ai/v1beta/models/gemini-3-pro-preview:generateContent" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contents": [ { "role": "user", "parts": [ {"text": "Explain the value of an AI Gateway in one paragraph."} ] } ] }' ``` ## Request body ### Minimal structure ```json { "contents": [ { "role": "user", "parts": [{"text": "Hello"}] } ] } ``` ### Parameters | Field | Type | Required | Description | |---|---|---|---| | `contents` | array | Yes | Conversation content. Send one user content for single-turn calls, or full history for multi-turn calls. | | `contents[].role` | string | No | `user` or `model`. | | `contents[].parts` | array | Yes | Message parts. Text uses `{ "text": "..." }`. | | `systemInstruction` | object | No | System instruction, usually `parts: [{"text":"..."}]`. | | `generationConfig` | object | No | Generation parameters such as temperature, topP, maxOutputTokens, responseMimeType. | | `tools` | array | No | Tool definitions such as function calling, Google Search, and code execution. | | `toolConfig` | object | No | Tool calling policy. | | `safetySettings` | array | No | Safety settings. | ## Multi-turn chat Native Gemini `generateContent` is stateless. For multi-turn chat, send the conversation history from the client: ```json { "contents": [ { "role": "user", "parts": [{"text": "What is the capital of France?"}] }, { "role": "model", "parts": [{"text": "Paris."}] }, { "role": "user", "parts": [{"text": "What is its approximate population?"}] } ] } ``` ## System instruction ```bash curl "https://api.unigateway.ai/v1beta/models/gemini-3-pro-preview:generateContent" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "systemInstruction": { "parts": [{"text": "You are a concise technical assistant for production integrations."}] }, "contents": [ {"parts": [{"text": "Give a Redis cache penetration mitigation plan."}]} ] }' ``` ## Generation config ```json { "generationConfig": { "temperature": 0.7, "topP": 0.95, "topK": 40, "maxOutputTokens": 2048, "stopSequences": ["\n\n"] }, "contents": [ {"parts": [{"text": "Write an API gateway integration checklist."}]} ] } ``` | Field | Description | |---|---| | `temperature` | Sampling randomness. For Gemini 3 models, prefer the model default first. | | `topP` | Nucleus sampling. | | `topK` | Top-K sampling. | | `maxOutputTokens` | Maximum output tokens. | | `stopSequences` | Stop sequence array. | | `thinkingConfig` | Reasoning configuration, such as `thinkingLevel`. Support depends on the model. | ## Structured output For JSON output, set `responseMimeType` and `responseJsonSchema` in `generationConfig`: ```bash curl "https://api.unigateway.ai/v1beta/models/gemini-3-pro-preview:generateContent" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contents": [ {"parts": [{"text": "Extract city and temperature from: Shanghai is 18 degrees Celsius today."}]} ], "generationConfig": { "responseMimeType": "application/json", "responseJsonSchema": { "type": "object", "properties": { "city": {"type": "string"}, "temperature_celsius": {"type": "number"} }, "required": ["city", "temperature_celsius"] } } }' ``` The text is usually returned at `candidates[0].content.parts[0].text` as a JSON string matching the schema. ## Function calling ```json { "contents": [ { "parts": [{"text": "Check today's weather in Tokyo."}] } ], "tools": [ { "functionDeclarations": [ { "name": "get_weather", "description": "Get current weather for a city", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"} }, "required": ["location"] } } ] } ] } ``` The model may return a `functionCall` part. Your application should execute the function and send a `functionResponse` with the conversation history in the next request. ## Built-in tools Native Gemini format supports some built-in tools. Availability depends on model and account permissions. ```json { "tools": [ {"googleSearch": {}}, {"codeExecution": {}} ], "contents": [ {"parts": [{"text": "Search and summarize recent AI Gateway trends."}]} ] } ``` Common tools: | Tool | Description | |---|---| | `googleSearch` | Search grounding. | | `codeExecution` | Code execution. | | `urlContext` | URL context retrieval. | | `functionDeclarations` | Client-side function calling. | ## Streaming Use the `:streamGenerateContent?alt=sse` path for SSE streaming: ```bash curl "https://api.unigateway.ai/v1beta/models/gemini-3-pro-preview:streamGenerateContent?alt=sse" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contents": [ {"parts": [{"text": "Explain SSE in three sentences."}]} ] }' ``` Parse `candidates[].content.parts[].text` incrementally from SSE events. ## Response format ```json { "candidates": [ { "content": { "role": "model", "parts": [ {"text": "An AI Gateway unifies model access, routing, and billing..."} ] }, "finishReason": "STOP", "index": 0 } ], "usageMetadata": { "promptTokenCount": 12, "candidatesTokenCount": 64, "totalTokenCount": 76 } } ``` ### Response fields | Field | Description | |---|---| | `candidates[]` | Candidate outputs. | | `candidates[].content.parts[]` | Output content parts. Text output is in the `text` field. | | `candidates[].finishReason` | Finish reason, such as `STOP`. | | `usageMetadata` | Token usage metadata. | ## Errors | Status | Cause | Resolution | |---|---|---| | `400` | Request body does not match native Gemini format | Check `contents[].parts[]`, `generationConfig`, and related fields. | | `401` | Invalid or missing API key | Verify `Authorization: Bearer ...`. | | `404` | Model does not exist or is unavailable for the account | Re-check `GET /v1/models`. | | `429` | Rate limit exceeded | Add backoff and retry. | | `5xx` | Server error | Retry with exponential backoff; switch model if needed. | ## Developer Guides # Model Selection and Fallback > Category: Developer Guides | Last updated: 2026-08-09 How to pick model IDs and define fallback chains across OpenAI, Claude, and Gemini. # Model Selection and Fallback Run stable production traffic across model families. ## Discover First, Then Pin ## Code examples ### curl ```curl curl https://api.unigateway.ai/v1/chat/completions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"gpt-5.4","messages":[{"role":"user","content":"hello"}]}' ``` ### python ```python from openai import OpenAI client = OpenAI(api_key="", base_url="https://api.unigateway.ai/v1") resp = client.chat.completions.create(model="claude-sonnet-4-6", messages=[{"role":"user","content":"hello"}]) print(resp.choices[0].message.content) ``` ### typescript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1" }); const resp = await client.chat.completions.create({ model: "gemini-3-pro-preview", messages: [{ role: "user", content: "hello" }] }); console.log(resp.choices[0]?.message?.content); ``` Read live model IDs from `GET /v1/models`, then pin IDs by use case. Model availability is account-specific; external references may differ. ## Build a Fallback Chain Example text-generation chain: 1. `gpt-5.4` 2. `claude-sonnet-4-6` 3. `gemini-3-pro-preview` Your actual chain should come from the live model list and your latency/cost targets. ## Keep Request Shape Conservative Start with the common request shape: ```json { "model": "gpt-5.4", "messages": [ { "role": "user", "content": "Summarize this in 3 bullets." } ], "temperature": 0.2 } ``` Avoid provider-specific optional fields unless you have endpoint-level validation for each fallback target. ## Routing Policy - Retry same model on transient failures (`429`, `5xx`) with exponential backoff - Switch to next model in chain after retry budget is exhausted - Log `model`, `request_id`, latency, and token usage per attempt ## Model Lifecycle Model states: `AVAILABLE`, `PREVIEW`, `DEPRECATED`, `SUNSET`, `UNAVAILABLE`. - Avoid adding new traffic to `DEPRECATED` or `SUNSET` models - Keep replacement mappings in configuration, not in application code ## Backoff Policy | Parameter | Value | |---|---| | Initial delay | `300ms` | | Multiplier | `2x` | | Max delay | `8s` | | Max attempts per model | `3` | After max attempts, route to the next fallback model. # Error Handling and Retries > Category: Developer Guides | Last updated: 2026-08-09 Practical retry and idempotency guidance for production integrations. # Error Handling and Retries Practical retry guidance for reliable integrations. ## Principles 1. Separate validation errors from transient errors 2. Retry only when the failure is temporary 3. Cap retries per model, per task, per user action 4. Log every attempt for auditability ## HTTP Status Handling ## Code examples ### curl ```curl curl https://api.unigateway.ai/v1/chat/completions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"gpt-5.4","messages":[{"role":"user","content":"retry demo"}]}' ``` ### python ```python from openai import OpenAI client = OpenAI(api_key="", base_url="https://api.unigateway.ai/v1") # implement retry with exponential backoff for 429/5xx ``` ### typescript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1" }); // retry 429/5xx with exponential backoff ``` | Status | Meaning | Action | |---|---|---| | 400 | Invalid request | Fix payload, do not retry | | 401 | Invalid or missing auth | Fix key or header | | 402 | Insufficient balance | Trigger billing workflow, pause retries | | 403 | Forbidden | Check account permissions | | 404 | Resource missing | Verify endpoint path and model ID | | 429 | Rate limited | Retry with exponential backoff and jitter | | 5xx | Server error | Retry with capped backoff, then switch to fallback model | ## Backoff Policy | Parameter | Value | |---|---| | Initial delay | `300ms` | | Multiplier | `2x` | | Max delay | `8s` | | Max attempts per model | `3` | ## Endpoint-Specific Playbooks ### Chat / Responses - Retry `429` and `5xx` on same model with capped backoff - Switch to fallback model after retries exhausted - Treat streaming and non-streaming requests separately ### Streaming - A retry creates a new request trace after a partial stream has been consumed. - Broken mid-flight: start a new request trace - Allow one reconnect only if no tokens arrived ### Model Discovery - Do not retry `404` blindly — refresh `GET /v1/models` first - Cache model lists briefly, invalidate on catalog changes ### Async Tasks - Retry create calls conservatively to avoid duplicate jobs - Poll existing task IDs before re-submitting - Use caller-side correlation IDs for idempotency ## Retry Budgets | Endpoint | Retry budget | After exhausted | |---|---|---| | `chat.completions` / `responses` | Up to 3 per model | Switch to next fallback | | Streaming chat | 1 reconnect (if no tokens arrived) | New request trace | | `GET /v1/models` | Short burst | Surface degraded state | | Async task create | 1–2 max | Query before creating another | | Async task poll | Many polls, low frequency | Stop at your timeout | ## Idempotency | Scenario | Strategy | |---|---| | Non-stream text | Safe to retry if previous attempt failed before full response | | Streaming | Partial stream = consumed output; retry creates new trace | | Async / stateful | Use caller-side idempotency keys | ## Escalation - `401`, `403`, `402` rates rising — retries will not fix account state - One family fails while another is healthy — routing or upstream-specific issue - Fallback usage spikes unexpectedly — system may appear healthy while cost drifts ## Observability Log per attempt: - Timestamp, model ID, endpoint path, status code - Retry count, latency, upstream error message - Fallback position, streaming flag, correlation ID, final outcome # Claude Prompt Caching > Category: Developer Guides | Last updated: 2026-08-09 Create and verify temporary Claude prompt caches through UniGateway, including breakpoints, TTLs, minimum lengths, and usage diagnostics. # Claude Prompt Caching UniGateway supports temporary caching for stable prompt prefixes through its Claude Messages API-compatible endpoint. Put long-lived system instructions, tool definitions, and reference material at the beginning of the request. Put the current question, timestamps, and live retrieval results after the cache breakpoint so later requests can reuse the cache. A cache hit depends on an identical request prefix, an unexpired cache entry, model support, and current account availability. Refer to the [UniGateway model catalog](https://unigateway.ai/models) and this guide for current model IDs, capabilities, and availability. ## Endpoint details | Item | Value | | --- | --- | | Method | `POST` | | Path | `/v1/messages` | | API URL | `https://api.unigateway.ai/v1/messages` | | Authentication | `x-api-key: $UNIGATEWAY_API_KEY` | | Version header | `anthropic-version: 2023-06-01` | | Content-Type | `application/json` | This guide applies only to Claude models called with the Claude Messages API-compatible format. Chat Completions uses different request fields and usage fields, so the examples in this guide do not apply to Chat Completions requests. ## Prerequisites Create or retrieve an API key from [UniGateway API Keys](https://unigateway.ai/dashboard/api-keys). Configure access controls, rotation, and secret management according to your security requirements. Store the API key in an environment variable. Do not put it in source code, frontend code, logs, or a code repository: ```bash export UNIGATEWAY_API_KEY="" ``` Windows PowerShell: ```powershell $env:UNIGATEWAY_API_KEY = "" ``` The examples below use `claude-sonnet-4-6` only to demonstrate request structure. For real requests, use a request model ID shown in the UniGateway model catalog and follow the support range and minimum cacheable length documented here. ## Activation requirements and cache breakpoints Add `cache_control` to the content block that should end the cacheable prefix. A cache breakpoint covers the preceding `tools`, `system`, and `messages` prefix, so place it on the final stable content block. | Field | Type | Required | Description | | --- | --- | --- | --- | | `cache_control.type` | string | Yes | Must be `ephemeral`. | | `cache_control.ttl` | string | No | Cache lifetime. Defaults to `5m`; supported values are `5m` and `1h`. | The context before the breakpoint must meet the selected model's minimum cacheable length before `cache_creation.ephemeral_5m_input_tokens` or `cache_creation.ephemeral_1h_input_tokens` reports a cache write. Minimum lengths vary by model: When `cache_control` is configured on a `system` content block with `type: "text"`, only that block's `text` is evaluated against the minimum cacheable length. Do not rely on later `messages`, the current question, or other `system` content blocks to reach the threshold. For example, `claude-sonnet-4-6` requires at least 1024 tokens. A `system` text block with `cache_control` below 1024 tokens still runs normally but does not write a cache entry. | Claude model | Minimum cacheable length | | --- | --- | | Claude Fable 5 | 1024 tokens | | Claude Opus 4.7 | 2048 tokens | | Claude Opus 4.6, Claude Opus 4.5 | 4096 tokens | | Claude Opus 4.8, Claude Sonnet 5, Claude Sonnet 4.6, Claude Sonnet 4.5 | 1024 tokens | | Claude Haiku 4.5 | 4096 tokens | Use this guide as the current source for UniGateway minimum cacheable lengths. Requests shorter than the minimum still run normally and do not return an error, but they do not create a cache entry. If both `cache_creation_input_tokens` and `cache_read_input_tokens` are `0`, no cache was created or read. A common cause is a prefix shorter than the selected model's minimum cacheable length. Do not use character or word counts as a substitute for token thresholds. Tokenization differs by model. Use the response `usage` fields to confirm whether a cache entry was actually created. ## Cache stable reference material and tool definitions Use this pattern when stable content such as a support knowledge base, coding standard, or product policy remains unchanged while each user question differs. Because `tools` comes before `system`, and the breakpoint is on the final stable reference block, the tool definitions, system instructions, and reference material all become part of the reusable prefix. ### cURL request This example puts the cache breakpoint on `system[0]`. Therefore, for `claude-sonnet-4-6`, `system[0].text` with `cache_control` must itself contain at least 1024 tokens. A shorter block does not produce a cache write. Replace the placeholder in `system[0].text` with the actual stable system rules, tool documentation, or knowledge-base content. The complete context before the breakpoint must meet the selected model's minimum cacheable length. Do not include the current date, a user identifier, live retrieval results, or the current question in this block. To use the five-minute cache, replace `"1h"` with `"5m"`. ```bash curl https://api.unigateway.ai/v1/messages \ -H "x-api-key: $UNIGATEWAY_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 600, "system": [ { "type": "text", "text": "", "cache_control": {"type": "ephemeral", "ttl": "1h"} } ], "messages": [ { "role": "user", "content": "Order UG-20260721-001 is delayed. Please check its status." } ] }' ``` When `usage.cache_creation.ephemeral_1h_input_tokens` is greater than `0`, a one-hour cache entry was created. After the first request creates the cache, send another request with exactly the same `system` and `cache_control`, and put the new dynamic question at the end of `messages`. Check `usage.cache_read_input_tokens` to verify a hit. ### Python SDK request The following Python example reads a local `knowledge-base.md` file. The file must contain only stable instructions and reference material, and the total context before the cache breakpoint must meet the selected model's minimum cacheable length. Install the SDK: ```bash pip install anthropic ``` ```python import os from pathlib import Path import anthropic MODEL = "claude-sonnet-4-6" CACHE_TTL = "1h" tools = [ { "name": "get_order_status", "description": "Look up the status of an order by its ID.", "input_schema": { "type": "object", "properties": { "order_id": { "type": "string", "description": "The order ID supplied by the customer.", } }, "required": ["order_id"], "additionalProperties": False, }, } ] stable_reference = Path("knowledge-base.md").read_text(encoding="utf-8") if not stable_reference.strip(): raise ValueError("knowledge-base.md must contain stable reference content.") client = anthropic.Anthropic( api_key=os.environ["UNIGATEWAY_API_KEY"], base_url="https://api.unigateway.ai", ) response = client.messages.create( model=MODEL, max_tokens=600, tools=tools, system=[ { "type": "text", "text": "You are an order-support assistant. Follow the supplied policy exactly.", }, { "type": "text", "text": stable_reference, "cache_control": {"type": "ephemeral", "ttl": CACHE_TTL}, }, ], messages=[ { "role": "user", "content": "Order UG-20260721-001 is delayed. Please check its status.", } ], ) print(response.content) print(response.usage) ``` The SDK automatically requests `POST /v1/messages`. Set `base_url` to `https://api.unigateway.ai` without appending `/v1`. Change `CACHE_TTL` to `"5m"` to create a five-minute cache entry. Before choosing a one-hour TTL, confirm that the stable prefix will be reused in that window. The first request creates the cache. The following example shows the `usage` structure. Token counts are illustrative; use the actual values returned by the API: ```json { "cache_creation_input_tokens": 2534, "cache_read_input_tokens": 0, "input_tokens": 22, "output_tokens": 148, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 2534 } } ``` In this example, `ephemeral_1h_input_tokens` becomes greater than `0` only when the stable prefix meets the selected model's minimum cacheable length. If you change the TTL to `"5m"`, check `cache_creation.ephemeral_5m_input_tokens`. A TTL field for which no cache was created may be `0` or omitted. ## Append-only multi-turn conversations When conversation history only grows by appending messages, add `cache_control` at the request top level. The service automatically places a cache breakpoint at the most recent cacheable history and moves the breakpoint forward as history is appended. This section covers only multi-turn `messages` history. It does not include stable reference material, `system`, or tool definitions. Every request must preserve the content and order of existing messages and append new messages only at the end of `messages`. The automatic breakpoint starts writing `ephemeral_5m_input_tokens` only after the conversation before it reaches the selected model's minimum cacheable length. The short text below demonstrates structure only. In real use, continue appending history until it reaches the required length, then verify cache creation and reads through `usage`. ### cURL request Send the first message: ```bash curl https://api.unigateway.ai/v1/messages \ -H "x-api-key: $UNIGATEWAY_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 600, "cache_control": {"type": "ephemeral"}, "messages": [ { "role": "user", "content": "What is the refund request deadline?" } ] }' ``` The first response uses the following structure. The `id`, `content.text`, and token counts vary by request: ```json { "model": "claude-sonnet-4-6", "id": "msg_xxx", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "Please provide the company or service so I can identify its refund deadline." } ], "stop_reason": "end_turn", "stop_sequence": null, "stop_details": null, "usage": { "input_tokens": 15, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 18 } } ``` A cache entry becomes available to later requests only after the first response begins. With the non-streaming cURL commands in this section, wait for the first command to return. Preserve the first `user` message and the `assistant` content from the response, then append the new `user` message. For a text-only response, put the text from the first response's `content` array in the `text` placeholder below. If the response contains other content blocks, preserve the original array structure and ordering: ```bash curl https://api.unigateway.ai/v1/messages \ -H "x-api-key: $UNIGATEWAY_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 600, "cache_control": {"type": "ephemeral"}, "messages": [ { "role": "user", "content": "What is the refund request deadline?" }, { "role": "assistant", "content": [ { "type": "text", "text": "" } ] }, { "role": "user", "content": "Does the deadline change for a damaged item?" } ] }' ``` When the history before the automatic breakpoint meets the selected model's minimum cacheable length, and the request that creates the cache has completed, a later response with `usage.cache_read_input_tokens` greater than `0` proves a hit. Do not send the cache-creation request and later requests concurrently. A later request cannot reuse a cache that has not finished being created. ### Python SDK request The following code matches the cURL example and demonstrates text-only multi-turn history: ```python def send(messages): return client.messages.create( model=MODEL, max_tokens=600, cache_control={"type": "ephemeral"}, messages=messages, ) history = [ { "role": "user", "content": "What is the refund request deadline?", } ] first_response = send(history) history.append( { "role": "assistant", "content": [block.model_dump(exclude_none=True) for block in first_response.content], } ) history.append( { "role": "user", "content": "Does the deadline change for a damaged item?", } ) second_response = send(history) print(second_response.content) print(second_response.usage) ``` When the conversation meets the cache threshold and the second request hits the cache, `usage` can contain the following structure: ```json { "cache_creation_input_tokens": 0, "cache_read_input_tokens": 2534, "input_tokens": 57, "output_tokens": 116 } ``` Only a `cache_read_input_tokens` value greater than `0` proves that input was read from the cache. If the conversation has not reached the selected model's minimum cacheable length, no temporary cache is created. Continue appending history and check the field again. ## Cacheable and non-cacheable content Most request content blocks can be cached automatically or marked as a cache breakpoint with `cache_control`: | Content | Cacheable scope | | --- | --- | | Tool definitions | Tool definitions in the `tools` array. | | System messages | Content blocks in the `system` array. | | Text messages | User and assistant text blocks in `messages[].content`. | | Images and documents | Image and document blocks in user `messages[].content`. | | Tool calls and tool results | Related assistant and user blocks in `messages[].content`. | The following content cannot be used directly as a cache breakpoint: - Thinking blocks cannot have `cache_control` directly. When they appear in prior assistant turns, they can be read together with other cacheable content and count toward cache-read input tokens. - Nested content such as citations cannot be cached directly. Cache the containing top-level block instead. For example, a top-level document block used as a citation source can be cached. - Empty text blocks cannot be cached. ## TTL and prefix consistency When `cache_control` does not specify `ttl`, the cache lifetime is five minutes. Each successful cache read refreshes that TTL. If a stable prefix is expected to be reused after more than five minutes but within one hour, set `"ttl": "1h"` on the breakpoint. One-hour cache writes cost more than five-minute writes, so do not enable them for requests that will not be reused. A single request can use at most four cache breakpoints, and top-level automatic caching counts toward that limit. Most workloads need one explicit breakpoint on the final stable content block. Do not combine top-level automatic caching and multiple explicit breakpoints without a specific need. The cache hierarchy is `tools → system → messages`. Changing `tools` invalidates later cached tool, system, and message prefixes. Changing `system` invalidates later cached system and message prefixes. Changing `messages` affects only that message layer and subsequent content. When one request mixes TTLs, every `1h` breakpoint must appear before every `5m` breakpoint. For example, long-lived base material can use `1h`, while short-lived session material can use `5m`: ```json { "system": [ { "type": "text", "text": "Long-lived policy and product reference content.", "cache_control": {"type": "ephemeral", "ttl": "1h"} }, { "type": "text", "text": "Short-lived session reference content.", "cache_control": {"type": "ephemeral", "ttl": "5m"} } ] } ``` A cache hit requires the entire prefix before the breakpoint to remain identical. Any of the following changes prevents an existing cache entry from being used as the reusable prefix for the request: - Changing the model, or changing the contents or ordering of `tools` or their schemas. - Changing system instructions, stable reference material, images, or their ordering. - Changing or moving `cache_control`, or inserting a timestamp, retrieval result, user identifier, or other dynamic value before the breakpoint. - Rewriting, reordering, or serializing existing history in a different format. Put dynamic content after the breakpoint and use the same construction order for every request. The stable prefix can then remain reusable even when the current question changes. ## Verify a cache hit Send one request to create the cache. A cache entry becomes available to later requests only after the first response begins. For non-streaming requests, wait for the first request to return. For streaming requests, wait for the first response to begin before sending concurrent requests that should hit the cache. Then send a request with an identical stable prefix and only append or replace dynamic content after the breakpoint. Do not treat concurrent initial requests as a cache-hit test. Check `usage` in every response: | Field | Description | | --- | --- | | `input_tokens` | Input tokens not counted as cache creation or cache reads. This is not the request's total input token count. | | `cache_creation_input_tokens` | Total input tokens used to create or rebuild cache entries in this request. Usually greater than `0` on initial creation. | | `cache_read_input_tokens` | Input tokens read from an existing cache. A value greater than `0` proves a hit. | | `cache_creation.ephemeral_5m_input_tokens` | Tokens written to a cache with the default five-minute TTL. Applies only when the prefix meets the selected model's minimum cacheable length. | | `cache_creation.ephemeral_1h_input_tokens` | Tokens written to a one-hour cache. Applies only when the prefix meets the selected model's minimum cacheable length and the breakpoint sets `"ttl": "1h"`. | | `output_tokens` | Tokens generated by the model in this response, reported separately from cache read and write tokens. | Use `cache_creation_input_tokens` for the total cache-write volume in this request and the `cache_creation` object to distinguish writes by TTL. If all cache fields are `0` in the initial request, the context before the breakpoint did not meet the selected model's minimum cacheable length or no cache was created successfully. A `cache_read_input_tokens` value of `0` means that the request did not read an existing cache entry. For repeated requests, calculate cache reuse by token volume rather than request count: ```text cache_read_input_tokens ------------------------------------------------ input_tokens + cache_creation_input_tokens + cache_read_input_tokens ``` Sum each field across requests in the same traffic set before calculating the ratio. The initial cache-creation request is a write, not a hit. Only `cache_read_input_tokens` in later requests proves reuse. ## Troubleshooting ### Why is `ephemeral_5m_input_tokens` or `ephemeral_1h_input_tokens` missing or `0`? Confirm the selected model's minimum cacheable length. Check whether the stable context before the block containing `cache_control` reaches that length, whether `"type": "ephemeral"` is set, and whether the TTL is `"5m"` or `"1h"`. Then inspect `usage.cache_creation_input_tokens` in the same response. Context below the minimum does not create a temporary cache. Expand the stable reference material, send the initial request again, and check `usage.cache_creation.ephemeral_5m_input_tokens` or `usage.cache_creation.ephemeral_1h_input_tokens`. ### Why is `cache_read_input_tokens` still `0` on the second request? Wait for the first request to finish before sending the second one. Compare the model, `tools` contents and ordering, `system` contents and ordering, `cache_control` placement, and message history before the breakpoint. They must remain identical. Move the current question, timestamps, and live retrieval results after the breakpoint, then check `usage.cache_read_input_tokens` again. An expired cache must be rebuilt according to the configured five-minute or one-hour TTL. ### Should I choose `5m` or `1h`? Use `5m` by default. Configure `"ttl": "1h"` only when the same stable prefix is expected to be reused after more than five minutes but within one hour. Changing the TTL changes the breakpoint configuration. Send a new initial request with the new configuration and check `usage.cache_creation.ephemeral_1h_input_tokens` to confirm that the one-hour cache was created. ## Security and billing recommendations - Keep the API key only in server-side environment variables. Do not put it in examples, browser code, logs, or a code repository. - Cached content can include system rules, reference material, and message history. Put only data that is allowed to be sent to the selected model in the cached prefix, and handle sensitive data according to your business requirements. - Cache reads and writes produce billable usage. Before release, record `usage` under fixed test traffic and confirm cache-read tokens, cache-write tokens, and charges in the UniGateway usage details. # Streaming > Category: Developer Guides | Last updated: 2026-08-09 # Streaming Incremental output over Server-Sent Events (SSE) for chat completions. ## Endpoint | Item | Value | |---|---| | Method | POST | | Path | /v1/chat/completions | | Set | `"stream": true` | | Gemini SSE | `/v1beta/models/{model}:streamGenerateContent?alt=sse` | ## cURL ```bash curl https://api.unigateway.ai/v1/chat/completions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -N \ -d '{ "model": "gpt-5.4", "stream": true, "messages": [{"role": "user", "content": "Say hi"}] }' ``` The `-N` flag disables output buffering. ## SSE Format ``` data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","choices":[{"delta":{"content":"Hello"},"finish_reason":null}]} data: [DONE] ``` ## Python ```python from openai import OpenAI client = OpenAI(api_key="", base_url="https://api.unigateway.ai/v1") stream = client.chat.completions.create(model="gpt-5.4", stream=True, messages=[{"role": "user", "content": "Say hi"}]) for chunk in stream: delta = chunk.choices[0].delta.content or "" if delta: print(delta, end="", flush=True) ``` ## TypeScript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1" }); const stream = await client.chat.completions.create({ model: "gpt-5.4", stream: true, messages: [{ role: "user", content: "Say hi" }] }); for await (const chunk of stream) { const delta = chunk.choices[0]?.delta?.content ?? ""; if (delta) process.stdout.write(delta); } ``` ## Gemini SSE Streaming ```bash curl "https://api.unigateway.ai/v1beta/models/gemini-3-pro-preview:streamGenerateContent?alt=sse" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"contents": [{"parts": [{"text": "Say hi"}]}]}' ``` ## Image Generation Streaming Image generation also supports SSE streaming for progressive rendering of intermediate and final images. ### Request ```bash curl -sS -X POST "https://api.unigateway.ai/v1/images/generations" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -N \ -d '{"model":"gpt-image-2","prompt":"A winter landscape.","stream":true,"partial_images":2}' ``` ### SSE Event Format | Event Type | Fields | Description | |---|---|---| | `image_generation.partial_image` | `b64_json`, `partial_image_index` | Intermediate low-resolution image | | `image_generation.completed` | `b64_json` | Final full-resolution image | ``` event: image_generation.partial_image data: {"created_at":1718000000,"type":"image_generation.partial_image","b64_json":"iVBOR...","partial_image_index":0} event: image_generation.completed data: {"created_at":1718000000,"type":"image_generation.completed","b64_json":"iVBOR..."} ``` > **Endpoint Compatibility**: Not all upstream endpoints support true SSE streaming. See [OpenAI Images API → Endpoint Compatibility](/docs/openai-images-api#endpoint-compatibility). ## Error Handling | Scenario | Resolution | |---|---| | Stream breaks mid-flight | Start a new request trace | | No tokens received | Retry with fresh request after one attempt | | SSE keep-alive `: UNIGATEWAY PROCESSING` | Normal — ignore comment lines starting with `:` | ## Image Generation # Image Generation Overview > Category: Image Generation | Last updated: 2026-08-09 Image generation overview comparing OpenAI Images API and Gemini Images API protocols. # Image Generation Overview Generate images with UniGateway through OpenAI-compatible or Gemini native protocols. | Protocol | Model | Endpoint | |---|---|---| | OpenAI Images API | `gpt-image-2` | `POST /v1/images/generations`, `/v1/images/edits` | | Gemini Images API | `gemini-3-pro-image-preview`, `gemini-3.1-flash-image-preview` | `POST /v1beta/models/{model}:generateContent` | - **[OpenAI Images API](/docs/openai-images-api)** — text-to-image, editing, inpainting, streaming via `gpt-image-2` - **[Gemini Images API](/docs/gemini-images-api)** — text-to-image and image-to-image with Nano Banana Pro and Nano Banana 2 Confirm model availability with `GET /v1/models` before production use. # Image Generation and Editing > Category: Image Generation | Last updated: 2026-08-09 Generate and edit images with gpt-image-2 and Gemini image models through UniGateway. # Image Generation and Editing Use UniGateway to generate and edit images: `gpt-image-2` via OpenAI-compatible Images API, Nano Banana models via Gemini `generateContent`. ## Quick Reference | Task | Model ID | Endpoint | Content-Type | |---|---|---|---| | Text-to-image | `gpt-image-2` | `POST /v1/images/generations` | `application/json` | | Multi-image (n) | `gpt-image-2` | `POST /v1/images/generations` | `application/json` | | Streaming | `gpt-image-2` | `POST /v1/images/generations` + `stream:true` | `application/json` | | Edit / composite / inpaint | `gpt-image-2` | `POST /v1/images/edits` | `multipart/form-data` | | Gemini text-to-image | `gemini-3-pro-image-preview` or `gemini-3.1-flash-image-preview` | `POST /v1beta/models/{model}:generateContent` | `application/json` | | Gemini image-to-image | same as above, add `inline_data` in `parts[]` | same | `application/json` | **Base URL**: `https://api.unigateway.ai/v1` for Images API; `https://api.unigateway.ai` for Gemini. > Display names like Nano Banana Pro / Nano Banana 2 are product nicknames. The [model library](https://unigateway.ai/models) shows requestable model IDs. Always use the exact `id` from `GET /v1/models`. Authentication: `Authorization: Bearer $UNIGATEWAY_API_KEY` ## Prerequisites - A UniGateway API key stored in `UNIGATEWAY_API_KEY` - Confirm the target model is available via `GET /v1/models` ## gpt-image-2 ### Text-to-Image ```bash curl -sS -X POST "https://api.unigateway.ai/v1/images/generations" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-image-2", "prompt": "A clean product hero image for an AI gateway dashboard, dark background, soft blue lighting." }' > response.json ``` **Python** ```python from openai import OpenAI; import base64 client = OpenAI(api_key="", base_url="https://api.unigateway.ai/v1") result = client.images.generate(model="gpt-image-2", prompt="A clean product hero image.") with open("out.png", "wb") as f: f.write(base64.b64decode(result.data[0].b64_json)) ``` **TypeScript** ```typescript import OpenAI from "openai"; import fs from "fs"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1" }); const r = await client.images.generate({ model: "gpt-image-2", prompt: "A clean product hero image." }); fs.writeFileSync("out.png", Buffer.from(r.data[0].b64_json, "base64")); ``` ### Multiple Images ```bash curl -sS -X POST "https://api.unigateway.ai/v1/images/generations" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-image-2", "prompt": "Four icons: chat, image, video, search.", "n": 4, "size": "1024x1024", "quality": "medium", "output_format": "png" }' > batch.json ``` ### Parameters | Parameter | Required | Values | Notes | |---|---|---|---| | `model` | Yes | `gpt-image-2` | | | `prompt` | Yes | text | | | `size` | No | `1024x1024`, `1536x1024`, `2048x2048`, `auto`, etc. | W×H, multiples of 16, max 3840, ratio ≤ 3:1 | | `quality` | No | `low` / `medium` / `high` / `auto` | default `auto` | | `n` | No | 1–10 | | | `output_format` | No | `png` / `jpeg` / `webp` | default `png` | | `output_compression` | No | 0–100 | jpeg/webp only | | `background` | No | `opaque` / `auto` | | | `moderation` | No | `auto` / `low` | | | `stream` | No | `true` | SSE streaming | | `partial_images` | No | 0–3 | streaming intermediates | | `user` | No | string | end-user identifier | ### Streaming ```bash curl -sS -X POST "https://api.unigateway.ai/v1/images/generations" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-image-2", "prompt": "A winter landscape.", "stream": true, "partial_images": 2 }' ``` ### Save Response ```bash jq -r '.data[0].b64_json' response.json | base64 -D > output.png # macOS jq -r '.data[0].b64_json' response.json | base64 --decode > output.png # Linux ``` ### Edit / Composite / Inpaint All use `POST /v1/images/edits` with `multipart/form-data`. **Single image edit** — pass the original in `image[]`: ```bash curl -sS -X POST "https://api.unigateway.ai/v1/images/edits" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -F "model=gpt-image-2" -F "image[]=@room.png" \ -F "prompt=Change the sofa to cream white, keep everything else." \ -F "quality=high" -F "size=1024x1024" -F "output_format=png" > edit.json ``` **Multi-reference composition** — pass multiple `image[]`: ```bash curl -sS -X POST "https://api.unigateway.ai/v1/images/edits" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -F "model=gpt-image-2" \ -F "image[]=@item1.png" -F "image[]=@item2.png" -F "image[]=@item3.png" \ -F "prompt=Combine all items into a single product photo on white background." \ -F "quality=high" -F "output_format=png" > composite.json ``` **Masked inpainting** — pass `mask` alongside `image[]`: ```bash curl -sS -X POST "https://api.unigateway.ai/v1/images/edits" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -F "model=gpt-image-2" -F "mask=@mask.png" -F "image[]=@src.png" \ -F "prompt=Fill the masked area with a pink flamingo pool float." > inpaint.json ``` Mask must match source dimensions and format, ≤ 50 MB, include alpha channel. ### Response ```json { "created": 1710000000, "data": [{ "b64_json": "..." }] } ``` --- ## Nano Banana (Gemini Image Models) Both text-to-image and image-to-image use `POST /v1beta/models/{model}:generateContent`. Images are passed as `inline_data` inside `parts[]`. | Model | API Model ID | Best for | |---|---|---| | Nano Banana Pro | `gemini-3-pro-image-preview` | Highest quality, complex instructions, text rendering, 4K | | Nano Banana 2 | `gemini-3.1-flash-image-preview` | Speed, high volume, general-purpose | ### Text-to-Image ```bash curl -sS -X POST "https://api.unigateway.ai/v1beta/models/gemini-3-pro-image-preview:generateContent" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contents": [{ "parts": [{ "text": "Your prompt here." }] }], "generationConfig": { "responseModalities": ["TEXT", "IMAGE"], "imageConfig": { "aspectRatio": "16:9", "imageSize": "4K" } } }' ``` Replace the model with `gemini-3.1-flash-image-preview` for Nano Banana 2. ### Image-to-Image (Edit / Transform) ```bash B64=$(base64 -i input.png 2>/dev/null || base64 -w0 input.png) curl -sS -X POST "https://api.unigateway.ai/v1beta/models/gemini-3-pro-image-preview:generateContent" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"contents\": [{ \"parts\": [ {\"text\": \"Your editing instruction.\"}, {\"inline_data\": {\"mime_type\": \"image/png\", \"data\": \"${B64}\"}} ]}], \"generationConfig\": { \"responseModalities\": [\"TEXT\", \"IMAGE\"], \"imageConfig\": { \"aspectRatio\": \"16:9\", \"imageSize\": \"4K\" } } }" ``` For multiple input images, add more `inline_data` blocks before the text instruction. ### Image Size Control | Field | Values | Notes | |---|---|---| | `imageConfig.aspectRatio` | `1:1`, `4:3`, `3:4`, `16:9`, `9:16` | Nano Banana Pro also supports `2:3`, `3:2`, `4:5`, `5:4`, `21:9` | | `imageConfig.imageSize` | `1K`, `2K`, `4K` | Pro supports up to 4K; Flash supports up to 4K | ### Save Response ```bash jq -r 'first(..|objects|select(.inlineData?.data)|.inlineData.data)' result.json | base64 -D > out.png # macOS jq -r 'first(..|objects|select(.inlineData?.data)|.inlineData.data)' result.json | base64 --decode > out.png # Linux ``` ### Response ```json { "candidates": [{ "content": { "parts": [ { "text": "Here is the generated image." }, { "inlineData": { "mimeType": "image/png", "data": "..." } } ] } }] } ``` --- ## Tips - Use `gpt-image-2` for the Images API, SDK integration, and `multipart/form-data` editing workflows. - Use Nano Banana Pro for highest quality Gemini output; Nano Banana 2 for speed and volume. - Gemini models handle both text-to-image and image-to-image through the same endpoint. - Upload decoded images to your own object storage for production. ## Errors | Status | Cause | Resolution | |---|---|---| | `400` | Invalid parameters or format | Check `prompt`, `size`, `mask` | | `401` | Invalid API key | Verify `Authorization` header | | `404` | Model not available | Confirm via `GET /v1/models` | | `429` | Rate limit | Back off and retry | | `5xx` | Server error | Exponential backoff | # OpenAI Images API > Category: Image Generation | Last updated: 2026-08-09 Generate and edit images with gpt-image-2 through the OpenAI-compatible Images API. # OpenAI Images API Generate and edit images with `gpt-image-2` through the OpenAI-compatible Images API. Base URL: `https://api.unigateway.ai/v1` ## Text-to-Image ## Code examples ### curl ```curl curl -sS -X POST "https://api.unigateway.ai/v1/images/generations" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"gpt-image-2","prompt":"A clean product hero image.","size":"1024x1024","quality":"high","output_format":"png"}' > image.json ``` ### python ```python from openai import OpenAI; import base64 client = OpenAI(api_key="", base_url="https://api.unigateway.ai/v1") r = client.images.generate(model="gpt-image-2", prompt="A hero image.") with open("out.png","wb") as f: f.write(base64.b64decode(r.data[0].b64_json)) ``` ### typescript ```typescript import OpenAI from "openai"; import fs from "fs"; const c = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1" }); const r = await c.images.generate({ model: "gpt-image-2", prompt: "A hero image." }); fs.writeFileSync("out.png", Buffer.from(r.data[0].b64_json, "base64")); ``` ```bash curl -sS -X POST "https://api.unigateway.ai/v1/images/generations" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-image-2", "prompt": "A clean product hero image for an AI gateway dashboard." }' > response.json ``` **Python** ```python from openai import OpenAI; import base64 client = OpenAI(api_key="", base_url="https://api.unigateway.ai/v1") r = client.images.generate(model="gpt-image-2", prompt="A hero image.") with open("out.png","wb") as f: f.write(base64.b64decode(r.data[0].b64_json)) ``` **TypeScript** ```typescript import OpenAI from "openai"; import fs from "fs"; const c = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1" }); const r = await c.images.generate({ model: "gpt-image-2", prompt: "A hero image." }); fs.writeFileSync("out.png", Buffer.from(r.data[0].b64_json, "base64")); ``` ## Multiple Images ```bash curl -sS -X POST "https://api.unigateway.ai/v1/images/generations" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"gpt-image-2","prompt":"Four icons: chat, image, video, search.","n":4,"size":"1024x1024","quality":"medium","output_format":"png"}' > batch.json ``` ## Parameters | Parameter | Required | Values | Notes | |---|---|---|---| | `model` | Yes | `gpt-image-2` | | | `prompt` | Yes | text | | | `size` | No | `1024x1024`, `1536x1024`, `2048x2048`, `auto`, etc. | W×H ×16, max 3840, ratio ≤ 3:1 | | `quality` | No | `low` / `medium` / `high` / `auto` | default `auto` | | `n` | No | 1–10 | | | `output_format` | No | `png` / `jpeg` / `webp` | default `png` | | `output_compression` | No | 0–100 | jpeg/webp only | | `background` | No | `opaque` / `auto` | | | `moderation` | No | `auto` / `low` | | | `stream` | No | `true` | SSE streaming | | `partial_images` | No | 0–3 | streaming intermediates | | `user` | No | string | end-user identifier | ## Streaming Enable `stream: true` to receive progressive image updates via Server-Sent Events (SSE). Clients can render partial images as they arrive. ### Request ```bash curl -sS -X POST "https://api.unigateway.ai/v1/images/generations" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"gpt-image-2","prompt":"A winter landscape.","stream":true,"partial_images":2}' ``` ### SSE Event Format | Event Type | Fields | Description | |---|---|---| | `image_generation.partial_image` | `b64_json`, `partial_image_index` | Intermediate low-resolution image (count controlled by `partial_images`) | | `image_generation.completed` | `b64_json` | Final full-resolution image | Raw SSE output example: ``` event: image_generation.partial_image data: {"created_at":1718000000,"type":"image_generation.partial_image","b64_json":"iVBOR...","partial_image_index":0} event: image_generation.partial_image data: {"created_at":1718000000,"type":"image_generation.partial_image","b64_json":"iVBOR...","partial_image_index":1} event: image_generation.completed data: {"created_at":1718000000,"type":"image_generation.completed","b64_json":"iVBOR..."} ``` ### Python Streaming ```python from openai import OpenAI import base64 client = OpenAI(api_key="", base_url="https://api.unigateway.ai/v1") stream = client.images.generate( model="gpt-image-2", prompt="A winter landscape.", stream=True, partial_images=2, ) for event in stream: if event.type == "image_generation.partial_image": idx = event.partial_image_index with open(f"partial_{idx}.png", "wb") as f: f.write(base64.b64decode(event.b64_json)) elif event.type == "image_generation.completed": with open("final.png", "wb") as f: f.write(base64.b64decode(event.b64_json)) ``` ### TypeScript Streaming ```typescript import OpenAI from "openai"; import fs from "fs"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1" }); const stream = await client.images.generate({ model: "gpt-image-2", prompt: "A winter landscape.", stream: true, partial_images: 2, }); for await (const event of stream) { if (event.type === "image_generation.partial_image") { const idx = event.partial_image_index; fs.writeFileSync(`partial_${idx}.png`, Buffer.from(event.b64_json, "base64")); } else if (event.type === "image_generation.completed") { fs.writeFileSync("final.png", Buffer.from(event.b64_json, "base64")); } } ``` ## Endpoint Compatibility Streaming support varies across upstream endpoints. Only OpenAI Official and Azure OpenAI support true SSE streaming with progressive `partial_image` events. Other endpoints accept `stream:true` but may return the complete JSON payload after generation finishes without progressive rendering. > **Note**: For true streaming (progressive image rendering), route `gpt-image-2` streaming requests to an SSE-capable endpoint such as OpenAI Official or Azure OpenAI. ## Save Response ```bash jq -r '.data[0].b64_json' response.json | base64 -D > output.png # macOS jq -r '.data[0].b64_json' response.json | base64 --decode > output.png # Linux ``` ## Edit / Composite / Inpaint All use `POST /v1/images/edits` with `multipart/form-data`. **Single image edit:** ```bash curl -sS -X POST "https://api.unigateway.ai/v1/images/edits" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -F "model=gpt-image-2" -F "image[]=@room.png" \ -F "prompt=Change the sofa to cream white, keep everything else." \ -F "quality=high" -F "size=1024x1024" -F "output_format=png" > edit.json ``` **Multi-reference composition:** ```bash curl -sS -X POST "https://api.unigateway.ai/v1/images/edits" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -F "model=gpt-image-2" \ -F "image[]=@item1.png" -F "image[]=@item2.png" -F "image[]=@item3.png" \ -F "prompt=Combine into a single product photo." \ -F "quality=high" -F "output_format=png" > composite.json ``` **Masked inpainting:** ```bash curl -sS -X POST "https://api.unigateway.ai/v1/images/edits" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -F "model=gpt-image-2" -F "mask=@mask.png" -F "image[]=@src.png" \ -F "prompt=Fill the masked area with a pink flamingo." > inpaint.json ``` Mask requirements: identical dimensions, ≤ 50 MB, alpha channel. ## Response ```json { "created": 1710000000, "data": [{ "b64_json": "..." }] } ``` ## Errors | Status | Cause | Resolution | |---|---|---| | `400` | Invalid parameters | Check `prompt`, `size`, `mask` | | `401` | Invalid API key | Verify `Authorization` | | `404` | Model not available | Confirm via `GET /v1/models` | | `429` | Rate limit | Back off and retry | | `5xx` | Server error | Exponential backoff | # Gemini Images API > Category: Image Generation | Last updated: 2026-08-09 Generate and edit images with Nano Banana models through Gemini generateContent. # Gemini Images API Generate and edit images with Nano Banana models through Gemini `generateContent`. Base URL: `https://api.unigateway.ai` | Display name | API model ID | Best for | |---|---|---| | Nano Banana Pro | `gemini-3-pro-image-preview` | Highest quality, complex instructions, text rendering, 4K | | Nano Banana 2 | `gemini-3.1-flash-image-preview` | Speed, high volume, general-purpose | ## Text-to-Image ## Code examples ### curl ```curl curl -sS -X POST "https://api.unigateway.ai/v1beta/models/gemini-3-pro-image-preview:generateContent" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"contents":[{"parts":[{"text":"Your prompt."}]}],"generationConfig":{"responseModalities":["TEXT","IMAGE"],"imageConfig":{"aspectRatio":"16:9","imageSize":"4K"}}}' > image.json ``` ### python ```python import requests key = "" resp = requests.post("https://api.unigateway.ai/v1beta/models/gemini-3-pro-image-preview:generateContent", headers={"Authorization":f"Bearer {key}","Content-Type":"application/json"}, json={"contents":[{"parts":[{"text":"Your prompt."}]}],"generationConfig":{"responseModalities":["TEXT","IMAGE"],"imageConfig":{"aspectRatio":"16:9","imageSize":"4K"}}}) print(resp.json()) ``` ### typescript ```typescript const resp = await fetch("https://api.unigateway.ai/v1beta/models/gemini-3-pro-image-preview:generateContent", { method:"POST", headers:{ Authorization:`Bearer ${process.env.UNIGATEWAY_API_KEY}`, "Content-Type":"application/json" }, body:JSON.stringify({ contents:[{ parts:[{ text:"Your prompt." }] }], generationConfig:{ responseModalities:["TEXT","IMAGE"], imageConfig:{ aspectRatio:"16:9", imageSize:"4K" } } }) }); console.log(await resp.json()); ``` ```bash curl -sS -X POST "https://api.unigateway.ai/v1beta/models/gemini-3-pro-image-preview:generateContent" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contents": [{ "parts": [{ "text": "Your prompt here." }] }], "generationConfig": { "responseModalities": ["TEXT", "IMAGE"], "imageConfig": { "aspectRatio": "16:9", "imageSize": "4K" } } }' ``` Replace the model with `gemini-3.1-flash-image-preview` for Nano Banana 2. ## Image-to-Image Pass images as `inline_data` in `parts[]` alongside a text instruction: ```bash B64=$(base64 -i input.png 2>/dev/null || base64 -w0 input.png) curl -sS -X POST "https://api.unigateway.ai/v1beta/models/gemini-3-pro-image-preview:generateContent" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"contents\": [{ \"parts\": [ {\"text\": \"Your editing instruction.\"}, {\"inline_data\": {\"mime_type\": \"image/png\", \"data\": \"${B64}\"}} ]}], \"generationConfig\": { \"responseModalities\": [\"TEXT\", \"IMAGE\"], \"imageConfig\": { \"aspectRatio\": \"16:9\", \"imageSize\": \"4K\" } } }" ``` For multiple input images, add more `inline_data` blocks before the text instruction. ## Image Size Control | Field | Values | Notes | |---|---|---| | `imageConfig.aspectRatio` | `1:1`, `4:3`, `3:4`, `16:9`, `9:16` | Pro also: `2:3`, `3:2`, `4:5`, `5:4`, `21:9` | | `imageConfig.imageSize` | `1K`, `2K`, `4K` | | ## Save Response ```bash jq -r 'first(..|objects|select(.inlineData?.data)|.inlineData.data)' result.json | base64 -D > out.png # macOS jq -r 'first(..|objects|select(.inlineData?.data)|.inlineData.data)' result.json | base64 --decode > out.png # Linux ``` ## Response ```json { "candidates": [{ "content": { "parts": [ { "text": "Here is the generated image." }, { "inlineData": { "mimeType": "image/png", "data": "..." } } ] } }] } ``` ## Errors | Status | Cause | Resolution | |---|---|---| | `400` | Invalid parameters | Check `imageConfig` | | `401` | Invalid API key | Verify `Authorization` | | `404` | Model not available | Confirm via `GET /v1/models` | | `429` | Rate limit | Back off and retry | | `5xx` | Server error | Exponential backoff | ## Video Generation # Video Generation Overview > Category: Video Generation | Last updated: 2026-08-09 All video generation providers: Seedance and Sora, with protocol differences. # Video Generation Overview UniGateway separates video models by provider and protocol. Each video family uses its own Base URL and request format. ## Providers and Protocols | Provider | Model family | Base URL | Protocol | Content-Type | Auth | |---|---|---|---|---|---| | ByteDance | Seedance | `https://api.unigateway.ai` | `/api/v3/contents/generations/tasks` | `application/json` | `Authorization: Bearer $UNIGATEWAY_API_KEY` | | OpenAI | Sora | `https://api.unigateway.ai/v1` | `/v1/videos` | `multipart/form-data` | `Authorization: Bearer $UNIGATEWAY_API_KEY` | ## Key Differences | Aspect | Seedance | Sora | |---|---|---| | Base URL | `https://api.unigateway.ai` | `https://api.unigateway.ai/v1` | | Content-Type | `application/json` | `multipart/form-data` | | Create task | `POST /api/v3/contents/generations/tasks` | `POST /v1/videos` | | Query status | `GET /api/v3/contents/generations/tasks/{id}` | `GET /v1/videos/{id}` | | List tasks | `GET /api/v3/contents/generations/tasks` | Not available | | Delete task | `DELETE /api/v3/contents/generations/tasks/{id}` | Not available | | Asset libraries | `/api/v3/asset-groups` and `/api/v3/assets` | Not available | | Model discovery | Model catalog or `GET /v1/models` | `GET /v1/models` with `supported_endpoint_types: ["openai-video"]` | | Recommended model | `doubao-seedance-2-0-fast-260128` | `sora-2` | Do not send Sora model IDs to Seedance endpoints or vice versa. The request formats and content types are incompatible. ## Common Workflow 1. Choose a provider 2. Submit a video generation request 3. Receive a task/job ID 4. Poll the status endpoint until the video is complete 5. Save the video URL - [ByteDance (Seedance)](/docs/seedance-overview) - [OpenAI (Sora)](/docs/sora-overview) # Sora Video Generation > Category: Video Generation | Last updated: 2026-08-09 Generate videos from text prompts with Sora through the OpenAI-compatible video protocol. # Sora Video Generation Use Sora through UniGateway to generate videos from text prompts. Requests use `multipart/form-data`. ## Prerequisites - A UniGateway API key stored in `UNIGATEWAY_API_KEY` - Confirm Sora models are available via `GET /v1/models` ## Endpoints | Action | Method | Path | Content-Type | |---|---|---|---| | Create video | `POST` | `/v1/videos` | `multipart/form-data` | | Query status | `GET` | `/v1/videos/{id}` | — | | Download video | `GET` | `/v1/videos/{id}/content` | — | Base URL: `https://api.unigateway.ai/v1` ## Supported Models | Model ID | Description | |---|---| | `sora-2` | General-purpose video generation | | `sora-2-pro` | Higher-quality video generation | ## Create a Video ## Code examples ### curl ```curl curl -sS -X POST "https://api.unigateway.ai/v1/videos" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "sora-2", "prompt": "A cinematic aerial shot of a coastline at golden hour.", "size": "1280x720", "seconds": "5" }' ``` ### python ```python import requests key = "" resp = requests.post( "https://api.unigateway.ai/v1/videos", headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"}, json={"model": "sora-2", "prompt": "A cinematic aerial shot of a coastline.", "size": "1280x720", "seconds": "5"} ) print(resp.json()) ``` ### typescript ```typescript const resp = await fetch("https://api.unigateway.ai/v1/videos", { method: "POST", headers: { Authorization: `Bearer ${process.env.UNIGATEWAY_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ model: "sora-2", prompt: "A cinematic aerial shot of a coastline.", size: "1280x720", seconds: "5" }), }); console.log(await resp.json()); ``` ```bash curl -sS -X POST "https://api.unigateway.ai/v1/videos" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -F 'model=sora-2' \ -F 'prompt=A cinematic aerial shot of a coastline at golden hour.' \ -F 'size=1280x720' \ -F 'seconds=5' ``` Response: ```json { "id": "task_4kX5fFNCbHgMJLx", "object": "video", "status": "queued", "model": "sora-2", "progress": 0, "size": "1280x720", "seconds": "5" } ``` ## Query Video Status ```bash curl -sS "https://api.unigateway.ai/v1/videos/{id}" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` Response (in progress): ```json { "id": "task_4kX5fFNCbHgMJLx", "object": "video", "status": "in_progress", "model": "sora-2", "progress": 0, "size": "1280x720", "seconds": "5" } ``` Response (completed): ```json { "id": "task_4kX5fFNCbHgMJLx", "object": "video", "status": "completed", "model": "sora-2", "progress": 100, "size": "1280x720", "seconds": "5", "video_url": "https:///videos/output.mp4" } ``` ## Download Video ```bash curl -sS "https://api.unigateway.ai/v1/videos/{id}/content" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -o output.mp4 ``` ## Parameters (Create) | Parameter | Required | Description | |---|---|---| | `model` | Yes | Model ID: `sora-2` or `sora-2-pro` | | `prompt` | Yes | Text description of the desired video | | `size` | No | Output resolution, e.g. `1280x720` | | `seconds` | No | Target duration in seconds, e.g. `4`, `5` | ## Polling Strategy - First query: 5-10 seconds after creation - Increase interval as wait time grows - Set a total timeout on the caller side ## Sora vs Other Video Models | Video family | Base URL | Protocol | Content-Type | Model example | |---|---|---|---|---| | Sora | `https://api.unigateway.ai/v1` | OpenAI-compatible `/v1/videos` | `multipart/form-data` | `sora-2` | | Seedance | `https://api.unigateway.ai` | `/api/v3/contents/generations/tasks` | `application/json` | `doubao-seedance-2-0-fast-260128` | ## Errors | Status | Cause | Resolution | |---|---|---| | `400` | Invalid parameters or unsupported size | Check `size` and `seconds` | | `401` | Invalid or missing API key | Verify `Authorization` header | | `404` | Model not found | Confirm via `GET /v1/models` | | `429` | Rate limit exceeded | Add backoff and retry | | `500` | Server or upstream error | Retry with exponential backoff | # ByteDance Seedance / Overview > Category: Video Generation | Last updated: 2026-08-09 Overview of the Seedance workflow, interface surfaces, and child endpoint docs. # ByteDance Seedance / Overview Seedance video generation uses an asynchronous task API under the UniGateway API domain. ## Prerequisites - A UniGateway API key stored in `UNIGATEWAY_API_KEY` ```bash UNIGATEWAY_API_KEY= ``` ## Authentication Send the API key as a Bearer token: ```http Authorization: Bearer $UNIGATEWAY_API_KEY ``` ## Base URL ```bash BASE_URL=https://api.unigateway.ai ``` ## Endpoints ### Video Generation | Method | Path | Purpose | |---|---|---| | `POST` | `/api/v3/contents/generations/tasks` | Create a video task | | `GET` | `/api/v3/contents/generations/tasks` | List recent tasks | | `GET` | `/api/v3/contents/generations/tasks/{id}` | Query one task | | `DELETE` | `/api/v3/contents/generations/tasks/{id}` | Cancel or delete a task | ### Asset Libraries | Method | Path | Purpose | |---|---|---| | `POST` | `/api/v3/asset-groups` | Create an asset group | | `GET` | `/api/v3/asset-groups` | List asset groups | | `GET` | `/api/v3/asset-groups/{groupId}` | Get one asset group | | `PATCH` | `/api/v3/asset-groups/{groupId}` | Update an asset group | | `POST` | `/api/v3/assets` | Create an asset | | `GET` | `/api/v3/assets` | List assets | | `GET` | `/api/v3/assets/{assetId}` | Get one asset | | `PATCH` | `/api/v3/assets/{assetId}` | Update an asset | | `DELETE` | `/api/v3/assets/{assetId}` | Delete an asset | ## Find Available Models Use the model catalog or the models API to find account-enabled model IDs: ```bash curl https://api.unigateway.ai/v1/models \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` Example model IDs, subject to account availability: | Model ID | Description | |---|---| | `doubao-seedance-2-0-260128` | Domestic Doubao Seedance 2.0 | | `doubao-seedance-2-0-fast-260128` | Domestic Doubao Seedance 2.0 Fast | | `dreamina-seedance-2-0-260128` | International Dreamina Seedance 2.0 | | `dreamina-seedance-2-0-fast-260128` | International Dreamina Seedance 2.0 Fast | | `doubao-seedance-2.0` | Compatibility ID for standard Seedance 2.0 | | `doubao-seedance-2.0-fast` | Compatibility ID for fast Seedance 2.0 | ## Integration Guidance For ordinary text-to-video or image-to-video requests, use the video generation API directly. Use the asset library API when the workflow needs reviewed assets, allowlisted material, reusable character or scene references, or long-running generation around the same approved material. If the content does not involve sensitive material, real people, celebrities, digital humans, copyrighted characters, or reusable references, the asset library workflow is usually unnecessary. ## Task Flow ### Create Task ```bash curl -sS -X POST "https://api.unigateway.ai/api/v3/contents/generations/tasks" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "doubao-seedance-2-0-fast-260128", "content": [ { "type": "text", "text": "A blue sky with one white cloud." } ], "ratio": "16:9", "duration": 5, "generate_audio": false }' ``` Response: ```json { "id": "task_123456" } ``` ### Query Task ```bash curl -sS "https://api.unigateway.ai/api/v3/contents/generations/tasks/task_123456" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` Running response: ```json { "id": "task_123456", "model": "doubao-seedance-2-0-fast-260128", "status": "running", "content": {}, "usage": { "billing_mode": "quota", "charged_quota": 875000 }, "created_at": 1781255171, "updated_at": 1781255181, "progress": "50%", "service_tier": "default", "generate_audio": false } ``` Completed response: ```json { "id": "task_123456", "model": "doubao-seedance-2-0-fast-260128", "status": "succeeded", "content": { "video_url": "https://video.demo.url" }, "usage": { "billing_mode": "token", "completion_tokens": 108900, "total_tokens": 108900 }, "created_at": 1781255171, "updated_at": 1781255427, "completed_at": 1781255427, "progress": "100%", "resolution": "720p", "ratio": "16:9", "duration": 5, "service_tier": "default", "generate_audio": false } ``` ### List Tasks ```bash curl -sS "https://api.unigateway.ai/api/v3/contents/generations/tasks?page_num=1&page_size=20" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` Task lists include recent tasks from the last 7 days. Supported filters include `filter.status`, repeated `filter.task_ids`, `filter.model`, and `filter.service_tier`. ### Delete Task ```bash curl -sS -X DELETE "https://api.unigateway.ai/api/v3/contents/generations/tasks/task_123456" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` Queued tasks are cancelled and return `{}`. Running tasks cannot be deleted. Completed tasks return `{}` when deletion succeeds. ## Task States | Status | Meaning | Action | |---|---|---| | `queued` | Accepted and waiting for processing | Continue polling | | `running` | Generation is in progress | Continue polling | | `succeeded` | Result is ready | Save `content.video_url` | | `failed` | Processing failed | Inspect `error` and decide whether to retry | | `expired` | Task or result expired | Submit a new task if needed | | `cancelled` | Task was cancelled | Treat as terminal | | `approved_asset_required` | Asset review is required | Review or replace the referenced asset | | `content_adjustment_required` | Content must be adjusted | Revise the prompt or input media | Some responses may include `presentation_status` and `presentation_reason` for review-related failures while the raw task `status` remains `failed`. ## Create Parameters | Field | Type | Required | Description | |---|---|---|---| | `model` | string | Yes | Seedance model ID enabled for the account | | `content` | array | Yes | Text, image, video, and audio inputs | | `ratio` | string | No | Output aspect ratio: `16:9`, `4:3`, `1:1`, `3:4`, `9:16`, `21:9`, or `adaptive` | | `duration` | integer | No | Output duration in seconds | | `generate_audio` | boolean | No | Whether to generate synchronized audio | | `tools` | array | No | Tool configuration. Supported `type`: `web_search` | | `resolution` | string | No | Output resolution: `480p` or `720p` | | `seed` | integer | No | Random seed. Use `-1` for default, or an integer from `0` to `4294967295` | | `service_tier` | string | No | Service tier when enabled for the model | Detailed references: [Create Task](/docs/seedance-create-task), [Query Task](/docs/seedance-task-query), [Asset Libraries](/docs/seedance-asset-libraries). # ByteDance Seedance / Create Task > Category: Video Generation | Last updated: 2026-08-09 Create asynchronous video generation tasks with the Seedance interface. # ByteDance Seedance / Create Task Create an asynchronous Seedance video generation task. ## Endpoint | Item | Value | |---|---| | Method | `POST` | | Path | `/api/v3/contents/generations/tasks` | | Base URL | `https://api.unigateway.ai` | | Auth | `Authorization: Bearer $UNIGATEWAY_API_KEY` | ## Request ## Code examples ### curl ```curl curl -sS -X POST "https://api.unigateway.ai/api/v3/contents/generations/tasks" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "doubao-seedance-2-0-fast-260128", "content": [ { "type": "text", "text": "A cinematic tracking shot of a sports car driving through neon streets at night." } ], "ratio": "16:9", "duration": 5, "resolution": "720p", "generate_audio": false, "seed": -1 }' ``` ### python ```python import requests api_key = "" base_url = "https://api.unigateway.ai" headers = { "Authorization": "Bearer " + api_key, "Content-Type": "application/json", } resp = requests.post( base_url + "/api/v3/contents/generations/tasks", headers=headers, json={ "model": "doubao-seedance-2-0-fast-260128", "content": [ { "type": "text", "text": "A cinematic tracking shot of a sports car driving through neon streets at night.", } ], "ratio": "16:9", "duration": 5, "resolution": "720p", "generate_audio": False, "seed": -1, }, ) resp.raise_for_status() print(resp.json()) ``` ### typescript ```typescript const baseURL = "https://api.unigateway.ai"; const resp = await fetch(`${baseURL}/api/v3/contents/generations/tasks`, { method: "POST", headers: { Authorization: `Bearer ${process.env.UNIGATEWAY_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "doubao-seedance-2-0-fast-260128", content: [ { type: "text", text: "A cinematic tracking shot of a sports car driving through neon streets at night.", } ], ratio: "16:9", duration: 5, resolution: "720p", generate_audio: false, seed: -1, }), }); console.log(await resp.json()); ``` ```bash curl -sS -X POST "https://api.unigateway.ai/api/v3/contents/generations/tasks" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "doubao-seedance-2-0-fast-260128", "content": [ { "type": "text", "text": "A cinematic tracking shot of a sports car driving through neon streets at night." } ], "ratio": "16:9", "duration": 5, "resolution": "720p", "generate_audio": false, "seed": -1 }' ``` Response: ```json { "id": "task_123456" } ``` The create response only returns the task ID. Query the task to retrieve the final video URL and usage data. ## Request Body | Field | Type | Required | Description | |---|---|---|---| | `model` | string | Yes | Seedance model ID enabled for the account, such as `doubao-seedance-2-0-fast-260128` | | `content` | array | Yes | Input items. Text, images, videos, and audio can be combined | | `ratio` | string | No | `16:9`, `4:3`, `1:1`, `3:4`, `9:16`, `21:9`, or `adaptive` | | `duration` | integer | No | Output duration in seconds | | `generate_audio` | boolean | No | Whether to generate synchronized audio | | `tools` | array | No | Tool configuration. Supported `type`: `web_search` | | `resolution` | string | No | Output resolution: `480p` or `720p` | | `seed` | integer | No | Random seed. `-1` uses the default. Otherwise use `0` to `4294967295` | | `service_tier` | string | No | Service tier when enabled for the model | ## Content URL Sources For image, video, and audio items, `url` can use: - A publicly reachable URL - A data URI, such as `data:image/png;base64,{base64_image}` - An asset library reference, such as `asset://` The request body must not exceed 64 MB. Avoid base64 encoding for large media. ## Text Input ```json { "type": "text", "text": "A blue sky with one white cloud." } ``` ## Image Input Use `image_url` for first frames, last frames, and reference images. | Rule | Value | |---|---| | Formats | `jpeg`, `png`, `webp`, `bmp`, `tiff`, `gif`, `heic`, `heif` | | Aspect ratio | Greater than `0.4` and less than `2.5` | | Width and height | `300` to `6000` px | | Size | Less than `30 MB` per image | | First-frame image-to-video | 1 image | | First-and-last-frame image-to-video | 2 images | | Multimodal reference generation | 1 to 9 images | For a single first-frame image, `role` can be omitted or set to `first_frame`. ```json { "type": "image_url", "image_url": { "url": "https://example.com/first-frame.png" }, "role": "first_frame" } ``` For first-and-last-frame generation, set `role` to `first_frame` and `last_frame`. ```json [ { "type": "image_url", "image_url": { "url": "https://example.com/first-frame.png" }, "role": "first_frame" }, { "type": "image_url", "image_url": { "url": "https://example.com/last-frame.png" }, "role": "last_frame" } ] ``` For reference images, set `role` to `reference_image`. Seedance supports up to 9 reference images. ```json { "type": "image_url", "image_url": { "url": "asset://ua_123" }, "role": "reference_image" } ``` ## Video Input Use `video_url` for reference videos and set `role` to `reference_video`. | Rule | Value | |---|---| | Container formats | `mp4`, `mov` | | Supported video codecs | H.264/AVC, H.265/HEVC | | Supported audio codecs | AAC, MP3 | | Reference video resolution | `480p`, `720p`, `1080p` | | Duration | 2 to 15 seconds per video | | Count | Up to 3 reference videos | | Total duration | Up to 15 seconds across all reference videos | | Aspect ratio | `0.4` to `2.5` | | Width and height | `300` to `6000` px | | Total pixels | `409600` to `2086876` | | Size | Up to `50 MB` per video | | FPS | `24` to `60` | ```json { "type": "video_url", "video_url": { "url": "https://example.com/reference-video.mp4" }, "role": "reference_video" } ``` ## Audio Input Use `audio_url` for reference audio and set `role` to `reference_audio`. | Rule | Value | |---|---| | Formats | `wav`, `mp3` | | Duration | 2 to 15 seconds per audio file | | Count | Up to 3 reference audio files | | Total duration | Up to 15 seconds across all reference audio | | Size | Up to `15 MB` per audio file | ```json { "type": "audio_url", "audio_url": { "url": "https://example.com/reference-audio.mp3" }, "role": "reference_audio" } ``` ## Multimodal Example ```json { "model": "doubao-seedance-2-0-fast-260128", "content": [ { "type": "text", "text": "A product hero video with consistent character appearance and smooth camera motion." }, { "type": "image_url", "image_url": { "url": "asset://ua_character_reference" }, "role": "reference_image" }, { "type": "video_url", "video_url": { "url": "https://example.com/motion-reference.mp4" }, "role": "reference_video" } ], "ratio": "16:9", "duration": 5, "resolution": "720p", "generate_audio": false, "seed": -1 } ``` ## Common Failures | Status | Cause | Action | |---|---|---| | `400` | Invalid field shape or unsupported option | Remove optional fields and retry with a minimal payload | | `401` / `403` | Invalid API key or insufficient permissions | Check the API key and account access | | `404` | Model not found | Confirm the model ID from the model catalog | | `429` | Rate limit or review submission limit | Back off and queue requests | | `5xx` | Gateway or upstream supplier error | Retry with capped backoff | # ByteDance Seedance / Query Task > Category: Video Generation | Last updated: 2026-08-09 Query asynchronous video generation task results with the Seedance interface. # ByteDance Seedance / Query Task Query Seedance task status, results, usage, and task history. ## Query One Task | Item | Value | |---|---| | Method | `GET` | | Path | `/api/v3/contents/generations/tasks/{id}` | | Base URL | `https://api.unigateway.ai` | | Auth | `Authorization: Bearer $UNIGATEWAY_API_KEY` | Replace `{id}` with the task ID returned by the create call: ```bash curl -sS "https://api.unigateway.ai/api/v3/contents/generations/tasks/task_123456" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` Running response: ```json { "id": "task_123456", "model": "doubao-seedance-2-0-fast-260128", "status": "running", "usage": { "billing_mode": "quota", "charged_quota": 875000 }, "created_at": 1781255171, "updated_at": 1781255181, "progress": "50%", "service_tier": "default", "generate_audio": false, "content": {}, "error": null } ``` Completed response: ```json { "id": "task_123456", "model": "doubao-seedance-2-0-fast-260128", "status": "succeeded", "content": { "video_url": "https://video.demo.url" }, "usage": { "billing_mode": "token", "completion_tokens": 108900, "total_tokens": 108900 }, "created_at": 1781255171, "updated_at": 1781255427, "completed_at": 1781255427, "progress": "100%", "resolution": "720p", "ratio": "16:9", "duration": 5, "service_tier": "default", "generate_audio": false, "error": null } ``` Failed response: ```json { "id": "task_123456", "model": "doubao-seedance-2-0-fast-260128", "status": "failed", "content": {}, "usage": null, "error": { "code": "UPSTREAM_ERROR", "message": "Upstream task failed" } } ``` ## Response Fields | Field | Description | |---|---| | `id` | Task ID | | `model` | Model ID used by the request | | `status` | Raw task status | | `content.video_url` | Generated video URL when the task succeeds | | `usage` | Usage object when available | | `created_at` / `updated_at` / `completed_at` | Unix timestamps in seconds when available | | `progress` | Progress string when available | | `ratio` / `duration` / `resolution` / `generate_audio` / `service_tier` | Echoed request parameters when available | | `error` | Error object for failed tasks, otherwise `null` | | `presentation_status` / `presentation_reason` | Review-related presentation fields when available | `usage` depends on the supplier and billing mode. It may include fields such as `completion_tokens`, `total_tokens`, `request_count`, `duration_seconds`, `total_duration_seconds`, `size_multiplier`, `billing_units`, `credits`, `charged_quota`, or `billing_mode`. ## Task States | Status | Meaning | Action | |---|---|---| | `queued` | Accepted and waiting for processing | Continue polling | | `running` | Generation is in progress | Continue polling | | `succeeded` | Result is ready | Save `content.video_url` | | `failed` | Processing failed | Inspect `error` and decide whether to retry | | `expired` | Task or result expired | Submit a new task if needed | | `cancelled` | Task was cancelled | Treat as terminal | | `approved_asset_required` | Asset review is required | Review or replace the referenced asset | | `content_adjustment_required` | Content must be adjusted | Revise the prompt or input media | Before a terminal state, `content.video_url` may be empty and `usage` may be absent or incomplete. ## Polling Strategy - First query: wait 2 to 3 seconds after creation - Increase polling interval as wait time grows, such as 3s, 5s, 10s, 15s - Set a total timeout in the caller - Avoid high-frequency concurrent queries for the same task ID ## List Tasks | Item | Value | |---|---| | Method | `GET` | | Path | `/api/v3/contents/generations/tasks` | | Base URL | `https://api.unigateway.ai` | ```bash curl -sS "https://api.unigateway.ai/api/v3/contents/generations/tasks?page_num=1&page_size=20&filter.status=succeeded" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` Query parameters: | Parameter | Required | Description | |---|---|---| | `page_num` | No | Defaults to `1`, maximum `500` | | `page_size` | No | Defaults to `20`, maximum `500` | | `filter.status` | No | `queued`, `running`, `cancelled`, `succeeded`, `failed`, `expired`, `approved_asset_required`, or `content_adjustment_required` | | `filter.task_ids` | No | Repeat this query parameter to filter by multiple task IDs | | `filter.model` | No | Filter by model ID | | `filter.service_tier` | No | Filter by request service tier | Response: ```json { "total": 1, "items": [ { "id": "task_123456", "status": "succeeded", "model": "doubao-seedance-2-0-fast-260128", "created_at": 1781255171, "updated_at": 1781255427, "content": { "video_url": "https://video.demo.url" } } ] } ``` ## Delete Task | Item | Value | |---|---| | Method | `DELETE` | | Path | `/api/v3/contents/generations/tasks/{id}` | | Base URL | `https://api.unigateway.ai` | ```bash curl -sS -X DELETE "https://api.unigateway.ai/api/v3/contents/generations/tasks/task_123456" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` Delete behavior: - Queued tasks are cancelled and return `{}` - Running tasks cannot be deleted - Already-cancelled tasks cannot be deleted again - Completed or failed tasks return `{}` when deletion succeeds - Do not rely on querying a task after deleting it ## Common Failures | Status | Cause | Action | |---|---|---| | `400` | Invalid status filter, page value, or delete state | Fix the parameter or task state | | `401` / `403` | Invalid API key or insufficient permissions | Check the API key and account access | | `404` | Task ID not found | Confirm the task ID and account | | `429` | Polling too aggressively | Increase backoff and reduce concurrent queries | | `5xx` | Gateway or upstream supplier error | Retry with capped backoff | # ByteDance Seedance / Asset Libraries > Category: Video Generation | Last updated: 2026-08-09 Manage Seedance-compatible asset groups and assets for reusable video workflows. # ByteDance Seedance / Asset Libraries Manage reusable Seedance asset groups and assets for reference-based video workflows. Use asset libraries when a workflow needs material review, allowlisting, reusable character or scene references, or repeated generation around the same approved material. Ordinary text-to-video and simple image-to-video requests can use the video task API without asset libraries. ## Endpoint | Item | Value | |---|---| | Base URL | `https://api.unigateway.ai` | | Auth | `Authorization: Bearer $UNIGATEWAY_API_KEY` | ## Endpoints | Operation | Method | Path | |---|---|---| | Create asset group | `POST` | `/api/v3/asset-groups` | | List asset groups | `GET` | `/api/v3/asset-groups` | | Get asset group | `GET` | `/api/v3/asset-groups/{groupId}` | | Update asset group | `PATCH` | `/api/v3/asset-groups/{groupId}` | | Create asset | `POST` | `/api/v3/assets` | | List assets | `GET` | `/api/v3/assets` | | Get asset | `GET` | `/api/v3/assets/{assetId}` | | Update asset | `PATCH` | `/api/v3/assets/{assetId}` | | Delete asset | `DELETE` | `/api/v3/assets/{assetId}` | Request and response fields use Volcengine-style PascalCase names such as `Name`, `Description`, `GroupType`, `GroupId`, `URL`, `AssetType`, and `Id`. When creating an asset group, include a Seedance model ID so the group is consistently assigned to the intended domestic or global Seedance version. If no model ID is provided, the system selects from available channels automatically, which may not consistently choose the expected version. ## Create Asset Group ```bash curl -sS -X POST "https://api.unigateway.ai/api/v3/asset-groups" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "Name": "Seedance Character Library", "Description": "Reference characters for story-consistent renders.", "GroupType": "AIGC", "ModelId": "dreamina-seedance-2-0-260128" }' ``` | Field | Type | Required | Description | |---|---|---|---| | `Name` | string | Yes | Asset group name | | `Description` | string | No | Description | | `GroupType` | string | No | Defaults to `AIGC`; only `AIGC` is currently accepted | | `ModelId` / `model_id` | string | Recommended | Seedance model ID used to select the matching domestic or global version. For example, `dreamina-seedance-2-0-260128` creates a group assigned to global Dreamina Seedance 2.0 | Response: ```json { "Id": "uag_123", "ResponseMetadata": { "Action": "CreateAssetGroup", "Region": "cn-beijing", "RequestId": "", "Service": "ark", "Version": "2024-01-01" }, "Result": { "Id": "uag_123" } } ``` ## List Asset Groups ```bash curl -sS "https://api.unigateway.ai/api/v3/asset-groups?page_num=1&page_size=50&filter.group_type=AIGC" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` | Query parameter | Required | Description | |---|---|---| | `page_num` | No | Defaults to `1` | | `page_size` | No | Defaults to `50`, maximum `200` | | `filter.name` | No | Filter by group name | | `filter.group_ids` | No | Comma-separated group IDs | | `filter.group_type` | No | If provided, must be `AIGC` | Response: ```json { "ResponseMetadata": { "Action": "ListAssetGroups", "Region": "cn-beijing", "RequestId": "", "Service": "ark", "Version": "2024-01-01" }, "Result": { "AssetGroups": [ { "Id": "uag_123", "Name": "Seedance Character Library", "Title": "Seedance Character Library", "Description": "Reference characters for story-consistent renders.", "GroupType": "AIGC", "ProjectName": "default", "CreatedAt": 1781256203, "UpdatedAt": 1781256203 } ], "PageNumber": 1, "PageSize": 50, "TotalCount": 1 } } ``` Some deployments may also include top-level compatibility fields such as `Items`, `TotalCount`, `PageNumber`, `PageSize`, `Limit`, and `Offset`. ## Get Asset Group ```bash curl -sS "https://api.unigateway.ai/api/v3/asset-groups/uag_123" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` Response: ```json { "ResponseMetadata": { "Action": "GetAssetGroup", "Region": "cn-beijing", "RequestId": "", "Service": "ark", "Version": "2024-01-01" }, "Result": { "Id": "uag_123", "Name": "Seedance Character Library", "Description": "Reference characters for story-consistent renders.", "GroupType": "AIGC", "ProjectName": "default", "CreateTime": "2026-06-12T09:23:23Z", "UpdateTime": "2026-06-12T09:23:23Z" } } ``` ## Update Asset Group Provide at least one of `Name` or `Description`. ```bash curl -sS -X PATCH "https://api.unigateway.ai/api/v3/asset-groups/uag_123" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "Name": "Seedance Resource Library", "Description": "Approved resources for Seedance workflows." }' ``` Response: ```json { "ResponseMetadata": { "Action": "UpdateAssetGroup", "Region": "cn-beijing", "RequestId": "", "Service": "ark", "Version": "2024-01-01" }, "Result": { "Id": "uag_123" } } ``` ## Create Asset ```bash curl -sS -X POST "https://api.unigateway.ai/api/v3/assets" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "Name": "hero-reference-01", "AssetType": "Image", "URL": "https://example.com/assets/hero-reference-01.jpg", "GroupId": "uag_123" }' ``` | Field | Type | Required | Description | |---|---|---|---| | `Name` | string | Yes | Asset display name | | `URL` | string | Yes | Source file URL | | `GroupId` | string | Yes | Asset group ID returned by create or list asset groups | | `AssetType` | string | Recommended | `Image`, `Video`, or `Audio`; defaults to `Image` when omitted | Response: ```json { "Id": "ua_123", "ResponseMetadata": { "Action": "CreateAsset", "Region": "cn-beijing", "RequestId": "", "Service": "ark", "Version": "2024-01-01" }, "Result": { "Id": "ua_123" } } ``` Use the returned asset ID in video generation as `asset://ua_123`. ## List Assets ```bash curl -sS "https://api.unigateway.ai/api/v3/assets?page_num=1&page_size=20&filter.group_ids=uag_123&filter.group_type=AIGC&filter.statuses=Active&sort_by=created_at&sort_order=asc" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` | Query parameter | Required | Description | |---|---|---| | `page_num` | No | Defaults to `1` | | `page_size` | No | Defaults to `10`, maximum `100` | | `filter.group_ids` | Yes | Comma-separated group IDs | | `filter.group_type` | Yes | Must be `AIGC` | | `filter.statuses` | No | Comma-separated statuses such as `Active`, `Processing`, `Failed` | | `filter.name` | No | Filter by asset name | | `sort_by` | No | `created_at`, `updated_at`, or `group_id` | | `sort_order` | No | `asc` or `desc` | Response: ```json { "ResponseMetadata": { "Action": "ListAssets", "Region": "cn-beijing", "RequestId": "", "Service": "ark", "Version": "2024-01-01" }, "Result": { "Assets": [ { "Id": "ua_123", "Name": "hero-reference-01", "URL": "https://example.com/assets/hero-reference-01.jpg", "GroupId": "uag_123", "AssetType": "Image", "Status": "Active", "ProjectName": "default", "CreatedAt": 1781257489, "UpdatedAt": 1781257865 } ], "PageNumber": 1, "PageSize": 20, "TotalCount": 1 } } ``` ## Get Asset ```bash curl -sS "https://api.unigateway.ai/api/v3/assets/ua_123" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` Response: ```json { "ResponseMetadata": { "Action": "GetAsset", "Region": "cn-beijing", "RequestId": "", "Service": "ark", "Version": "2024-01-01" }, "Result": { "Id": "ua_123", "Name": "hero-reference-01", "URL": "https://example.com/assets/hero-reference-01.jpg", "GroupId": "uag_123", "AssetType": "Image", "Status": "Active", "ProjectName": "default", "Moderation": { "Strategy": "Default" }, "CreateTime": "2026-06-12T09:34:53Z", "UpdateTime": "2026-06-12T09:35:00Z" } } ``` ## Update Asset Currently only `Name` is updated. ```bash curl -sS -X PATCH "https://api.unigateway.ai/api/v3/assets/ua_123" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "Name": "hero-reference-01-rename" }' ``` Response: ```json { "ResponseMetadata": { "Action": "UpdateAsset", "Region": "cn-beijing", "RequestId": "", "Service": "ark", "Version": "2024-01-01" }, "Result": { "Id": "ua_123" } } ``` ## Delete Asset ```bash curl -sS -X DELETE "https://api.unigateway.ai/api/v3/assets/ua_123" \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` Response: ```json { "ResponseMetadata": { "Action": "DeleteAsset", "Region": "cn-beijing", "RequestId": "", "Service": "ark", "Version": "2024-01-01" }, "Result": {} } ``` ## ID Usage - Asset groups and assets are isolated per account - Use the returned `Id` for follow-up queries, updates, deletes, and `asset://` references - Use only assets that are accessible to the same API key used to create the video task ## Integrations # OpenAI SDK > Category: Integrations | Last updated: 2026-08-09 Connect OpenAI-compatible SDKs to UniGateway with a custom base URL and live model discovery. # OpenAI SDK Use UniGateway with the official OpenAI SDK by overriding base URL and API key. ## Prerequisites - OpenAI SDK installed - UniGateway API key ## Install ## Code examples ### python ```python from openai import OpenAI client = OpenAI(api_key="", base_url="https://api.unigateway.ai/v1") print(client.models.list()) ``` ### typescript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1" }); console.log(await client.models.list()); ``` ```bash # Python pip install openai # TypeScript npm install openai ``` For chat completions examples with the OpenAI SDK, see [Quickstart](/docs/quickstart). The key change is overriding `base_url` / `baseURL` to `https://api.unigateway.ai/v1`. Fetch `GET /v1/models` before choosing a model ID. Start with `chat.completions`. Enable streaming or tools after basic non-streaming requests work. # Dify > Category: Integrations | Last updated: 2026-08-09 Use UniGateway as an OpenAI-compatible upstream in Dify. # Dify Add UniGateway as an OpenAI-compatible model provider in Dify. ## Prerequisites - Dify instance running - UniGateway API key ## Configuration In Dify admin panel, go to **Settings > Model Providers** and add an OpenAI-compatible provider: | Field | Value | |---|---| | Base URL | `https://api.unigateway.ai/v1` | | API Key | Your UniGateway key | | Model Name | Exact ID from `GET /v1/models` | > A duplicate `/v1` in the base URL will cause requests to fail. Requests hitting `/v1/v1/...` will fail. ## Verify Send a short test prompt to confirm the connection works. ```bash curl https://api.unigateway.ai/v1/models \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` ## Capability Mapping | Workload | Recommendation | |---|---| | Chat apps | Standard chat-completions models | | Automation workflows | Models verified for tool use | | Embeddings | `text-embedding-3-small` or equivalent | | Image / audio | Validate separately | ## Common Failures | Problem | Resolution | |---|---| | Provider connects but inference fails | Check exact model ID from `GET /v1/models` | | Requests hit `/v1/v1/...` | Remove duplicate `/v1` from base URL | | Features work inconsistently | Treat each endpoint family as a separate rollout | # OpenWebUI > Category: Integrations | Last updated: 2026-08-09 Use UniGateway as an OpenAI-compatible backend in OpenWebUI. # OpenWebUI Add UniGateway as an OpenAI-compatible connection in OpenWebUI. ## Prerequisites - OpenWebUI instance running - UniGateway API key ## Configuration Create one OpenAI-compatible connection: | Field | Value | |---|---| | Endpoint Base | `https://api.unigateway.ai/v1` | | API Key | Your UniGateway bearer token | | Model IDs | From `GET /v1/models` | ## Verify Send a short prompt to confirm models and chat work. ## Notes | Area | Recommendation | |---|---| | Model refresh | Re-sync after catalog changes | | Shared deployments | Use separate test and production keys | | Advanced features | Verify tools, files, multimodal routes one by one | ## Common Failures | Problem | Resolution | |---|---| | Connection saves but no models appear | Check API key scope and confirm `/v1/models` works | | Chat works, streaming inconsistent | Test streaming as a separate compatibility gate | | Workspaces behave differently | Check for cached config or duplicate connections | # AI Coding Tools > Category: Integrations | Last updated: 2026-08-09 Use UniGateway with coding assistants and agent tools that support OpenAI-compatible base URLs. # AI Coding Tools Coding assistants, CLI tools, and automation workflows can use UniGateway if they support an OpenAI-compatible base URL override. ## Requirements The tool must let you configure: - API key - Base URL - Model ID If a tool only supports vendor-native endpoints with no base URL override, UniGateway cannot be integrated directly. ## Configuration ```bash export OPENAI_API_KEY="$UNIGATEWAY_API_KEY" export OPENAI_API_BASE="https://api.unigateway.ai/v1" ``` Choose a model ID from `GET /v1/models`. ## Validation Flow 1. Send one non-stream request to confirm basic connectivity 2. Test tool calling and multi-step workflows after plain completion works 3. Check how the tool handles retries, streaming, and partial failures 4. Keep a fallback model ready for interactive coding traffic ## Good Fit vs Poor Fit | Good fit | Poor fit | |---|---| | Tools that support OpenAI-compatible chat completions | Tools that hardcode one vendor endpoint | | Systems with configurable environment variables | Systems requiring vendor-native auth only | | Applications with explicit model routing config | Desktop apps with no custom base URL support | ## Notes - Interactive coding traffic is latency-sensitive - Re-test tool use and structured outputs when switching model families - Keep test and production credentials separate # Codex CLI Integration > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in Codex-Cli. # Codex CLI Integration OpenAI Codex CLI is an autonomous coding agent that runs in your terminal. By connecting Codex to UniGateway, you gain access to a wide range of models through a single unified endpoint, with centralized key management, automatic fallback, and no need to modify any upstream provider SDKs. ## Prerequisites - Node.js 22+ (required by Codex CLI) - A valid UniGateway API Key — see [Authentication](/docs/authentication) ## Installation Install the Codex CLI globally: ```bash npm install -g @openai/codex # or pnpm install -g @openai/codex ``` ## Configuration Codex reads its configuration from `~/.codex/config.toml`. Create this file with the following content: ```toml model_provider = "unigateway" model = "gpt-5.4" model_reasoning_effort = "medium" [model_providers.unigateway] name = "UniGateway" base_url = "https://api.unigateway.ai/v1" env_key = "UNIGATEWAY_API_KEY" wire_api = "responses" ``` > **Important:** `wire_api` must be set to `"responses"`. Codex uses the OpenAI Responses API, and UniGateway fully supports this protocol. Do **not** use `"chat_completions"` — Codex relies on the Responses API's tool-use and multi-turn capabilities. ### Environment Variable Set your UniGateway API key in your shell configuration (`~/.zshrc` or `~/.bashrc`): ```bash export UNIGATEWAY_API_KEY="your-api-key-here" ``` Then reload your shell: ```bash source ~/.zshrc # or source ~/.bashrc ``` ### Model Selection The `model` field in `config.toml` specifies which model Codex uses. You can use any model available on UniGateway that supports the Responses API. Common choices: | Use Case | Recommended Models | Notes | |----------|-------------------|-------| | Complex coding tasks | `gpt-5.4`, `claude-sonnet-4-6` | Strong reasoning and tool use | | Balanced performance | `gpt-4.1`, `gemini-2.5-pro` | Good quality with reasonable speed | | Fast iterations | `gpt-4.1-mini`, `deepseek-chat` | Quick responses for simple tasks | > **Tip:** To see all available models, run: > ```bash > curl https://api.unigateway.ai/v1/models \ > -H "Authorization: Bearer $UNIGATEWAY_API_KEY" > ``` ### Reasoning Effort The `model_reasoning_effort` field controls how much reasoning the model applies: | Value | Behavior | |-------|----------| | `low` | Fast responses, minimal reasoning — suitable for simple tasks | | `medium` | Balanced reasoning — good default for most coding tasks | | `high` | Deep reasoning — best for complex multi-step problems | ## Verification After configuration, verify the connection: 1. **Check the API key is set:** ```bash echo $UNIGATEWAY_API_KEY ``` 2. **Test basic connectivity:** ```bash curl https://api.unigateway.ai/v1/models \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` 3. **Launch Codex:** ```bash cd your-project codex ``` Type a simple task (e.g., "list the files in this directory") and confirm Codex responds through UniGateway. ## Troubleshooting | Issue | Solution | |-------|----------| | `UNIGATEWAY_API_KEY` not found | Ensure the environment variable is set in `~/.zshrc` or `~/.bashrc` and reload with `source` | | Connection refused / timeout | Verify `base_url` is `https://api.unigateway.ai/v1` (must include `/v1`) | | Model not found | Run `GET /v1/models` to confirm the model ID is available | | `wire_api` error | Confirm `wire_api = "responses"` in `config.toml` | | Config not loaded | Ensure `config.toml` is at `~/.codex/config.toml` | ## How It Works ``` +-------------+ +----------------------+ +-----------------+ | Codex CLI |----->| UniGateway Proxy |----->| Model Provider | | (terminal) | | api.unigateway.ai | | (OpenAI, etc.) | +-------------+ +----------------------+ +-----------------+ | | | Responses API | Automatic fallback, | (wire_api=responses) | key management, | | unified logging ``` Codex sends requests using the OpenAI Responses API protocol. UniGateway translates and routes these requests to the appropriate model provider, handling authentication, fallback, and observability transparently. ## Related Topics - [Authentication](/docs/authentication) — Get and manage your UniGateway API key - [Models](/docs/models) — Browse available models and their capabilities - [Endpoint Compatibility](/docs/endpoint-compatibility) — Check which endpoints each model supports - [Coding Tools and Agents](/docs/coding-tools-and-agents) — Connect other coding tools to UniGateway # Codex Desktop Integration > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in Codex-Desktop. # Codex Desktop Integration The Codex desktop app brings the Codex coding agent into a native GUI experience. By connecting Codex Desktop to UniGateway, you gain access to a wide range of models through a single unified endpoint, with centralized key management, automatic fallback, and no need to modify any upstream provider SDKs. ## Prerequisites The Codex desktop app must be installed. ## Configuration 1. Open **Settings**, click **Configuration**, find **Custom config.toml settings**, click **Open config.toml** to edit it. > **Note:** The desktop app and the CLI share the same `config.toml` file, so the configuration below is identical to the CLI version. 2. Paste the following content into `config.toml` and save: ```toml model_provider = "unigateway" model = "gpt-5.5" model_reasoning_effort = "medium" [model_providers.unigateway] name = "UniGateway" base_url = "https://api.unigateway.ai/v1" env_key = "UNIGATEWAY_API_KEY" wire_api = "responses" ``` ## Environment Variable Configure the environment variable. The steps are identical to the CLI version: 1. Open **Control Panel** → **System** → **Advanced system settings**, or search for **Edit the system environment variables** from the Start menu. 2. In the **System Properties** window, click the **Environment Variables...** button. 3. Under **User variables** or **System variables**, click **New...**. 4. Fill in: - **Variable name:** `UNIGATEWAY_API_KEY` - **Variable value:** `your-api-key-here` 5. Click **OK** to save and close all windows. 6. Reopen the terminal (e.g., PowerShell, CMD) so the new environment variable takes effect. 7. Verify the variable is set: in a newly opened PowerShell terminal, run the command below — it should return the API key you just created: ```powershell echo $env:UNIGATEWAY_API_KEY ``` ## Final Verification In the Codex desktop app, start a chat, select the model defined above, and send a short message. If the AI responds successfully, the integration is complete. # OpenCode Integration > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in OpenCode through the OpenAI-compatible provider path. # OpenCode Integration OpenCode is a terminal-first AI coding agent. By connecting OpenCode to UniGateway, you gain access to a wide range of models through a single unified endpoint, with centralized key management, automatic fallback, and no need to modify any upstream provider SDKs. ## Compatibility Notes UniGateway fully supports the OpenAI Chat Completions protocol and can be seamlessly integrated into OpenCode as an OpenAI-compatible provider. The configuration uses the `@ai-sdk/openai-compatible` npm package with `baseURL` pointing to the UniGateway endpoint. Note that the `baseURL` must end with `/v1` — the full URL is `https://api.unigateway.ai/v1`. OpenCode appends downstream paths (such as `/chat/completions`) automatically, so do **not** omit the `/v1` suffix or add it a second time. OpenCode relies on tool calling (function calling) for multi-step coding workflows. Ensure the model you select supports tool use — most models available on UniGateway do, but see [Endpoint Compatibility](/docs/endpoint-compatibility) for details. ## Configuration ### Step 1: Install OpenCode OpenCode offers multiple installation methods. Choose the one that best fits your system environment: - **Quick install (recommended)** — Run the following command: ```bash curl -fsSL https://opencode.ai/install | bash ``` - **npm / pnpm / yarn / bun** — Install globally: ```bash npm i -g opencode-ai@latest ``` - **Homebrew (macOS and Linux)**: ```bash brew install sst/tap/opencode ``` - **Scoop / Chocolatey (Windows)**: ```bash scoop bucket add extras scoop install extras/opencode ``` ```bash choco install opencode ``` > For more installation options, refer to the [OpenCode official documentation](https://opencode.ai/docs/#install). ### Step 2: Configure OpenCode Find or create the OpenCode configuration file (`opencode.json`) and paste the following content into it: ```json { "$schema": "https://opencode.ai/config.json", "provider": { "unigateway": { "npm": "@ai-sdk/openai-compatible", "options": { "baseURL": "https://api.unigateway.ai/v1" }, "models": { "gpt-5.5": { "name": "gpt-5.5" } } } } } ``` > **Important:** The model name in the `models` field (e.g., `gpt-5.5`) should match a model ID from `GET /v1/models` — see [Models](/docs/models). You can switch to any supported model at any time. **Model Recommendations** We recommend adding models across different capability and price tiers to suit various use cases: - **High-complexity tasks** (multi-file refactors, debugging, complex logic): `claude-sonnet-4-6`, `gpt-5.2` - **Balanced tasks** (general coding, explanations): `gemini-2.5-pro`, `gpt-4.1` - **Fast iterations** (quick edits, simple questions): `gpt-4.1-mini`, `deepseek-chat` ### Step 3: Authenticate with UniGateway Run the following command to start the login process: ```bash opencode auth login ``` On the provider selection screen, choose **Other** and enter the provider ID `unigateway`. Then enter your UniGateway API Key when prompted. > **Important:** You must select **Other** on the provider selection screen and enter `unigateway` as the provider ID. Do not select a pre-listed provider. **Getting an API Key** You can obtain or create your API Key in the [UniGateway Console](https://unigateway.ai). ### Step 4: Start a Conversation Launch OpenCode and type a simple message to verify the connection: ```bash opencode ``` Type a message and confirm you receive a response. Use the `/models` command to view and switch between available models at any time. **Verification Checklist** | Check | What to Confirm | |-------|-----------------| | Authentication works | OpenCode accepts your API Key without 401/403 errors | | Chat works | OpenCode returns a non-empty response to a prompt | | Model available | The configured model appears in the model selector | | Tool calling works | OpenCode can read files, run commands, and write code | ## Usage Experience Once configured, OpenCode acts as a terminal-first coding agent powered by UniGateway models: - **Terminal-first coding** — Interact with AI directly in your terminal for coding tasks - **Tool calling** — Models can use tools to interact with your filesystem, terminal, and browser - **Model switching** — Use `/models` to switch between models at any time - **Automatic fallback** — When configured with [Model Selection and Fallback](/docs/model-selection-and-fallback), requests automatically retry on alternate models **Tool Call Compatibility** Different models have varying levels of tool call support. When a model lacks tool call support, OpenCode will fall back to text-only mode. See [Endpoint Compatibility](/docs/endpoint-compatibility) for details on which models support tool use. ## Troubleshooting ### API Key Error **Issue:** OpenCode reports an invalid or unauthorized API Key. **Solutions:** - Check if the API Key was copied correctly — avoid extra spaces or line breaks - Confirm the API Key is active and the account has sufficient balance - Verify the API Key format — UniGateway API Keys start with a specific prefix - Re-run `opencode auth login` and reconfigure ### Config File Not Found **Issue:** OpenCode cannot find or read the configuration file. **Solutions:** - Make sure `opencode.json` is in the correct location (project root or home directory) - Check that the JSON syntax is valid — run it through a JSON validator - Ensure the `$schema`, `provider`, and `models` fields are all present ### Model Not Available **Issue:** The configured model does not respond or returns a 404. **Solutions:** - Confirm the model ID in `opencode.json` matches exactly what `GET /v1/models` returns — a single character mismatch causes a 404 - Verify that `baseURL` is set to `https://api.unigateway.ai/v1` (note the `/v1` suffix) - Use `/models` within OpenCode to check which models are currently available ### Authentication Provider Error **Issue:** Provider selection during `opencode auth login` does not complete. **Solutions:** - Make sure you selected **Other** (not a pre-listed provider) and entered `unigateway` as the provider ID - Confirm the `provider.unigateway` section in `opencode.json` is correctly configured with the `@ai-sdk/openai-compatible` npm package - Re-run `opencode auth login` and carefully follow the provider selection steps ### Tool Calling Not Working **Issue:** OpenCode responds with text but cannot execute tools (file reads, commands, etc.). **Solutions:** - Verify the model supports tool use — not all models support function calling - Switch to a model with known tool call support (e.g., `claude-sonnet-4-6`, `gpt-4.1`) - See [Endpoint Compatibility](/docs/endpoint-compatibility) for a list of models with tool support ### Streaming Issues **Issue:** Responses are truncated or streaming behaves unexpectedly. **Solutions:** - Check whether the model supports streaming — see [Streaming](/docs/streaming) - Inspect the raw request/response in OpenCode logs for error details - If output is truncated, verify your token limit settings ## See Also - [Chat Completions](/docs/chat-completions) — the core endpoint used by OpenCode - [Models](/docs/models) — query available models and their capabilities - [Streaming](/docs/streaming) — configure streaming responses for real-time output - [Authentication](/docs/authentication) — manage API keys and troubleshoot auth errors - [Endpoint Compatibility](/docs/endpoint-compatibility) — verify which endpoints and features are available - [Model Selection and Fallback](/docs/model-selection-and-fallback) — configure fallback chains for resilience - [Error Handling and Retries](/docs/error-handling-and-retries) — handle rate limits and transient failures - [Coding Tools and Multi-step Workflows](/docs/coding-tools-and-agents) — general coding tool integration # GitHub Copilot Integration > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in Copilot through the OpenAI-compatible provider path. # GitHub Copilot Integration GitHub Copilot is Microsoft's AI coding assistant, integrated into VS Code to provide intelligent suggestions, auto-completions, and code generation as you write. By connecting GitHub Copilot to UniGateway through the OAI Compatible Provider extension, you can drive Copilot with your own API key — enjoying a more flexible and cost-effective AI coding experience. ## Compatibility Notes UniGateway fully supports the OpenAI Chat Completions protocol and can be seamlessly integrated into GitHub Copilot via the OAI Compatible Provider for Copilot extension. Set `oaicopilot.baseUrl` to the UniGateway endpoint and declare available models in `settings.json`. Note that the Base URL must end with `/v1` — the full URL is `https://api.unigateway.ai/v1`. Copilot appends downstream paths (such as `/chat/completions`) automatically, so do **not** omit the `/v1` suffix or add it a second time. GitHub Copilot relies on tool calling (function calling) for multi-step coding workflows. Ensure the model you select supports tool use — most models available on UniGateway do, but see [Endpoint Compatibility](/docs/endpoint-compatibility) for details. ## Configuration ### Step 1: Install Required Extensions Install the following two extensions in VS Code: 1. **GitHub Copilot Chat** — Search for "GitHub Copilot Chat" in the Extensions marketplace and click Install. 2. **OAI Compatible Provider for Copilot** — Search for "OAI Compatible Provider for Copilot" in the Extensions marketplace and click Install. > Both extensions are required. The OAI Compatible Provider bridges your custom OpenAI-compatible endpoint into Copilot Chat. ### Step 2: Configure OAI Compatible Provider After installation, open VS Code settings: - Click the gear icon in the left sidebar (or use `Cmd+,` / `Ctrl+,`) - Search for `oaicopilot` Find and configure the following two settings: **Set the Base URL** Set `oaicopilot.baseUrl` to: ``` https://api.unigateway.ai/v1 ``` **Configure Available Models** Add the model list to your VS Code `settings.json`. Open the Command Palette (`Cmd+Shift+P` / `Ctrl+Shift+P`), type "Open User Settings (JSON)", and add: ```json "oaicopilot.models": ["gpt-5.5"] ``` > **Important:** Model IDs in the list should match those returned by `GET /v1/models` — see [Models](/docs/models). You can add or remove models at any time. **Model Recommendations** We recommend adding models across different capability and price tiers to suit various use cases: - **High-complexity tasks** (multi-file refactors, debugging, complex logic): `claude-sonnet-4-6`, `gpt-5.2` - **Balanced tasks** (general coding, explanations): `gemini-2.5-pro`, `gpt-4.1` - **Fast iterations** (quick edits, simple questions): `gpt-4.1-mini`, `deepseek-chat` ### Step 3: Add Models in Copilot Chat 1. Open the **Copilot Chat** view in the VS Code sidebar (or use `Cmd+Shift+I` / `Ctrl+Shift+I`). 2. Click the **model selector** above the chat input, then choose **"Manage Models..."** from the dropdown. 3. Click **"Add Models"**. 4. Select **"OAI Compatible"** from the provider list. 5. Enter your UniGateway **API Key** (the key is securely stored locally). 6. Check the models you want to appear in the model selector. **Getting an API Key** You can obtain or create your API Key in the [UniGateway Console](https://unigateway.ai). > **Important:** You must select the **"OAI Compatible"** provider — not other pre-listed providers. ### Step 4: Start Using Once configured, you can: - Switch between different UniGateway models in the model selector - Get intelligent code completion suggestions as you type - Use the Chat view to interact with AI and get coding assistance **Verification Checklist** | Check | What to Confirm | |-------|-----------------| | Extensions installed | Both GitHub Copilot Chat and OAI Compatible Provider are installed and enabled | | Base URL correct | `oaicopilot.baseUrl` is set to `https://api.unigateway.ai/v1` | | Models configured | `oaicopilot.models` contains at least one valid model ID | | Authentication works | Copilot accepts your API Key without 401/403 errors | | Chat works | Copilot Chat returns a non-empty response to a prompt | ## Keyboard Shortcuts | Action | macOS | Windows / Linux | Use Case | |--------|-------|-----------------|----------| | Open Chat | `Cmd+Shift+I` | `Ctrl+Shift+I` | Quickly open the AI chat view | | Inline Chat | `Cmd+I` | `Ctrl+I` | Interact with AI directly on the current code line | | Accept suggestion | `Tab` | `Tab` | Accept the AI-suggested code completion | | Dismiss suggestion | `Esc` | `Esc` | Cancel the current code suggestion | | Next suggestion | `Option+]` | `Alt+]` | View the next alternative suggestion | | Previous suggestion | `Option+[` | `Alt+[` | View the previous alternative suggestion | > Some shortcuts may conflict with system or other application shortcuts. You can customize them in VS Code settings if needed. ## Troubleshooting ### Extension Not Found **Issue:** Cannot find "OAI Compatible Provider for Copilot" in the Extensions marketplace. **Solutions:** - Confirm your VS Code version is 1.104.0 or above — check with `code --version` or via `Help > About` in the menu - Verify that the Extensions marketplace is accessible - Try installing directly from the extension's marketplace page ### Invalid Base URL **Issue:** Copilot reports a connection error or invalid Base URL. **Solutions:** - Confirm `oaicopilot.baseUrl` is set to `https://api.unigateway.ai/v1` (note the `/v1` suffix) - Check whether a proxy or firewall is blocking requests - Run `curl https://api.unigateway.ai/v1/models` in your terminal to verify the endpoint is reachable ### Models Not Appearing in Selector **Issue:** Configured models don't appear in the model selector. **Solutions:** - Confirm the model IDs in `oaicopilot.models` in `settings.json` match exactly what `GET /v1/models` returns - Ensure you selected "OAI Compatible" as the provider in "Manage Models" - Reload the VS Code window (`Cmd+Shift+P` / `Ctrl+Shift+P` → "Reload Window") ### API Key Authentication Failure **Issue:** Copilot shows 401 or 403 errors after entering the API Key. **Solutions:** - Check if the API Key was copied correctly — avoid extra spaces or line breaks - Confirm the API Key is active and the account has sufficient balance - Re-enter the API Key through "Manage Models" → "Add Models" ### Tool Calling Not Working **Issue:** Copilot responds with text but cannot execute tools (file reads, commands, etc.). **Solutions:** - Verify the model supports tool use — not all models support function calling - Switch to a model with known tool call support (e.g., `claude-sonnet-4-6`, `gpt-4.1`) - See [Endpoint Compatibility](/docs/endpoint-compatibility) for a list of models with tool support ## See Also - [Chat Completions](/docs/chat-completions) — the core endpoint used by Copilot - [Models](/docs/models) — query available models and their capabilities - [Streaming](/docs/streaming) — configure streaming responses for real-time output - [Authentication](/docs/authentication) — manage API keys and troubleshoot auth errors - [Endpoint Compatibility](/docs/endpoint-compatibility) — verify which endpoints and features are available - [Coding Tools and Multi-step Workflows](/docs/coding-tools-and-agents) — general coding tool integration # WorkBuddy Integration > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in WorkBuddy. # WorkBuddy Integration WorkBuddy is a desktop AI client that supports custom model providers. By integrating WorkBuddy with UniGateway, you can access the extensive model catalog aggregated on the UniGateway platform within WorkBuddy, enjoying flexible model selection and competitive pricing. ## Compatibility Notes UniGateway fully supports the OpenAI Chat Completions protocol and can be seamlessly integrated into clients like WorkBuddy that support custom OpenAI-compatible endpoints. Simply add UniGateway as a custom model provider to use all models that support the OpenAI protocol. Note that the base URL for the OpenAI protocol is `https://api.unigateway.ai/v1`. ## Configuration ### Step 1: Download and Install WorkBuddy Download and install the WorkBuddy desktop client. ### Step 2: Add a Custom Model 1. Open WorkBuddy and go to the **Settings → Models** page. 2. In the model settings, locate **Custom Models** and click **Add Model**. 3. Fill in the following details in the configuration dialog: - **Provider:** select **Custom** - **API Endpoint:** `https://api.unigateway.ai/v1` - **API Key:** enter your UniGateway API Key - **Model Name:** enter the model ID you want to use (e.g., `gpt-5.5`) 4. Click **Save**. 5. After saving, the model you just added will appear in the **Saved Models** list. > **Important:** Make sure to enter your actual UniGateway API Key. You can obtain your API Key from the UniGateway Console. > The model name should match a model ID returned by `GET /v1/models` — see [Models](/docs/models). You can add multiple models by repeating this step. **Model Recommendations** We recommend adding models across different capability and price tiers to suit various use cases: - **High-performance models:** e.g., `claude-sonnet-4-6`, `gpt-4.1` - **Balanced models:** e.g., `gemini-2.5-pro`, `grok-3-fast` - **Cost-effective models:** e.g., `deepseek-chat`, `qwen3-coder-plus` ### Step 3: Verify the Connection In the chat interface, select the model you just added, send a message to the AI, and confirm that you receive a response. > If you receive a successful response, the integration is complete. You can switch between any added models at any time from the chat interface. ## Troubleshooting ### API Key Error **Issue:** Invalid or unauthorized API Key when adding a model **Solutions:** - Check if the API Key was copied correctly; avoid extra spaces or line breaks - Verify the API Key is activated and the account has sufficient balance - Log in to the UniGateway Console to check the API Key status ### Model Invocation Failed **Issue:** Errors returned after selecting a model and sending a message **Solutions:** - Confirm the model ID matches exactly what `GET /v1/models` returns — a single character mismatch causes a 404 - Verify that the API endpoint is set to `https://api.unigateway.ai/v1` (note the `/v1` suffix) - Check whether your account balance is sufficient - Try switching to another model to determine if the issue is specific to a particular model ### Connection Refused / Timeout **Issue:** Unable to reach the UniGateway endpoint **Solutions:** - Verify network connectivity and try accessing `https://api.unigateway.ai` in your browser - Ensure firewall or proxy settings are not blocking access to the UniGateway servers - Confirm the API endpoint includes the `/v1` suffix ## See Also - [Chat Completions](/docs/chat-completions) — core text generation endpoint - [Models](/docs/models) — verify available models - [Authentication](/docs/authentication) — API key setup - [Endpoint Compatibility](/docs/endpoint-compatibility) — check supported endpoints - [Overview](/docs/overview) — UniGateway platform introduction # pi-dev Integration > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in Pi. # pi-dev Integration pi-dev is a terminal-first AI coding agent distributed via npm. By connecting pi to UniGateway, you gain access to a wide range of models through a single unified endpoint, with centralized key management, automatic fallback, and no need to modify any upstream provider SDKs. ## Compatibility Notes UniGateway fully supports the OpenAI Chat Completions protocol and can be seamlessly integrated into pi as a custom provider. The configuration uses the `openai-completions` API type with `baseUrl` pointing to the UniGateway endpoint. Note that `baseUrl` must end with `/v1` — the full URL is `https://api.unigateway.ai/v1`. pi appends downstream paths automatically, so do **not** omit the `/v1` suffix or add it a second time. ## Configuration ### Step 1: Install pi Install the pi coding agent globally via npm: ```bash npm install -g @earendil-works/pi-coding-agent ``` ### Step 2: Configure models.json Create the configuration file at the following path: **Windows:** ``` C:\Users\\.pi\agent\models.json ``` **macOS / Linux:** ``` ~/.pi/agent/models.json ``` Paste the following content into it: ```json { "providers": { "unigateway": { "baseUrl": "https://api.unigateway.ai/v1", "api": "openai-completions", "apiKey": "your api key", "models": [ { "id": "gpt-5.5" } ] } } } ``` > **Important:** Replace the `apiKey` field with your actual UniGateway API Key. You can obtain your API Key from the UniGateway Console. > The model ID in the `models` array should match a model ID returned by `GET /v1/models` — see [Models](/docs/models). You can add multiple models to the array. **Model Recommendations** We recommend adding models across different capability and price tiers to suit various use cases: - **High-complexity tasks** (multi-file refactors, debugging, complex logic): `claude-sonnet-4-6`, `gpt-5.2` - **Balanced tasks** (general coding, explanations): `gemini-2.5-pro`, `gpt-4.1` - **Fast iterations** (quick edits, simple questions): `gpt-4.1-mini`, `deepseek-chat` ### Step 3: Verify the Connection Open a new PowerShell (or terminal) window and launch pi: ```bash pi ``` Send a simple message to the AI. If you receive a response, the integration is complete. ## Troubleshooting ### API Key Error **Issue:** pi reports an invalid or unauthorized API Key. **Solutions:** - Check if the API Key was copied correctly — avoid extra spaces or line breaks - Confirm the API Key is active and the account has sufficient balance - Re-edit `models.json` and verify the `apiKey` field ### Config File Not Found **Issue:** pi cannot find or read the configuration file. **Solutions:** - Make sure `models.json` is at `~/.pi/agent/models.json` (or `C:\Users\\.pi\agent\models.json` on Windows) - Check that the JSON syntax is valid — run it through a JSON validator - Ensure the `providers`, `baseUrl`, `api`, `apiKey`, and `models` fields are all present ### Model Not Available **Issue:** The configured model does not respond or returns a 404. **Solutions:** - Confirm the model `id` in `models.json` matches exactly what `GET /v1/models` returns — a single character mismatch causes a 404 - Verify that `baseUrl` is set to `https://api.unigateway.ai/v1` (note the `/v1` suffix) ### Connection Refused / Timeout **Issue:** Unable to reach the UniGateway endpoint. **Solutions:** - Verify network connectivity and try accessing `https://api.unigateway.ai` in your browser - Ensure firewall or proxy settings are not blocking access to the UniGateway servers - Confirm `baseUrl` includes the `/v1` suffix ## See Also - [Chat Completions](/docs/chat-completions) — the core endpoint used by pi - [Models](/docs/models) — query available models and their capabilities - [Authentication](/docs/authentication) — manage API keys and troubleshoot auth errors - [Endpoint Compatibility](/docs/endpoint-compatibility) — verify which endpoints and features are available - [Coding Tools and Agents](/docs/coding-tools-and-agents) — general coding tool integration # CC-Switch Integration > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in CC-Switch. # CC-Switch Integration CC-Switch is a desktop tool that lets you switch between AI provider configurations for coding tools like Claude Code, Codex, and Gemini CLI from a single UI. By connecting CC-Switch to UniGateway, you gain access to a wide range of models through a single unified endpoint, with centralized key management, automatic fallback, and no need to modify any upstream provider SDKs. ## Prerequisites - A valid UniGateway API Key - CC-Switch installed ## Install CC-Switch Choose the installation method that matches your operating system: - **macOS** — Install via Homebrew, or download the installer manually - **Windows** — Download the MSI installer or use the portable build - **Linux** — Use the .deb package or the AppImage ## Configuration Launch CC-Switch, select the target coding tool from the top navigation, click **Add Provider**, fill in the configuration, then click the switch button to activate the provider. The three coding tools are configured as follows. ### Claude Code (Anthropic protocol) | Field | Value | |-------|-------| | Provider Name | UniGateway | | Base URL | `https://api.unigateway.ai` | | API Key | Your UniGateway API Key | Configure the model mapping: | Role | Model ID | |------|----------| | Haiku | `claude-haiku-4-5` | | Sonnet | `claude-sonnet-4-6` | | Opus | `claude-opus-4-7` | ### Codex (OpenAI protocol) | Field | Value | |-------|-------| | Provider Name | UniGateway | | Base URL | `https://api.unigateway.ai/v1` | | API Key | Your UniGateway API Key | Configure Codex's `config.toml`: ```toml model_provider = "unigateway" model = "gpt-5.5" [model_providers.unigateway] name = "UniGateway" base_url = "https://api.unigateway.ai/v1" env_key = "UNIGATEWAY_API_KEY" wire_api = "responses" ``` ### Gemini CLI (Vertex AI protocol) | Field | Value | |-------|-------| | Provider Name | UniGateway | | Base URL | `https://api.unigateway.ai` | | API Key | Your UniGateway API Key | Configure environment variables: ```bash export GEMINI_API_BASE="https://api.unigateway.ai" export GEMINI_API_KEY="your-unigateway-api-key" ``` ## Final Verification In CC-Switch, switch to the UniGateway provider you just configured, then send a new request from the corresponding coding tool. If you receive a response, the integration is complete. # OpenClaw Integration > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in OpenClaw. # OpenClaw Integration OpenClaw is an npm-distributed CLI that runs a local AI gateway for coding workflows. By connecting OpenClaw to UniGateway, you gain access to a wide range of models through a single unified endpoint, with centralized key management, automatic fallback, and no need to modify any upstream provider SDKs. ## Prerequisites - Node.js installed - A valid UniGateway API Key ## Configuration ### Step 1: Install OpenClaw ```bash npm install -g openclaw@latest ``` ### Step 2: Install and start the gateway service Run the following two commands in order to install and start the gateway service: ```bash openclaw gateway install openclaw gateway start ``` ### Step 3: Configure OpenClaw manually Open `C:\Users\Administrator\.openclaw\openclaw.json` and add the following content to the configuration file: ```json { "agents": { "defaults": { "workspace": "C:\Users\Administrator\.openclaw\workspace", "model": { "primary": "unigateway/gpt-5.1" }, "models": { "unigateway/gpt-5.1": {} } } }, "models": { "mode": "merge", "providers": { "unigateway": { "baseUrl": "https://api.unigateway.ai/v1", "apiKey": "api-key", "models": [ { "id": "gpt-5.5", "name": "gpt-5.5(unigateway)" } ] } } } } ``` Replace `api-key` with your UniGateway API Key. ## Final Verification Open two terminal windows. Start the gateway in the first terminal: ```bash openclaw gateway ``` Start a chat session in the second terminal: ```bash openclaw chat ``` Send a simple message. If the AI responds successfully, the integration is complete. # Claude Code Integration > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in Claude Code. # Claude Code Integration Claude Code is an intelligent coding tool that runs in your terminal, helping developers complete code generation, debugging, and refactoring tasks through natural language commands. By modifying the local persistent configuration files, you can point Claude Code's default request endpoint to UniGateway's native API interface, achieving a stable and efficient coding workflow. Since UniGateway provides the native Claude protocol, **no model name replacement or mapping is required.** > **Tip:** If you encounter 400 errors caused by `tool_search` calls during use, you can add `"ENABLE_TOOL_SEARCH": "0"` to the configuration file below to temporarily resolve it. ## Step 1: Install Claude Code **Prerequisites:** - Node.js 18 or later - macOS/Linux users are recommended to install Node.js via `nvm` or `fnm` (avoid permission issues from direct installers) - Windows users are recommended to install Git for Windows in advance Open a terminal (Terminal or PowerShell) and run the following command to install Claude Code globally: ```bash npm install -g @anthropic-ai/claude-code ``` After installation, run the command below to check the version. If a version number is shown, the installation succeeded: ```bash claude --version ``` ## Step 2: Configure Persistent Environment Variables (UniGateway) To make Claude Code permanently connect to UniGateway without manually setting environment variables every time, we modify Claude Code's global local configuration files. Create or edit the following two JSON configuration files under the corresponding user directory. ### 1. Configure `settings.json` (Endpoint and API Key) This file persists the API environment variables. - **macOS & Linux path:** `~/.claude/settings.json` - **Windows path:** `C:\Users\\.claude\settings.json` If the directory or file does not exist, create it manually. Fill `settings.json` with the following content and replace it with your own API Key: ```json { "env": { "ANTHROPIC_API_KEY": "sk-replace-with-your-UniGateway-API-Key", "ANTHROPIC_BASE_URL": "https://api.unigateway.ai" } } ``` ### 2. Configure `.claude.json` (Skip Official Forced Onboarding) This file marks initialization as completed, preventing the Anthropic official sign-in page from popping up on every launch. - **macOS & Linux path:** `~/.claude.json` - **Windows path:** `C:\Users\\.claude.json` Create or edit this file with the following content: ```json { "hasCompletedOnboarding": true } ``` > **Note:** > - Ensure the JSON format is exactly correct (e.g., no missing quotes, no extra commas). > - After modifying the configuration files, **close the current terminal window and open a new one** to ensure the configuration is loaded correctly. ## Step 3: Start Using Claude Code After configuration, open a terminal in any of your code project working directories and run: ```bash claude ``` On startup: 1. If the system prompts "Do you want to use this API key?", select **Yes**. 2. Then choose to trust Claude Code's access to the files in that folder. Once in the interactive interface, you can type `/status` to confirm the currently connected model status. Since the native interface is configured, you can directly use `claude-opus-4-6` and other official native models for efficient development. ## Troubleshooting **Manual config changes don't take effect?** - Confirm you have closed and reopened the terminal window. - Check that the `settings.json` and `.claude.json` paths are correct (note the difference between the `.claude` folder and the `.claude.json` file). - Confirm the JSON format is valid — use an online JSON validator to check for missing brackets or commas. - If still not working, try deleting `settings.json` and recreating it strictly in the format above. # LobeChat > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in LobeChat through the OpenAI-compatible provider path. # LobeChat Configure UniGateway as an OpenAI-compatible provider in LobeChat. ## Prerequisites - LobeChat instance running - UniGateway API key - Model ID from `GET /v1/models` ## Configuration In LobeChat model provider settings: - **Provider**: OpenAI-compatible path - **API key**: your UniGateway key - **Base URL**: `https://api.unigateway.ai/v1` - **Model IDs**: exact values from `GET /v1/models` Confirm whether your deployment expects the `/v1` suffix in the API endpoint URL. After normal chat works, enable tools, files, or multimodal features separately. ## Common Failures | Problem | Resolution | |---|---| | Chat loads but empty output | Re-check proxy/base URL, especially `/v1` suffix | | Models do not appear | Verify API key and confirm `GET /v1/models` works | | One model works, another does not | Model availability is account-specific | | Feature parity inconsistent | Validate each endpoint family separately | # Claude Desktop Integration > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in Claude Desktop. # Claude Desktop Integration Claude Desktop is Anthropic's official desktop application for interacting with Claude. By connecting Claude Desktop to UniGateway via its third-party inference feature, you gain access to a wide range of models through a single unified endpoint, with centralized key management, automatic fallback, and no need for a claude.ai account. ## Prerequisites - macOS 13 or later, or Windows 10/11 - A valid UniGateway API Key ## Configuration ### Step 1: Install Claude Desktop Download and install from [claude.ai/download](https://claude.ai/download): - **Mac** — Download the `.dmg`, drag to Applications - **Windows** — Download the `.msix`, double-click to install After installation, open the app once to let it initialize. **When you see the login window, close it immediately — do not sign in to a claude.ai account.** ### Step 2: Enable Developer Mode The freshly installed app does not show the third-party API entry by default. You need to enable Developer Mode first. Navigate to: ``` Menu bar → Help → Troubleshooting → Check "Enable Developer mode" ``` After checking the box, Claude will restart automatically. Once restarted, a new **Developer** menu appears in the menu bar. Click **Developer → Configure third-party inference** to open the configuration panel. ### Step 3: Fill in the Configuration In the configuration panel, fill in the following fields: | Field | Value | |-------|-------| | Inference Provider | `gateway` | | Base URL | `https://api.unigateway.ai` | | API Key | Your UniGateway API Key | Click the **Apply locally** button at the bottom of the panel (**not** Export). ## Final Verification Fully quit Claude Desktop and reopen it: - **Mac** — `Cmd + Q`, or menu bar Claude → Quit Claude (closing the window does not quit the app — it keeps running in the background) - **Windows** — Right-click the taskbar icon and exit After restarting, if the configuration is active you will see: - The sidebar shows only **Cowork** and **Code** tabs - There is no **Chat** tab - The app does not ask you to sign in Send a simple message. If you receive a response, the integration is complete. > If you still see the **Chat** tab after restarting, the configuration was not applied. Go back to Step 3 and verify you clicked **Apply locally** and fully quit the app before reopening. # Hermes Agent Integration > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in Hermes-Agent. # Hermes Agent Integration Hermes Agent is a powerful AI agent framework supporting tool calls, browser automation, code execution, file operations, and more. By connecting Hermes Agent to UniGateway, you gain access to a wide range of models from major providers through a single unified endpoint, with centralized key management, automatic fallback, and no need to modify any upstream provider SDKs. ## Prerequisites - Operating system: Linux / macOS / WSL2 / Android (Termux) - Git installed - A valid UniGateway API Key > Python 3.11, Node.js v22, ripgrep, ffmpeg and other dependencies are installed automatically by the install script — no need to install them in advance. ## Configuration ### Step 1: Install Hermes Agent Run the following command in your terminal to install Hermes Agent: ```bash curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash ``` After installation, reload your shell configuration: ```bash source ~/.bashrc # zsh users run: source ~/.zshrc ``` ### Step 2: Configure the UniGateway Connection Launch the configuration wizard: ```bash hermes model ``` In the wizard, choose **Custom endpoint (enter URL manually)**, then enter the following fields in order: | Field | Value | |-------|-------| | API Base URL | `https://api.unigateway.ai` | | API Key | Your UniGateway API Key | | Model name | e.g. `gpt-5.5`, `claude-sonnet-4-6` (use the exact ID returned by `GET /v1/models`) | The configuration is automatically written to `~/.hermes/config.yaml` — no manual editing required. ## Final Verification Launch Hermes Agent and send a test message: ```bash hermes ``` Send a simple message (for example: `Hello, please only reply "Hi!"`). If you receive a response, the integration is complete. # n8n > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in n8n through OpenAI-compatible nodes or the HTTP Request node. # n8n Use UniGateway through OpenAI-compatible nodes or the HTTP Request node. ## Prerequisites - n8n instance running - UniGateway API key - Model ID from `GET /v1/models` ## Integration Options | Path | When | |---|---| | OpenAI / Chat OpenAI nodes | Your n8n version supports the settings you need | | HTTP Request node | You need full control over URL, headers, and payload | ## Configuration Start with one non-streaming chat request to confirm the connection. After the first flow works, add embeddings, responses, or image/audio routes. > Keep test and production environment keys separate in n8n credentials. ## Notes | Area | Guidance | |---|---| | Credentials | Separate keys for test and production | | OpenAI node versions | Re-check behavior after n8n upgrades | | HTTP Request fallback | Use when built-in node lacks endpoint behavior | | Error handling | Route `429` and `5xx` to workflow-level backoff | ## Common Failures | Problem | Resolution | |---|---| | Node authenticates but requests fail | Verify endpoint URL, model ID, request family | | Built-in node lacks capability | Use HTTP Request node for that endpoint | | Works in one workflow, not another | Check cached credentials or hardcoded model IDs | # LangChain > Category: Integrations | Last updated: 2026-08-09 Use UniGateway with LangChain through the OpenAI-compatible langchain-openai package. # LangChain Use UniGateway through `langchain-openai`. ## Prerequisites - Package: `langchain-openai` - UniGateway API key - Model ID from `GET /v1/models` ## Install ```bash pip install langchain-openai ``` ## Configure ```python from langchain_openai import ChatOpenAI llm = ChatOpenAI( model="gpt-5.4", api_key="", base_url="https://api.unigateway.ai/v1", temperature=0, ) print(llm.invoke("Give me a short deployment checklist.").content) ``` Or via environment variables: ```bash export OPENAI_API_KEY="$UNIGATEWAY_API_KEY" export OPENAI_API_BASE="https://api.unigateway.ai/v1" ``` ## Common Failures | Problem | Resolution | |---|---| | Requests hit wrong host | Re-check `base_url` / `OPENAI_API_BASE` | | One prompt works, another unstable | Test with simpler request shape first | | Multi-step workflows amplify costs | Add per-run budgets and fallback limits | | Structured outputs differ across models | Validate schema-sensitive flows per model | # Cherry Studio > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in Cherry Studio through a custom OpenAI-compatible provider configuration. # Cherry Studio Configure UniGateway through a custom OpenAI-type provider. ## Prerequisites - Cherry Studio installed - UniGateway API key ## Configuration In **Settings > Model Services**: | Step | Action | |---|---| | 1 | Add a new provider, type **OpenAI** | | 2 | **API Key**: your UniGateway key | | 3 | **API Address**: `https://api.unigateway.ai/v1` | | 4 | Add model IDs from `GET /v1/models` | | 5 | Test one normal chat | > Cherry Studio's built-in key check is only a first pass. Run one real request. ## Verify ```bash curl https://api.unigateway.ai/v1/models \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` ## Common Failures | Problem | Resolution | |---|---| | Provider saves but requests fail | Check API address includes `/v1` | | Added model does not work | Verify exact model ID from `GET /v1/models` | | UI shows model but runtime fails | UI list shows configured state, not live availability | # Flowise > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in Flowise through ChatOpenAI with a custom base URL. # Flowise Configure UniGateway through the ChatOpenAI node with a custom base URL. ## Prerequisites - Flowise instance running - UniGateway API key - Model ID from `GET /v1/models` ## Configuration 1. Drag a `ChatOpenAI` node into your flow 2. Create a credential with your UniGateway key 3. Open **Additional Parameters** and set base path to `https://api.unigateway.ai/v1` 4. Set model name to an exact ID from `GET /v1/models` 5. Test one non-streaming chat before adding images, tools, or custom models If the standard ChatOpenAI node does not expose your model ID, use `ChatOpenAI Custom`. ## Common Failures | Problem | Resolution | |---|---| | Credential works but inference fails | Re-check base path and model ID | | Standard node cannot select model | Switch to `ChatOpenAI Custom` | | Text works, image upload fails | Validate multimodal support separately | # Continue > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in Continue through the OpenAI provider with a custom apiBase. # Continue Configure UniGateway through the `openai` provider with a custom `apiBase`. ## Prerequisites - Continue extension installed - UniGateway API key - Model ID from `GET /v1/models` ## Configuration ```yaml name: UniGateway version: 0.0.1 schema: v1 models: - name: UniGateway Chat provider: openai model: gpt-5.2 apiBase: https://api.unigateway.ai/v1 apiKey: ``` Keep the `/v1` suffix in `apiBase`. ## Validate Send a simple message to confirm the connection. After chat works, enable edit, autocomplete, or other roles as needed. ## Common Failures | Problem | Resolution | |---|---| | Config loads but model fails | Re-check `apiBase`, `apiKey`, and model ID | | Autocomplete worse than chat | Treat each role as a separate rollout | | Streaming or reasoning differs | Verify model behavior with Continue's request mode | # Cline > Category: Integrations | Last updated: 2026-08-09 Use UniGateway in Cline through the OpenAI-compatible provider path. # Cline Configure UniGateway as an OpenAI-compatible provider in Cline. ## Prerequisites - Cline extension installed - UniGateway API key ## Configuration In Cline settings, set: | Field | Value | |---|---| | Base URL | `https://api.unigateway.ai/v1` | | API Key | Your UniGateway key | | Model ID | Exact value from `GET /v1/models` | > Base URL must point to UniGateway, not the official OpenAI endpoint. ## Verify Send a basic coding question to confirm the connection works. ## Notes | Area | Guidance | |---|---| | Base URL | Use UniGateway `/v1` path | | Model IDs | Re-check after catalog changes | | Tool-heavy tasks | Validate tool use separately from plain chat | ## Common Failures | Problem | Resolution | |---|---| | Provider saves but no model works | Confirm base URL, API key, and model ID with `/v1/models` | | One task succeeds, another unstable | Validate tool use and long-context separately | | Latency spikes in multi-step workflows | Prepare a faster fallback model | ## Operations & Billing # API Key Usage > Category: Operations & Billing | Last updated: 2026-08-09 Query per-API-Key request records, usage details, billing results, statuses, and pagination. # API Key Usage Use the API Key usage endpoint to retrieve request records, usage details, and billing results generated by the API Key used to authenticate the request. This endpoint is suitable for per-key reconciliation, cost analysis, internal usage displays, and automated exports. It does not return records for other API Keys under the same account. ## Endpoint | Item | Value | | --- | --- | | Method | `GET` | | Path | `/v1/usage` | | API URL | `https://unigateway.ai/v1/usage` | | Authentication | `Authorization: Bearer $UNIGATEWAY_API_KEY` | | Response format | `application/json` | **Note: `/v1/usage` is an API Key management endpoint. You must use `https://unigateway.ai`, not the standard `https://api.unigateway.ai` API URL.** **Authentication note: this endpoint authenticates with the regular API Key, not a Management Key. Pass the API Key whose usage you want to query as the Bearer token; the endpoint only returns request records and usage data generated by that API Key.** ## Prerequisites Create or retrieve an API Key from [UniGateway API Keys](https://unigateway.ai/dashboard/api-keys). Configure access controls, rotation, and security policies according to your requirements. Store the API Key in a server-side environment variable. Do not put it in browser code, logs, public repositories, or export files: ```bash export UNIGATEWAY_API_KEY="" ``` Windows PowerShell: ```powershell $env:UNIGATEWAY_API_KEY = "" ``` ## Query Usage ```http GET /v1/usage ``` ### Query Parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `limit` | integer | No | Records per page. Defaults to `50`. Valid range: `1` to `200`. | | `offset` | integer | No | Offset. Defaults to `0`, with a maximum of `100000`. Omit it or set it to `0` when using `cursor`. | | `cursor` | string | No | The `next_cursor` returned by the previous page. Use it for sequential exports and large result sets. | | `charged_only` | boolean | No | Defaults to `true`. When `status` is omitted, only `SUCCESS` and `PARTIAL` records are returned. Set it to `false` to query all statuses. | | `status` | string | No | Exact filter for `SUCCESS`, `FAILED`, `PARTIAL`, `UNKNOWN_MODEL`, or `REFUNDED`. Takes precedence over `charged_only`. | | `model_id` | string | No | Exact filter by model ID. | | `model` | string | No | Exact filter by the model name stored in the record. | | `from` | string | No | Inclusive start time in ISO 8601 format. | | `to` | string | No | Inclusive end time in ISO 8601 format. | | `is_stream` | boolean | No | Pass `true` or `false` to filter by streaming mode. | For example, use `2026-07-01T00:00:00Z` for `from` or `to`. The time range also supports `start_date` / `end_date` and `startDate` / `endDate` for compatibility. New integrations should use `from` and `to`. ### cURL Requests The following request returns up to 100 successful records in the specified time range: ```bash curl -G https://unigateway.ai/v1/usage \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ --data-urlencode "limit=100" \ --data-urlencode "from=2026-07-01T00:00:00Z" \ --data-urlencode "to=2026-07-31T23:59:59Z" \ --data-urlencode "status=SUCCESS" ``` To query failed, refunded, and all other statuses, remove `status` and pass `charged_only=false`: ```bash curl -G https://unigateway.ai/v1/usage \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ --data-urlencode "limit=100" \ --data-urlencode "charged_only=false" ``` ### Python Pagination This example uses the Python standard library to retrieve every record in the same time range. Monetary fields remain strings to avoid introducing floating-point precision loss during transport. ```python import json import os from urllib.parse import urlencode from urllib.request import Request, urlopen API_URL = "https://unigateway.ai/v1/usage" params = { "limit": 100, "from": "2026-07-01T00:00:00Z", "to": "2026-07-31T23:59:59Z", "status": "SUCCESS", } cursor = None while True: page_params = {**params} if cursor: page_params["cursor"] = cursor request = Request( f"{API_URL}?{urlencode(page_params)}", headers={"Authorization": f"Bearer {os.environ['UNIGATEWAY_API_KEY']}"}, ) with urlopen(request) as response: page = json.load(response) for record in page["items"]: print(record["id"], record["model_name"], record["final_price"]) if not page["has_more"]: break cursor = page["next_cursor"] ``` ### JavaScript Pagination This example requires Node.js 18 or later. Do not use it in browser code because the API Key must remain in a server-side environment. ```javascript const apiUrl = new URL("https://unigateway.ai/v1/usage"); const params = { limit: "100", from: "2026-07-01T00:00:00Z", to: "2026-07-31T23:59:59Z", status: "SUCCESS", }; let cursor; do { const query = new URLSearchParams(params); if (cursor) { query.set("cursor", cursor); } apiUrl.search = query.toString(); const response = await fetch(apiUrl, { headers: { Authorization: `Bearer ${process.env.UNIGATEWAY_API_KEY}`, }, }); if (!response.ok) { throw new Error(`Usage query failed: ${response.status} ${await response.text()}`); } const page = await response.json(); for (const record of page.items) { console.log(record.id, record.model_name, record.final_price); } cursor = page.has_more ? page.next_cursor : undefined; } while (cursor); ``` ## Response The response is JSON. `items` are ordered by `created_at` from newest to oldest. All models use the same list structure. Each API call produces an independent record; records are not grouped or merged by model. The following excerpt includes Claude, Seedance, and `gpt-image-2` records. For readability, the example omits fields with `null` values and some common fields. The monetary values illustrate the response structure and do not represent current model prices. ```json { "items": [ { "id": "usage_claude_example", "model_name": "claude-sonnet-5", "prompt_tokens": 90, "completion_tokens": 9, "cache_read_tokens": 0, "cache_write_tokens": 124264, "total_tokens": 99, "request_count": 1, "billing_mode": "PER_TOKEN", "original_price": "0.310930000000", "discount_percentage": "21", "savings_amount": "0.065295300000", "final_price": "0.245634700000", "settlement_currency": "USD", "cache_creation_tokens_5m": 124264, "cache_creation_tokens_1h": 0, "cache_write_breakdown": { "cacheCreationTokens5m": 124264, "cacheCreationTokens1h": 0, "aggregateCacheWriteTokens": 124264, "cacheWrite5mPricePerMillionApplied": "2.500000000000", "cacheWrite1hPricePerMillionApplied": "4.000000000000", "cacheWrite5mOriginalPrice": "0.310660000000", "cacheWrite1hOriginalPrice": "0.000000000000", "cacheWrite5mFinalPrice": "0.245421400000", "cacheWrite1hFinalPrice": "0.000000000000" }, "currency": "USD", "status": "SUCCESS", "created_at": "2026-07-21T03:12:00.000Z" }, { "id": "usage_seedance_example", "model_name": "doubao-seedance-2-0-260128", "prompt_tokens": 0, "completion_tokens": 324900, "total_tokens": 324900, "request_count": 1, "billing_mode": "PER_TOKEN", "original_price": "2.397762000000", "discount_percentage": "4.55", "savings_amount": "0.109098171000", "final_price": "2.288663829000", "settlement_currency": "USD", "media_usage": { "modality": "video", "resolution": "720p", "ratio": "16:9", "duration": 15 }, "seedance_media_pricing": { "tokenPricePerMillion": 7.38, "billableTokens": 324900, "inferenceMode": "ONLINE", "inputHasVideo": false, "inputVideoCount": 0, "resolution": "720p", "duration": 15, "ratio": "16:9" }, "currency": "USD", "status": "SUCCESS", "created_at": "2026-07-21T03:11:00.000Z" }, { "id": "usage_image_example", "model_name": "gpt-image-2", "prompt_tokens": 6618, "completion_tokens": 4354, "total_tokens": 10972, "request_count": 1, "billing_mode": "PER_TOKEN", "original_price": "0.183564000000", "discount_percentage": "35", "savings_amount": "0.064247400000", "final_price": "0.119316600000", "settlement_currency": "USD", "media_usage": { "modality": "image", "resolution": "1K", "size": "1024x1024", "quality": "auto", "imageCount": 1, "imageOutputUnits": 4354, "requestPath": "/v1/images/edits" }, "currency": "USD", "status": "SUCCESS", "created_at": "2026-07-21T03:10:00.000Z" } ], "total": 3, "limit": 50, "offset": 0, "has_more": false, "next_cursor": null } ``` ### Top-Level Fields | Field | Type | Description | | --- | --- | --- | | `items` | array | Usage records on the current page, ordered by `created_at` from newest to oldest. | | `total` | integer / null | Total records matching the current filters in offset mode; `null` in cursor mode. | | `limit` | integer | Current page size. | | `offset` | integer / null | Current offset; `null` in cursor mode. | | `has_more` | boolean | Whether another page is available. | | `next_cursor` | string / null | Opaque cursor for the next page; `null` when no next page is available. | ### Common Fields | Field | Type | Description | | --- | --- | --- | | `id` | string | Usage record ID. | | `token_id` / `token_name` | string / null | ID and name of the current API Key. | | `model_id` / `model_name` | string / null | Model ID and model name stored for this API call. | | `prompt_tokens` / `completion_tokens` | integer / null | Input and output token usage. | | `cache_read_tokens` / `cache_write_tokens` | integer / null | Cache read and cache write token usage. | | `total_tokens` | integer / null | Total tokens stored in the record. Cache tokens are returned separately; do not add them to derive this value. | | `request_count` | integer | Number of billable requests. | | `duration_seconds` / `total_duration_seconds` | number / null | Duration-based billing or request duration information. | | `first_token_latency_ms` | integer / null | First token latency, when applicable. | | `is_stream` | boolean / null | Whether the request used streaming. | | `billing_mode` | string | Billing mode for this record: `PER_TOKEN`, `PER_REQUEST`, or `PER_SECOND`. | | `original_price` | string | Amount before discount. | | `discount_percentage` / `discount_source` | string / null | Discount percentage and source. | | `savings_amount` | string | Amount saved compared with the original price. | | `final_price` | string | Amount actually billed. | | `settlement_currency` | string | Currency used by monetary and applied unit-price fields. Currently `USD`. | | `currency` / `exchange_rate` | string / null | Source pricing currency and the rate used to convert it to the settlement currency. | | `status` / `error_reason` | string / null | Record status and error reason. | | `created_at` | string | Record time in ISO 8601 format. | `original_price`, `final_price`, `savings_amount`, `discount_percentage`, `exchange_rate`, and cache price fields are returned as decimal strings. Monetary and applied unit-price fields use `settlement_currency`, currently `USD`. The `currency` field retains the source model pricing currency, while `exchange_rate` is the conversion rate from that source currency to the settlement currency. Use a high-precision decimal type when processing these values to avoid floating-point precision loss. Some counts and unit prices in model-specific objects can be JSON numbers, such as `seedance_media_pricing.tokenPricePerMillion`. The same model can use different `billing_mode` values depending on the request type or billing rule. Always use the value returned by the current record. Do not infer it from the model name. ### Model-Specific Fields Model-specific fields are usually `null` when they do not apply or data is unavailable. Some historical records can return objects containing only `0` or `null` values. Check that the object and required fields contain meaningful values before using them. | Field | Applies To | Description | | --- | --- | --- | | `cache_creation_tokens_5m` | Claude caching | 5-minute cache write tokens. | | `cache_creation_tokens_1h` | Claude caching | 1-hour cache write tokens. | | `cache_write_breakdown` | Claude caching | 5-minute and 1-hour cache write usage, applied unit prices, original amounts, and billed amounts. | | `media_usage` | Images or video | Resolution, size, quality, duration, aspect ratio, and media counts for the request. | | `media_pricing_rule` | Media billing | Public pricing conditions and effective billable usage matched for the request. | | `seedance_media_pricing` | Seedance video | Unit price, effective billable usage, and media conditions for the Seedance request. | Claude records still return total cache write usage in `cache_write_tokens`. If a historical record cannot reliably separate 5-minute and 1-hour cache writes, the two breakdown fields are `null`. Use `cache_write_tokens` for the total in that case. ## Pagination The endpoint supports offset and cursor pagination. Prefer cursor pagination for sequential retrieval, automated exports, and large result sets. Cursor mode does not return or depend on `total` and `offset`, which avoids BigInt and integer precision concerns for large totals or offsets. | Mode | Request | Best For | Response | | --- | --- | --- | --- | | offset | Pass `limit` and `offset`. | Small result sets and manually selected page numbers. | Returns `total` and the current `offset`. | | cursor | Omit `cursor` on the first request, then pass the previous `next_cursor`. | Sequential retrieval, bulk exports, and resumable jobs. | `total` and `offset` are `null`. | Do not mix the two modes. When using `cursor`, omit `offset` or set it to `0`. ### Offset Pagination Start with `offset=0`. Calculate the next page as `offset + limit`. For example, to retrieve page 3 with 100 records per page: ```bash curl -G https://unigateway.ai/v1/usage \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ --data-urlencode "limit=100" \ --data-urlencode "offset=200" \ --data-urlencode "from=2026-07-01T00:00:00Z" \ --data-urlencode "to=2026-07-31T23:59:59Z" ``` The maximum `offset` is `100000`. Do not paginate through a large result set by continually increasing `offset`. Use cursor pagination instead. ### Cursor Pagination Omit `cursor` on the first request. When `has_more` is `true`, preserve the exact same filters and pass `next_cursor` unchanged to the next request. Do not parse or modify a cursor, and do not reuse a cursor from another query: ```bash curl -G https://unigateway.ai/v1/usage \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ --data-urlencode "limit=100" \ --data-urlencode "from=2026-07-01T00:00:00Z" \ --data-urlencode "to=2026-07-31T23:59:59Z" \ --data-urlencode "cursor=" ``` In cursor mode, `total` and `offset` are `null`. To resume an export job, store the `next_cursor` from the last successfully processed page together with the complete set of filters. ## Statuses | Status | Meaning | | --- | --- | | `SUCCESS` | The request succeeded and produced a complete usage record. | | `PARTIAL` | Partial usable usage information was recorded. | | `FAILED` | The request failed. | | `UNKNOWN_MODEL` | The record could not be matched to model billing information. | | `REFUNDED` | The record was refunded. | By default, the endpoint only returns `SUCCESS` and `PARTIAL`. To query failed or refunded records, pass the corresponding `status`. To query all statuses, set `charged_only=false` and omit `status`. ## Errors and Rate Limits | HTTP Status | Common Cause | Action | | --- | --- | --- | | `400` | Invalid query parameter format, time range, or parameter combination. | Check `limit`, `offset`, `cursor`, `status`, and ISO 8601 timestamps. Remove a nonzero `offset` when using `cursor`. | | `401` | The API Key is missing or invalid, or the environment variable is not loaded. | Check `UNIGATEWAY_API_KEY` on the server, reload the application configuration, and retry. | | `403` | The API Key is disabled or lacks permission. | Check the API Key status, access controls, and limits in the UniGateway dashboard, then verify again. | | `429` | The request rate limit was exceeded. | Reduce concurrency, retry with exponential backoff, and continue from the last successful page's `next_cursor`. | | `500` | The service cannot process the request temporarily. | Retry a limited number of times with exponential backoff. If failures continue, record the request time, filters, and error response for troubleshooting. | Each API Key can make up to 120 usage queries per minute. Increase polling intervals as needed. For bulk exports, prefer `limit=100` to `200` with cursor pagination instead of issuing many concurrent requests for the same API Key. ## FAQ ### Q: Why don't the results contain records for other API Keys? `GET /v1/usage` only returns records generated by the API Key used to authenticate the request. Make the request with the API Key you need to reconcile and confirm that the server-side environment variable contains that key. The endpoint does not combine data from other API Keys under the same account. ### Q: Why is a recently completed request missing from the results? First confirm that the request used the intended API Key. Then widen the `from` and `to` range and verify that the timestamps use ISO 8601 and the correct time zone. By default, the endpoint only returns `SUCCESS` and `PARTIAL`. To investigate failed or refunded records, pass the corresponding `status` or set `charged_only=false`. ### Q: Why are monetary fields strings? `original_price`, `savings_amount`, `final_price`, and related cache price fields are returned as decimal strings to prevent floating-point precision loss. Use a high-precision decimal type for aggregation and display. Do not convert these values directly to binary floating-point numbers. # API Key Quotas and Restrictions > Category: Operations & Billing | Last updated: 2026-08-09 Query effective API Key quota, period usage, model allowlists, and IP address restrictions. # API Key Quotas and Restrictions Use the API Key quota endpoint to retrieve the available quota, period usage, model allowlist, and IP address allowlist for the API Key used to authenticate the request. This endpoint is suitable for service startup checks, pre-request quota notices, budget monitoring, and access policy verification. It does not return information for other API Keys under the same account. ## Endpoint | Item | Value | | --- | --- | | Method | `GET` | | Path | `/v1/api-key` | | API URL | `https://unigateway.ai/v1/api-key` | | Authentication | `Authorization: Bearer $UNIGATEWAY_API_KEY` | | Response format | `application/json` | **Note: `/v1/api-key` is an API Key management endpoint. You must use `https://unigateway.ai`, not the standard `https://api.unigateway.ai` API URL.** This endpoint takes no query parameters and only returns information for the API Key used to authenticate the request. **Authentication note: this endpoint authenticates with the regular API Key, not a Management Key. Pass the API Key whose quota you want to query as the Bearer token; the endpoint only returns quota, limits, and access restrictions for that API Key.** ## Prerequisites Create or retrieve an API Key from [UniGateway API Keys](https://unigateway.ai/dashboard/api-keys). Configure access controls, rotation, and security policies according to your requirements. Store the API Key in a server-side environment variable. Do not put it in browser code, logs, public repositories, or monitoring payloads: ```bash export UNIGATEWAY_API_KEY="" ``` Windows PowerShell: ```powershell $env:UNIGATEWAY_API_KEY = "" ``` ## Query API Key Quotas and Restrictions ```http GET /v1/api-key ``` ### cURL Request ```bash curl https://unigateway.ai/v1/api-key \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` ### Python Request This example uses the Python standard library and preserves monetary fields as their original decimal strings: ```python import json import os from urllib.request import Request, urlopen request = Request( "https://unigateway.ai/v1/api-key", headers={"Authorization": f"Bearer {os.environ['UNIGATEWAY_API_KEY']}"}, ) with urlopen(request, timeout=30) as response: result = json.load(response) quota = result["quota"] print("Available amount:", quota["available_amount"], quota["currency"]) print("Constrained by:", ", ".join(quota["constrained_by"])) print("Allowed models:", result["restrictions"]["models"]["allowed"]) ``` ### JavaScript Request This example requires Node.js 18 or later. Do not use it in browser code because the API Key must remain in a server-side environment. ```javascript const response = await fetch("https://unigateway.ai/v1/api-key", { headers: { Authorization: `Bearer ${process.env.UNIGATEWAY_API_KEY}`, }, }); if (!response.ok) { throw new Error(`API Key quota query failed: ${response.status} ${await response.text()}`); } const result = await response.json(); console.log("Available amount:", result.quota.available_amount, result.quota.currency); console.log("Constrained by:", result.quota.constrained_by); console.log("Allowed models:", result.restrictions.models.allowed); ``` ## Response ```json { "api_key": { "name": "Production key", "last_used_at": "2026-07-21T03:12:00.000Z" }, "quota": { "currency": "USD", "billing_mode": "PREPAID", "available_amount": "12.340000000000", "is_unlimited": false, "constrained_by": ["DAILY_LIMIT"], "account_balance": "50.000000000000" }, "limits": { "daily": { "limit": "20.000000000000", "used": "7.660000000000", "remaining": "12.340000000000", "resets_at": "2026-07-22T00:00:00+08:00" }, "weekly": { "limit": "100.000000000000", "used": "35.000000000000", "remaining": "65.000000000000", "resets_at": "2026-07-27T00:00:00+08:00" }, "monthly": { "limit": null, "used": "80.000000000000", "remaining": null, "resets_at": "2026-08-01T00:00:00+08:00" } }, "restrictions": { "models": { "enabled": true, "allowed": ["claude-sonnet-5", "gpt-image-2"] }, "ip_addresses": { "enabled": true, "allowed": ["203.0.113.10", "198.51.100.0/24"] } }, "time_zone": "Asia/Shanghai" } ``` ### Top-Level Fields | Field | Type | Description | | --- | --- | --- | | `api_key` | object | Basic information about the current API Key. | | `quota` | object | Available quota, billing mode, and the constraints that determine the quota. | | `limits` | object | Daily, weekly, and monthly limits, usage, remaining quota, and reset times. | | `restrictions` | object | Model and IP address allowlist settings. | | `time_zone` | string | Time zone used for period limits and `resets_at`. | ## Quota Semantics `available_amount` is the billable amount that the current API Key can still use, denominated in `currency`. For prepaid accounts, the endpoint compares the account balance with the remaining quota for every configured API Key period limit and returns the smallest value. If the API Key has no daily, weekly, or monthly limits, `available_amount` uses the account balance. For postpaid accounts: - If no period limits are configured, `available_amount` is `null` and `is_unlimited` is `true`. - If period limits are configured, `available_amount` is the smallest remaining quota among the configured periods and `is_unlimited` is `false`. For postpaid accounts, `account_balance` is the account ledger balance at query time and does not constrain the available quota. `is_unlimited` only means that the current API Key has no monetary limit. Model permissions, IP address restrictions, and service capacity still apply. `constrained_by` identifies the constraints that determine the current available quota. It can contain: | Value | Description | | --- | --- | | `ACCOUNT_BALANCE` | Account balance | | `DAILY_LIMIT` | API Key daily limit | | `WEEKLY_LIMIT` | API Key weekly limit | | `MONTHLY_LIMIT` | API Key monthly limit | | `UNLIMITED` | Postpaid account with no period limits | When multiple constraints have the same remaining amount, `constrained_by` includes all of them. ### Postpaid Account Example The following result indicates that the current API Key has no daily, weekly, or monthly limits: ```json { "quota": { "currency": "USD", "billing_mode": "POSTPAID", "available_amount": null, "is_unlimited": true, "constrained_by": ["UNLIMITED"], "account_balance": "-12.500000000000" } } ``` ## Period Limits Daily, weekly, and monthly periods are calculated in the response `time_zone`: - Daily periods reset at local `00:00` every day. - Weekly periods reset at local `00:00` every Monday. - Monthly periods reset at local `00:00` on the first day of each month. When a period's `limit` is `null`, that period does not constrain the quota. Its `remaining` value is also `null`, while `used` still reports the amount billed in the current period. Period usage includes records that have completed billing or contain partially billable usage, based on data posted at query time. `resets_at` is an ISO 8601 timestamp with a time-zone offset. ## Model and IP Address Restrictions When `restrictions.models.enabled` is `false` and `allowed` is empty, the API Key does not restrict the `model`. When it is `true`, the key can only call a `model` listed in `allowed`. When `restrictions.ip_addresses.enabled` is `false` and `allowed` is empty, the API Key does not restrict access by IP address. When it is `true`, requests are accepted only from the IPv4 addresses, IPv6 addresses, or CIDR ranges listed in `allowed`. Actual model availability can also depend on model service status and account permissions. Cache this response only briefly on the server. Do not put it in a browser or long-term persistent storage. ## Field Formats All monetary fields are returned as decimal strings. Use a high-precision decimal type when processing them to avoid floating-point precision loss. `last_used_at` is `null` if the API Key has no usage records. | Field | Type | Description | | --- | --- | --- | | `api_key.name` | string | Name of the current API Key. | | `api_key.last_used_at` | string / null | Most recent usage time; `null` if the key has not been used. | | `quota.billing_mode` | string | Account billing mode: `PREPAID` or `POSTPAID`. | | `quota.available_amount` | string / null | Effective available quota; `null` when unlimited. | | `quota.is_unlimited` | boolean | Whether the current API Key has no effective monetary limit. | | `quota.constrained_by` | array | Constraints that determine the current available quota. | | `quota.account_balance` | string | Account ledger balance at query time. | | `limits.*.limit` | string / null | Period limit; `null` when not configured. | | `limits.*.used` | string | Amount billed during the period. | | `limits.*.remaining` | string / null | Remaining period quota; `null` when no limit is configured. | | `limits.*.resets_at` | string | Next reset time for the period in ISO 8601 format. | | `restrictions.models.enabled` | boolean | Whether the model allowlist is enabled. | | `restrictions.models.allowed` | array | Models that can be called when the model allowlist is enabled. | | `restrictions.ip_addresses.enabled` | boolean | Whether the IP address allowlist is enabled. | | `restrictions.ip_addresses.allowed` | array | IPv4 addresses, IPv6 addresses, or CIDR ranges accepted when the allowlist is enabled. | | `time_zone` | string | Time zone used for period limits and reset times. | ## Errors and Rate Limits | HTTP Status | Common Cause | Action | | --- | --- | --- | | `401` | The API Key is missing or invalid, or the environment variable is not loaded. | Check `UNIGATEWAY_API_KEY` on the server, reload the application configuration, and retry. | | `403` | The API Key is disabled or access is restricted. | Check the API Key status, access controls, and limits in the UniGateway dashboard, then verify again. | | `404` | The API Key was deleted or no longer exists. | Confirm whether the application's API Key was rotated or deleted, update the server-side environment variable, and retry. | | `429` | The request rate limit was exceeded. | Reduce the polling frequency and retry with exponential backoff. | | `500` | The service cannot process the request temporarily. | Retry a limited number of times with exponential backoff. If failures continue, record the request time and error response for troubleshooting. | Each API Key can make up to **120 requests per minute** to this endpoint. Cache the result on the server for one minute so the endpoint is not placed in the synchronous path of every model request. ## FAQ ### Q: How do I tell whether the API Key has daily, weekly, or monthly limits, a model allowlist, or an IP address allowlist? Check `limits.daily.limit`, `limits.weekly.limit`, and `limits.monthly.limit`. A `null` value means the corresponding period has no quota limit. For `restrictions.models`, `enabled` set to `false` with an empty `allowed` array means the `model` is not restricted. For `restrictions.ip_addresses`, `enabled` set to `false` with an empty `allowed` array means access is not restricted by IP address. Request `GET /v1/api-key` again after configuration changes. ### Q: Can I cache the quota response for a long time? Long-term caching is not recommended, but a one-minute server-side cache is. Balances, period usage, reset times, and allowlists can change. A short cache reduces polling without placing the endpoint in the synchronous path of every model request. Invalidate the cache and request `GET /v1/api-key` again after API Key configuration changes, or after receiving a `401`, `403`, or a quota-related request failure. Do not cache this response in browsers, logs, or long-term persistent storage. # Account Management API > Category: Operations & Billing | Last updated: 2026-08-09 Use management API keys to list account API keys and retrieve per-key or account-wide usage. # Account Management API Use a management API Key to list the API Keys in your account, inspect one API Key's quota and restrictions, and retrieve usage records across the account. Management API Keys use the `mk-` prefix and cannot call AI model endpoints. ## Prerequisites Regular API Keys cannot be used with the endpoints on this page. First read [Management API Key](/docs/management-key) and follow its console steps to create and save a Management API Key; the complete Key is shown only once on the creation success page and cannot be viewed or copied again after you leave it. Store the Key in a server-side environment variable. Do not put it in browser code, logs, public repositories, or export files: ```bash export UNIGATEWAY_MANAGEMENT_KEY="" ``` > Security: a management API Key can read metadata and usage for every API Key in its account. Rotate it immediately if it is exposed. ## Endpoint Base URL All account management endpoints use `https://unigateway.ai`, not the standard model API URL `https://api.unigateway.ai`. ```http Authorization: Bearer mk-... ``` Only management API Keys are accepted. A regular API Key with the `sk-` prefix returns `401 Unauthorized`. ## List Account API Keys ```http GET /v1/account/api-keys ``` | Item | Value | | --- | --- | | API URL | `https://unigateway.ai/v1/account/api-keys` | | Authentication | `Authorization: Bearer $UNIGATEWAY_MANAGEMENT_KEY` | | Response format | `application/json` | ### Query Parameters | Parameter | Type | Required | Description | | --- | --- | --- | | `limit` | integer | No | Records per page. Defaults to `50`; valid range is `1` to `100`. | | `offset` | integer | No | Offset. Defaults to `0`; maximum is `100000`. | ### Request ```bash curl -G https://unigateway.ai/v1/account/api-keys \ -H "Authorization: Bearer $UNIGATEWAY_MANAGEMENT_KEY" \ --data-urlencode "limit=50" \ --data-urlencode "offset=0" ``` ### Response ```json { "items": [ { "id": "token_01J4Z5D4V3K1R2M7Q8N9P0A1B2", "name": "Production", "key_prefix": "sk-abcdef0", "status": "ACTIVE", "created_at": "2026-07-23T03:12:00.000Z" } ], "total": 1, "limit": 50, "offset": 0, "has_more": false } ``` | Field | Type | Description | | --- | --- | --- | | `items[].id` | string | API Key ID. Use it in the detail and per-Key usage paths. | | `items[].name` | string | API Key name. | | `items[].key_prefix` | string / null | Non-secret Key prefix. It can be `null` for historical API Keys. | | `items[].status` | string | API Key status. | | `items[].created_at` | string | Creation time in ISO 8601 format. | | `total` | integer | Number of API Keys in the account. | | `has_more` | boolean | Whether another offset page is available. | The response never includes a complete API Key, Key hash, or access-control configuration. ## Get API Key Details ```http GET /v1/account/api-keys/{id} ``` Use the `id` returned by the list endpoint. The target API Key must belong to the same account as the management API Key. A missing or non-owned ID returns `404 Not Found`. ```bash curl https://unigateway.ai/v1/account/api-keys/token_01J4Z5D4V3K1R2M7Q8N9P0A1B2 \ -H "Authorization: Bearer $UNIGATEWAY_MANAGEMENT_KEY" ``` The response has the same structure as [API Key Quotas and Restrictions](/docs/api-key-quotas-and-restrictions): `api_key`, `quota`, `limits`, `restrictions`, and `time_zone`. It reports the target API Key rather than the management API Key used for authentication. ### Detail Response Fields | Field | Type | Description | | --- | --- | --- | | `api_key.name` | string | Target API Key name. | | `api_key.last_used_at` | string / null | Most recent use time in ISO 8601 format; `null` when the Key has not been used. | | `quota.currency` | string | Currency for quota and balance amounts. | | `quota.billing_mode` | string | Account billing mode: `PREPAID` or `POSTPAID`. | | `quota.available_amount` | string / null | Amount available to the target API Key; `null` when its monetary quota is unlimited. | | `quota.is_unlimited` | boolean | Whether the target API Key has no effective monetary limit. | | `quota.constrained_by` | array | Constraints that determine availability: `ACCOUNT_BALANCE`, `DAILY_LIMIT`, `WEEKLY_LIMIT`, `MONTHLY_LIMIT`, or `UNLIMITED`. | | `quota.account_balance` | string | Account ledger balance at query time. | | `limits.daily`, `limits.weekly`, `limits.monthly` | object | Period limit, billed usage, remaining amount, and ISO 8601 reset time. A `null` `limit` does not constrain the Key. | | `restrictions.models` | object | Model allowlist state and allowed model IDs. | | `restrictions.ip_addresses` | object | IP allowlist state and allowed IP addresses or CIDR ranges. | | `time_zone` | string | Time zone used to calculate period limits and reset times. | Amounts are decimal strings. Use a decimal type instead of binary floating-point when processing them. ## Get Usage for One API Key ```http GET /v1/account/api-keys/{id}/usage ``` Use this endpoint to retrieve usage records for one API Key. It accepts the same pagination, status, model, time-range, and streaming filters as [API Key Usage](/docs/usage-analytics). ```bash curl -G https://unigateway.ai/v1/account/api-keys/token_01J4Z5D4V3K1R2M7Q8N9P0A1B2/usage \ -H "Authorization: Bearer $UNIGATEWAY_MANAGEMENT_KEY" \ --data-urlencode "limit=100" \ --data-urlencode "from=2026-07-01T00:00:00Z" \ --data-urlencode "to=2026-07-31T23:59:59Z" \ --data-urlencode "status=SUCCESS" ``` Do not pass `token_id` to this endpoint. The Key is identified by `{id}` in the path, so `token_id` returns `400 Bad Request`. The response has the same fields and cursor behavior as [API Key Usage](/docs/usage-analytics). `items` only contains records for the API Key identified by `{id}`. ## Get Account Usage ```http GET /v1/account/usage ``` Use this endpoint to retrieve usage records for all API Keys in the account. It accepts every query parameter supported by [API Key Usage](/docs/usage-analytics), plus an optional `token_id` filter. | Parameter | Type | Required | Description | | --- | --- | --- | | `token_id` | string | No | Limits records to one API Key owned by the account. A missing or non-owned ID returns `404 Not Found`. | ```bash curl -G https://unigateway.ai/v1/account/usage \ -H "Authorization: Bearer $UNIGATEWAY_MANAGEMENT_KEY" \ --data-urlencode "limit=100" \ --data-urlencode "from=2026-07-01T00:00:00Z" \ --data-urlencode "to=2026-07-31T23:59:59Z" \ --data-urlencode "token_id=token_01J4Z5D4V3K1R2M7Q8N9P0A1B2" ``` Omit `token_id` to retrieve records across the account. The response has the same fields and cursor behavior as [API Key Usage](/docs/usage-analytics). ## Usage Response Fields The per-Key and account usage endpoints return the same response structure. | Field | Type | Description | | --- | --- | --- | | `items` | array | Usage records ordered by `created_at` from newest to oldest. | | `items[].id` | string | Usage record ID. | | `items[].token_id` / `items[].token_name` | string / null | API Key ID and name associated with the record. | | `items[].model_id` / `items[].model_name` | string / null | Model ID and model name stored for the request. | | `items[].prompt_tokens` / `items[].completion_tokens` | integer / null | Input and output token usage. | | `items[].cache_read_tokens` / `items[].cache_write_tokens` | integer / null | Cache token usage, returned separately from `total_tokens`. | | `items[].total_tokens` | integer / null | Total tokens stored in the record. Do not add cache tokens to derive this value. | | `items[].request_count` | integer | Number of billable requests in the record. | | `items[].billing_mode` | string | `PER_TOKEN`, `PER_REQUEST`, or `PER_SECOND`. | | `items[].original_price` / `items[].final_price` / `items[].savings_amount` | string | Amount before discount, amount billed, and discount saving. All are decimal strings. | | `items[].status` / `items[].error_reason` | string / null | Processing status and error reason when available. | | `items[].created_at` | string | Record time in ISO 8601 format. | | `total` | integer / null | Total records in offset mode; `null` in cursor mode. | | `limit` | integer | Current page size. | | `offset` | integer / null | Current offset in offset mode; `null` in cursor mode. | | `has_more` | boolean | Whether another page is available. | | `next_cursor` | string / null | Opaque cursor for the next page; `null` when no page remains. | For the complete list of model-specific usage fields, see [API Key Usage](/docs/usage-analytics). ## Verification Cases Set an API Key ID returned by the list endpoint before running the per-Key cases: ```bash export API_KEY_ID="" ``` | Case | Request | Expected Result | | --- | --- | --- | | List account API Keys | `GET /v1/account/api-keys?limit=1&offset=0` | `200`, `items` array, pagination fields, and no complete Key or Key hash. | | Get API Key details | `GET /v1/account/api-keys/$API_KEY_ID` | `200` with `api_key`, `quota`, `limits`, `restrictions`, and `time_zone`. | | Get per-Key usage | `GET /v1/account/api-keys/$API_KEY_ID/usage?limit=1&charged_only=false` | `200`; each returned record belongs to `$API_KEY_ID`. | | Get account usage | `GET /v1/account/usage?limit=1&charged_only=false` | `200` with `items`, `has_more`, and cursor pagination fields. | | Reject duplicate Key scope | `GET /v1/account/api-keys/$API_KEY_ID/usage?token_id=$API_KEY_ID` | `400` because the path already selects the API Key. | ```bash curl -sS -D /tmp/unigateway-headers -o /tmp/unigateway-body \ -G https://unigateway.ai/v1/account/api-keys \ -H "Authorization: Bearer $UNIGATEWAY_MANAGEMENT_KEY" \ --data-urlencode "limit=1" \ --data-urlencode "offset=0" grep -i '^cache-control: no-store' /tmp/unigateway-headers ``` The request must return `200`, the response must contain `items`, `total`, `limit`, `offset`, and `has_more`, and the header check must print `Cache-Control: no-store`. ```bash curl -sS -o /dev/null -w '%{http_code}\n' \ -G "https://unigateway.ai/v1/account/api-keys/$API_KEY_ID/usage" \ -H "Authorization: Bearer $UNIGATEWAY_MANAGEMENT_KEY" \ --data-urlencode "token_id=$API_KEY_ID" ``` This request must print `400`. ## Errors and Rate Limits All responses, including errors, use `Cache-Control: no-store`. | HTTP Status | Common Cause | Action | | --- | --- | --- | | `400` | Invalid pagination, usage filter, time range, or `token_id` on a per-Key usage request. | Correct the request parameters. | | `401` | The management API Key is missing, invalid, or a regular API Key was used. | Load a valid `mk-` Key on the server and retry. | | `404` | The requested API Key does not exist or is not owned by the management Key's account. | Refresh the account Key list and use an ID from that response. | | `429` | More than 60 account management requests were made with the same management API Key within one minute. | Reduce polling and retry with exponential backoff. | | `500` | The service cannot process the request temporarily. | Retry a limited number of times with exponential backoff. | The per-Key usage and account usage endpoints share the same 60 requests-per-minute limit. Keep usage exports server-side, use cursor pagination for large result sets, and persist the returned cursor with the complete filter set when a job needs to resume. # UniGateway Account Usage & Billing API > Category: Operations & Billing | Last updated: 2026-08-09 Use a management API Key to rank account API Key usage and retrieve billing details or daily summaries. # UniGateway Account Usage & Billing API This document explains how to use a Management API Key to query usage rankings, billing details, and daily billing summaries for all API Keys under the same account. Amounts, currencies, API Key statuses, and available data are determined by the live API response. ## Overview | Endpoint | Purpose | | --- | --- | | `GET /v1/account/api-keys/usage-stats` | Calculates and ranks API Key usage within an account by token count or cost, and returns model-level details. | | `GET /v1/account/getDailyList` | Queries billing details broken down by billing date, API Key, model, token type, and currency. | | `GET /v1/account/getDailySummary` | Queries daily billing summaries aggregated by billing date, API Key, and currency. | ## Migrating From Legacy Endpoints The following legacy paths remain available and share exactly the same processing logic as their new paths: | Legacy endpoint | New endpoint | | --- | --- | | `GET /v1/account/billing` | `GET /v1/account/getDailyList` | | `GET /v1/account/billing/daily-summary` | `GET /v1/account/getDailySummary` | New integrations should use `getDailyList` and `getDailySummary`. ## Prerequisites These endpoints require a Management API Key. Standard API Keys cannot call them. First read [Management API Key](/docs/management-key) and follow its console steps to create and save a Management API Key; the complete Key is shown only once on the creation success page and cannot be viewed or copied again after you leave it. For API Key access control, rotation, and security guidance, see [Account & API Keys](/docs/account-and-api-keys). Store the Management API Key only in a server-side environment variable. Do not include it in browser code, logs, public repositories, or exported files: ```bash export UNIGATEWAY_MANAGEMENT_KEY="" ``` Windows PowerShell: ```powershell $env:UNIGATEWAY_MANAGEMENT_KEY = "" ``` A Management API Key can read usage and billing data for API Keys in the account. If the key is exposed, rotate it immediately in the console and reload the server-side configuration of services that use it. ## Common Conventions | Item | Value | | --- | --- | | Base URL | `https://unigateway.ai` | | Authentication | `Authorization: Bearer $UNIGATEWAY_MANAGEMENT_KEY` | | Response format | `application/json` | All endpoints require a Management API Key. A standard API Key or invalid Management API Key causes the request to fail. Responses never include complete values or hashes of standard API Keys or Management API Keys. ### Rate Limits and Caching - Each Management API Key can make up to 60 requests per minute. - `usage-stats` uses a separate rate-limit bucket; `getDailyList` and `getDailySummary` share a rate-limit bucket of 60 requests per minute. - All responses, including error responses, include `Cache-Control: no-store`. Run bulk exports on the server. After receiving `429 Too Many Requests`, reduce concurrency, wait according to the response's `Retry-After`, then resume the query with the same filters. ### Array Filter Parameters `apiKeyIds`, `apiKeyNames`, `modelTypes`, `modelNames`, `apiKeyStatuses`, `tokenTypes`, and `currencies` support repeated parameters, comma-separated values, or JSON arrays. Each array filter accepts up to 100 values; omitting a parameter or passing `[]` means that dimension is unrestricted. ```text apiKeyIds=key-1,key-2 apiKeyIds=key-1&apiKeyIds=key-2 apiKeyIds=["key-1","key-2"] ``` `modelTypes` supports `claude`, `gemini`, `gpt`, `qwen`, `deepseek`, and `other`. `apiKeyStatuses` supports `ACTIVE`, `INSUFFICIENT_BALANCE`, `USER_DISABLED`, and `LIMIT_EXCEEDED`; status values are case-insensitive and converted to uppercase by the server. ## API Key Usage Rankings ```http GET /v1/account/api-keys/usage-stats ``` This endpoint ranks API Keys in an account by token count or cost and returns model-level details in each API Key item, sorted by the same rule. The statistics aggregate only usage records with a status of `SUCCESS` or `PARTIAL`; deleted API Keys are excluded from the results. ### Query Parameters | Parameter | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `date` | string | No | Current UTC+8 day | `YYYY-MM-DD` or `YYYY-MM-DDTHH:mm:ssZ`. Calculates the complete calendar day in UTC+8 containing this value. Cannot be used with `start_date` or `end_date`. | | `start_date` | string | Conditionally required | - | Must be provided with `end_date`. Supports a date-only value or a strict UTC second-level timestamp. | | `end_date` | string | Conditionally required | - | Must be provided with `start_date`. The maximum query range is 366 days. | | `sort_by` | string | No | `tokens` | Sort criterion: `tokens` or `cost`. | | `limit` | integer | No | `50` | API Keys per page, from `1` to `100`. | | `offset` | integer | No | `0` | Offset, from `0` to `100000`. | | `apiKeyIds` | array | No | All | API Key ID filter. | | `modelTypes` | array | No | All | Model type filter. | | `modelNames` | array | No | All | Exact model name filter. | | `apiKeyStatuses` | array | No | All | API Key status filter. | The endpoint always calculates complete UTC+8 calendar days and returns `time_zone: "UTC+8"`; it does not accept a timezone parameter. `date=2026-07-31` represents the 2026-07-31 UTC+8 day, from `2026-07-30T16:00:00.000Z` through `2026-07-31T15:59:59.999Z`. A value of `date=2026-07-31T23:59:59Z` falls on the 2026-08-01 UTC+8 day, so the endpoint queries `2026-07-31T16:00:00.000Z` through `2026-08-01T15:59:59.999Z`. Date-only ranges expand to the beginning and end of the UTC+8 day; second-level `start_date` is an exact start boundary and `end_date` includes all milliseconds in that second. Second-level timestamps accept only uppercase `Z`, not timezone offsets or fractional seconds. Response date ranges are always normalized to `YYYY-MM-DDTHH:mm:ssZ`. ### Request Example The following request queries API Key usage rankings for July 2026 by cost: ```bash curl -G 'https://unigateway.ai/v1/account/api-keys/usage-stats' \ -H "Authorization: Bearer $UNIGATEWAY_MANAGEMENT_KEY" \ --data-urlencode 'start_date=2026-07-01' \ --data-urlencode 'end_date=2026-07-31' \ --data-urlencode 'sort_by=cost' \ --data-urlencode 'limit=50' \ --data-urlencode 'offset=0' ``` ### Response Example ```json { "date_range": { "start_date": "2026-07-30T16:00:00Z", "end_date": "2026-07-31T15:59:59Z", "time_zone": "UTC+8" }, "sort_by": "tokens", "currency": "USD", "items": [ { "rank": 1, "id": "api-key-id-1", "name": "Production", "key_prefix": "sk-prod", "status": "ACTIVE", "request_count": "42", "total_tokens": "125000", "total_cost": "3.750000000000", "models": [ { "rank": 1, "model_id": "model-id-1", "model_type": "gpt", "model_name": "gpt-5", "request_count": "42", "total_tokens": "125000", "total_cost": "3.750000000000" } ] } ], "total": 1, "limit": 50, "offset": 0, "has_more": false } ``` | Field | Type | Description | | --- | --- | --- | | `date_range.start_date` / `date_range.end_date` | string | Actual statistics range, returned as UTC `Z` timestamps; interpret the corresponding statistics day according to `time_zone`. | | `date_range.time_zone` | string | Statistics timezone, always `UTC+8`. | | `sort_by` | string | Current sort criterion. | | `currency` | string | Currency of aggregated amounts. | | `items[].rank` | integer | API Key rank, starting at `1`; paginated results account for `offset`. | | `items[].id` / `items[].name` / `items[].key_prefix` | string / null | API Key ID, name, and non-sensitive prefix. | | `items[].status` | string | Current API Key status. | | `items[].request_count` / `items[].total_tokens` | string | Aggregated request and token counts, both returned as strings. | | `items[].total_cost` | string | Aggregated cost as a decimal string. | | `items[].models` | array | Model-level details for this API Key, ranked by the same rule. | | `items[].models[].rank` | integer | Model rank within the current API Key, starting at `1`. | | `items[].models[].model_id` / `model_type` / `model_name` | string / null | Model ID, type, and name. | | `items[].models[].request_count` / `total_tokens` / `total_cost` | string | Model-level aggregated request count, token count, and cost. | | `total` / `limit` / `offset` / `has_more` | integer / boolean | Pagination total, current page size, offset, and whether another page is available. | Amounts, request counts, and token counts are all returned as strings. Use a decimal fixed-point type for amounts, not binary floating-point numbers. ## Account Billing Details ```http GET /v1/account/getDailyList ``` This endpoint returns flat billing details broken down by billing date, API Key, model, token type, and currency. Details are sorted by billing date in descending order, then stably by model name, token type, currency, and API Key. ### Query Parameters | Parameter | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `startDate` | string | Conditionally required | Current billing date | Must be provided with `endDate`. Supports RFC 3339 timestamps or `YYYY-MM-DD`. | | `endDate` | string | Conditionally required | Current billing date | Must be provided with `startDate`. The maximum explicit range is 92 days. | | `pageSize` | integer | No | `100` | Detail records per page; values above `100` are treated as `100`. | | `pageNum` | integer | No | `1` | Page number, starting at `1`. | | `apiKeyId` | string | No | All | Exact filter for a single API Key ID. | | `apiKeyName` | string | No | All | Exact filter for the current API Key name. | | `apiKeyIds` | array | No | All | API Key ID filter. | | `apiKeyNames` | array | No | All | Current API Key name filter. | | `modelTypes` | array | No | All | Model type filter. | | `modelNames` | array | No | All | Exact model name filter. | | `tokenTypes` | array | No | All | Token type filter. | | `currencies` | array | No | All | Three-letter ISO 4217 currencies; `RMB` is normalized to `CNY`. | | `apiKeyStatuses` | array | No | All | API Key status filter. | `start_date` / `end_date` can be used in place of `startDate` / `endDate`. When `pageSize` and `pageNum` are not used, compatible `limit` and `offset` pagination can be used instead. When no explicit date range is provided, `period=day|month|year` can be used with the corresponding `date=YYYY/MM/DD|YYYY/MM|YYYY`. Both billing endpoints group results by UTC+8 and return `billingDateTimezone: "utc+8"`; they do not accept a timezone parameter. `startDate` and `endDate` support `YYYY-MM-DD` or RFC 3339 timestamps. RFC 3339 timestamps must include `Z` or `+/-HH:MM` and support 1 to 3 fractional-second digits. An `endDate` without fractional seconds includes all milliseconds in its final second; the beginning and end of date-only days are interpreted in UTC+8. RFC 3339 timestamps represent absolute instants and are not rewritten; results remain grouped by UTC+8 billing date. Supported `tokenTypes` are: ```text textInputTokens, textOutputTokens, imageInputTokens, imageOutputTokens, videoInputTokens, videoOutputTokens, audioInputTokens, audioOutputTokens, cacheCreationTokens5m, cacheCreationTokens1h, cacheTokens ``` | `tokenType` | Meaning | | --- | --- | | `cacheCreationTokens5m` | Cache creation or write tokens with a TTL of 5 minutes. | | `cacheCreationTokens1h` | Cache creation or write tokens with a TTL of 1 hour. | | `cacheTokens` | Cache hit or read tokens. | Claude 1-hour cache creation is classified as `cacheCreationTokens1h`, not as `cacheTokens`, which represents cache reads. Cache writes without an explicit TTL are classified as `cacheCreationTokens5m`. `cacheWriteTokens` is an internal cache-write aggregate field, not a public `tokenType`. ### Request Example The following request queries billing details for July 2026 using UTC+8 billing dates: ```bash curl -G 'https://unigateway.ai/v1/account/getDailyList' \ -H "Authorization: Bearer $UNIGATEWAY_MANAGEMENT_KEY" \ --data-urlencode 'startDate=2026-07-01T00:00:00Z' \ --data-urlencode 'endDate=2026-07-31T23:59:59Z' \ --data-urlencode 'pageSize=100' \ --data-urlencode 'pageNum=1' ``` ### Response Example ```json { "code": 0, "message": "success", "data": { "total": 1, "provider": "UniGateway", "rows": [ { "billMonth": "202607", "billDay": "2026-07-31", "billingDateTimezone": "utc+8", "account": "account-id-1", "apiKeyId": "api-key-id-1", "apiKeyName": "Production", "modelType": "claude", "modelName": "claude-opus-4-8", "tokenType": "cacheCreationTokens1h", "tokenCount": "5841", "tokenUnit": "piece", "currency": "USD", "subtotalBeforeTax": "0.05841000", "subtotalAfterDiscount": "0.05841000", "totalAmountAfterTax": "0.06322883", "entryType": "normal", "price": "0.00001000" } ] } } ``` | Field | Type | Description | | --- | --- | --- | | `code` | integer | `0` indicates a successful request. | | `message` | string | Response message. | | `data.total` | integer | Total number of complete detail records matching the filters, unaffected by the current page. | | `data.provider` | string | Data provider identifier. | | `data.rows` | array | Billing details for the current page. | | `data.rows[].billMonth` / `billDay` | string | Billing month (`YYYYMM`) and billing date. | | `data.rows[].billingDateTimezone` | string | Billing date grouping timezone, always `utc+8`. | | `data.rows[].account` | string | UniGateway account ID. | | `data.rows[].apiKeyId` / `apiKeyName` | string | Internal API Key ID and its current name. | | `data.rows[].modelType` / `modelName` | string | Model type and name. | | `data.rows[].tokenType` / `tokenCount` / `tokenUnit` | string | Token type, count, and unit. | | `data.rows[].currency` | string | Currency of the amount. | | `data.rows[].subtotalBeforeTax` | string | Subtotal before discounts, as a decimal string with 8 fractional digits. | | `data.rows[].subtotalAfterDiscount` | string | Subtotal after discounts, as a decimal string with 8 fractional digits. | | `data.rows[].totalAmountAfterTax` | string | Total after tax, as a decimal string with 8 fractional digits. | | `data.rows[].entryType` | string | Record type: `normal`, `refund`, or `adjustment`. Currently only `normal` is returned. | | `data.rows[].price` | string | Derived unit price, as a decimal string with 8 fractional digits. | Amounts and derived unit prices are decimal strings with 8 fractional digits. `totalAmountAfterTax` is calculated using the `tax_rate_snapshot` of each `usage_records` record; this field is a percentage, for example, `8.25` represents `8.25%`: ```text Record total after tax = Record subtotal after discount * (1 + tax_rate_snapshot / 100) ``` When a historical record's snapshot is `null`, it is treated as `0%`. When the same detail dimensions have different tax rates, the API first calculates the total after tax for each record, then aggregates and retains 8 fractional digits; it does not apply a single tax rate to the aggregated subtotal after discount. The current data source contains only normal usage records, so every detail record always returns `entryType: "normal"`; refund and adjustment records have not yet been added to this endpoint. Do not derive current model prices from the costs or unit prices in the examples. Refer to the [UniGateway Model Catalog](https://unigateway.ai/models) and the live API response. ## Daily Billing Summary ```http GET /v1/account/getDailySummary ``` This endpoint aggregates billing details by UTC+8 billing date, API Key, and currency. Its filtering and date parsing rules are the same as [Account Billing Details](#account-billing-details). ### Query Parameters Except for the following differences, this endpoint supports the same query parameters and compatible forms as `GET /v1/account/getDailyList`: | Parameter | Default | Description | | --- | --- | --- | | `startDate` / `endDate` | Current billing date | The maximum explicit query range is 366 days. | | `apiKeyId` | All | Exact filter for a single API Key ID. | | `apiKeyName` | All | Exact filter for the current API Key name. | | `pageSize` | `31` | Summary rows per page; values above `400` are treated as `400`. | | `pageNum` | `1` | Summary page number, starting at `1`. | `apiKeyId` and `apiKeyName` can be used independently; when both are provided, they are matched with `AND`. To query multiple values at once, use `apiKeyIds` and `apiKeyNames` in the format described by the [Array Filter Parameters](#array-filter-parameters) convention in this document. ### Request Example ```bash curl -G 'https://unigateway.ai/v1/account/getDailySummary' \ -H "Authorization: Bearer $UNIGATEWAY_MANAGEMENT_KEY" \ --data-urlencode 'startDate=2026-07-01' \ --data-urlencode 'endDate=2026-07-31' \ --data-urlencode 'apiKeyId=api-key-id-1' \ --data-urlencode 'apiKeyName=Production' \ --data-urlencode 'pageSize=31' \ --data-urlencode 'pageNum=1' ``` ### Response Example ```json { "code": 0, "message": "success", "data": { "total": 1, "provider": "UniGateway", "rows": [ { "billMonth": "202607", "billDay": "2026-07-31", "billingDateTimezone": "utc+8", "account": "account-id-1", "apiKeyId": "api-key-id-1", "apiKeyName": "Production", "currency": "USD", "subtotalBeforeTax": "25.68000000", "subtotalAfterDiscount": "20.54400000", "totalAmountAfterTax": "22.23888000", "entryCount": 3 } ] } } ``` | Field | Type | Description | | --- | --- | --- | | `code` / `message` | integer / string | Business response code and message; the request succeeds when `code` is `0`. | | `data.total` | integer | Total number of summary rows, not the number of billing details. | | `data.provider` | string | Data provider identifier. | | `data.rows[].billMonth` / `billDay` | string | Billing month (`YYYYMM`) and billing date. | | `data.rows[].billingDateTimezone` | string | Billing date grouping timezone, always `utc+8`. | | `data.rows[].account` | string | UniGateway account ID. | | `data.rows[].apiKeyId` / `apiKeyName` | string | Internal API Key ID and its current name. | | `data.rows[].currency` | string | Currency of the aggregated amounts. | | `data.rows[].subtotalBeforeTax` | string | Subtotal before discounts, as a decimal string with 8 fractional digits. | | `data.rows[].subtotalAfterDiscount` | string | Subtotal after discounts, as a decimal string with 8 fractional digits. | | `data.rows[].totalAmountAfterTax` | string | Total after tax, as a decimal string with 8 fractional digits. | | `data.rows[].entryCount` | integer | Number of billing detail records matched by this summary row. | For identical filters, summary amounts equal the sum of the billing detail endpoint's amounts with 8 fractional digits, including each independently calculated `totalAmountAfterTax`. Both endpoints always use UTC+8 billing dates; when reconciling, fix the date range, API Key, and currency filters before comparing the two result sets. ## Error Handling and Rate Limits The error response for `GET /v1/account/api-keys/usage-stats` has the following format: ```json { "error": { "code": "UnprocessableEntity", "message": "date must use YYYY-MM-DD or YYYY-MM-DDTHH:mm:ssZ UTC format", "param": null, "type": "BadRequest" } } ``` Common HTTP status codes for this endpoint are `400`, `401`, `429`, and `500`. Check the date format, parameter combination, and Management API Key before retrying. `GET /v1/account/getDailyList` and `GET /v1/account/getDailySummary` use the same error structure: ```json { "code": 40003, "message": "The date range must not exceed 92 days", "data": null } ``` | HTTP Status Code | Business `code` | Common Cause | Resolution | | --- | ---: | --- | --- | | `400` / `422` | `40001` | Invalid parameter format or filter value. | Check dates, timezones, pagination values, and array filter values. | | `400` / `422` | `40002` | Only a start or end date was provided. | Provide both `startDate` and `endDate`, or omit both. | | `400` / `422` | `40003` | The date order or range does not meet the constraints. | Confirm that the start time is before the end time and reduce the range to the limit allowed by the relevant endpoint. | | `401` | `40100` | Management API Key is missing or invalid. | Confirm the server has loaded a valid `UNIGATEWAY_MANAGEMENT_KEY`, then reload the configuration and make the request again. | | `403` | `40300` | The current Management API Key does not have access. | Check the key status and access controls in the Management API Key console. | | `429` | `42900` | Rate limit exceeded. | Wait according to `Retry-After: 60`, reduce concurrency, and retry. | | `503` | `50300` | Service is temporarily unavailable. | Retry a limited number of times with exponential backoff. | | `500` | `50000` | Unexpected server error. | Record the request time, path, and response information, then retry a limited number of times. | Check both the HTTP status code and the business `code` in the JSON response. Do not rely on error message text for programmatic decisions. ## FAQ ### Q: Why does using a standard API Key return `401`? All three endpoints in this document accept only Management API Keys. Confirm the request URL begins with `https://unigateway.ai` and that `Authorization` uses a currently valid Management API Key with the `mk-` prefix. Load `UNIGATEWAY_MANAGEMENT_KEY` into the server process, restart or reload that process, then run `GET /v1/account/api-keys/usage-stats?limit=1` to verify it. Standard API Keys cannot call account-level endpoints. ### Q: How do I investigate inconsistent amounts between billing details and daily summaries? First, set `startDate`, `endDate`, `apiKeyId`, `apiKeyName`, `apiKeyIds`, `apiKeyNames`, `modelTypes`, `modelNames`, `tokenTypes`, `currencies`, and `apiKeyStatuses` to exactly the same values in both requests. Both endpoints always aggregate by UTC+8 billing date. Confirm that the detail endpoint's range does not exceed 92 days and the summary endpoint's range does not exceed 366 days, then request the data again. Daily summary amounts are the net total of detail amounts for the same filters; do not compare across currencies or different filters directly. ### Q: How can I prevent omissions or duplicates when exporting large volumes of billing data? Use a fixed date range and filters, fetch pages sequentially with `pageNum` starting at `1`, and retain the page number after each successfully processed page. The endpoint always groups by UTC+8 billing date, so no timezone filter is needed. When encountering `429`, wait according to `Retry-After` and continue from the last successful page; do not change `pageSize`, the date range, or filters within the same export job. Exported API Key IDs, models, timestamps, and costs are sensitive operational data and should be stored and processed only in controlled environments. ## Compatibility & Troubleshooting # API Compatibility Matrix > Category: Compatibility & Troubleshooting | Last updated: 2026-08-09 Confirmed UniGateway supported and unavailable endpoints, with concrete Gemini paths and known gaps. # API Compatibility Matrix Main UniGateway endpoint families. > Availability may vary by account plan, region, or feature rollout stage. ## Supported Endpoints ## Code examples ### curl ```curl curl https://api.unigateway.ai/v1/models \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" ``` ### python ```python from openai import OpenAI client = OpenAI(api_key="", base_url="https://api.unigateway.ai/v1") print(client.models.list()) ``` ### typescript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1" }); console.log(await client.models.list()); ``` Base root: `https://api.unigateway.ai` | Method | Path | Purpose | |---|---|---| | `GET` | `/v1/models` | list available models | | `POST` | `/v1/chat/completions` | chat and text generation | | `POST` | `/v1/completions` | legacy completions | | `POST` | `/v1/responses` | enhanced conversation interface | | `POST` | `/v1/responses/compact` | compact responses | | `POST` | `/v1/embeddings` | vector embeddings | | `POST` | `/v1/moderations` | content moderation | | `POST` | `/v1/images/generations` | image generation | | `POST` | `/v1/images/edits` | image editing | | `POST` | `/v1/audio/transcriptions` | audio transcription | | `POST` | `/v1/audio/translations` | audio translation | | `GET` | `/v1/realtime` | realtime WebSocket | | `POST` | `/v1/videos` | Sora video generation | | `GET` | `/v1/videos/{id}` | Sora video status | | `GET` | `/v1/videos/{id}/content` | Sora video download | | `POST` | `/v1/messages` | Anthropic-compatible requests | | `GET` | `/v1beta/models` | Gemini-style model discovery | | `POST` | `/v1beta/models/{model}:generateContent` | Gemini text, multimodal, and image generation | | `POST` | `/v1beta/models/{model}:streamGenerateContent?alt=sse` | Gemini SSE streaming | Gemini-compatible paths use UniGateway Bearer Token authentication. Do not send `x-goog-api-key` or a `key=` query parameter. ## Choose the Right Endpoint | Goal | Endpoint | Model example | |---|---|---| | OpenAI-compatible chat | `/v1/chat/completions` | `gpt-5.4` | | Claude-native messages | `/v1/messages` | `claude-sonnet-4-6` | | Gemini-native generation | `/v1beta/models/{model}:generateContent` | `gemini-3-pro-preview` | | OpenAI-compatible images | `/v1/images/generations` | `gpt-image-2` | | Gemini image generation | `/v1beta/models/{model}:generateContent` | `gemini-3-pro-image-preview` | | Sora video generation | `/v1/videos` | `sora-2` | | Seedance video generation | `/api/v3/contents/generations/tasks` (api.unigateway.ai) | `doubao-seedance-2-0-fast-260128` | ## Unavailable Routes - `POST /v1/images/variations` - `POST /v1/audio/speech` - `GET/POST /v1/files` and related file operations - `POST/GET /v1/fine-tunes` and related fine-tune operations - `DELETE /v1/models/:model` Start integration with `GET /v1/models` and `POST /v1/chat/completions`. Add other endpoints after confirming they are enabled in your environment. Use `supported_endpoint_types` from `GET /v1/models` as a hint, then verify with a real request before production traffic. # Error Codes Reference > Category: Compatibility & Troubleshooting | Last updated: 2026-08-09 Troubleshooting manual organized by problem category: auth, billing, model, params, rate limits, upstream. # Error Codes Reference Complete reference for all UniGateway API error responses, with causes and resolution steps. ## Error Response Format ## Code examples ### curl ```curl curl https://api.unigateway.ai/v1/chat/completions \ -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"gpt-5.4","messages":[{"role":"user","content":"hello"}]}' -v ``` ### python ```python from openai import OpenAI client = OpenAI(api_key="", base_url="https://api.unigateway.ai/v1") resp = client.chat.completions.create(model="gpt-5.4", messages=[{"role":"user","content":"hello"}]) ``` ### typescript ```typescript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.UNIGATEWAY_API_KEY, baseURL: "https://api.unigateway.ai/v1" }); const resp = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "hello" }] }); ``` All errors follow a unified JSON structure: ```json { "error": { "code": "429", "type": "rate_limit", "message": "Rate limit exceeded" } } ``` | Field | Type | Description | |---|---|---| | `code` | string | HTTP status code (as a string) | | `type` | string | Error category — use this for programmatic logic | | `message` | string | Human-readable description — for display only, may change | ## Error Quick Reference | HTTP Status | Error Type | Description | |---|---|---| | 400 | `invalid_params` | Invalid request parameters | | 401 | `auth_invalid` | Invalid or missing API key | | 402 | `insufficient_credit` | Account overdue | | 402 | `reject_no_credit` | Insufficient balance for this model | | 403 | `access_denied` | Insufficient permissions | | 403 | `safety_check_failed` | Upstream safety policy triggered | | 404 | `model_not_available` | Model not in current plan | | 404 | `invalid_model` | Model does not exist | | 404 | `model_not_supported` | Model does not support this API | | 413 | `prompt_too_long` | Request exceeds max context length | | 422 | `provider_unprocessable` | Upstream cannot process request | | 429 | `rate_limit` | Rate limit exceeded | | 500 | `internal_server_error` | Platform internal error | | 500 | `provider_api_error` | Upstream provider API error | | 500 | `provider_error` | Upstream provider service error | | 502 | `no_provider_available` | No upstream provider available | | 503 | `service_unavailable` | Service temporarily unavailable | | 524 | `provider_timeout` | Upstream provider timed out | ## Authentication Errors ### 401 — `auth_invalid` ```json { "error": { "code": "401", "type": "auth_invalid", "message": "Invalid API key provided" } } ``` **Cause:** The API key is missing, invalid, or expired. **Resolution:** 1. Verify the `Authorization: Bearer ` header is set correctly 2. Log in to the console to confirm the key has not been revoked 3. Ensure there are no extra spaces or line breaks in the key value 4. If using an environment variable, confirm it is set and exported ## Authorization Errors ### 403 — `access_denied` **Cause:** The API key does not have permission to access the requested resource. **Resolution:** 1. Check whether you are using a standard key for a management endpoint (management key required) 2. Verify your account has access to the requested model or feature ### 403 — `safety_check_failed` **Cause:** The input content was blocked by an upstream safety policy. **Resolution:** 1. Adjust the prompt, images, or tool input content 2. Remove descriptions that may trigger safety rules 3. Switch to a different model with different safety thresholds ## Billing Errors ### 402 — `insufficient_credit` **Cause:** The account balance is negative (overdue). **Resolution:** 1. Go to **Settings → Billing** to top up your balance ### 402 — `reject_no_credit` **Cause:** The account balance is zero or too low, and the requested model requires a positive balance. **Resolution:** 1. Top up your account balance 2. Switch to a model or plan that allows low-balance access ## Parameter Validation Errors ### 400 — `invalid_params` Returned when request parameters fail validation. Specific sub-cases: | Error Message | Cause | Solution | |---|---|---| | `Parameter model is required` | Missing or empty `model` field | Add a valid `model` value | | `Model {model} is not valid` | Model identifier does not exist | Check `GET /v1/models` for valid names | | `Parameter messages is required` | Missing `messages` field | Include a `messages` array | | `Parameter messages can not be empty` | `messages` is an empty array | Provide at least one message | | `Parameter messages can not contain null elements` | `messages` contains null entries | Remove null items | | `Parameter messages can not contain elements without content` | Message missing `content` field | Ensure every message has content | | `Parameter stream_options is not supported while stream is false` | `stream_options` set without `stream: true` | Set `stream: true` or remove `stream_options` | | `Parameter n greater than 1 is not supported` | `n` value exceeds 1 | Set `n` to 1 or omit it | | `Parameter temperature is not valid` | Negative `temperature` value | Use a value >= 0 | ### 413 — `prompt_too_long` **Cause:** The request body (including all messages) exceeds the model's maximum context length. **Resolution:** 1. Reduce the number or length of messages 2. Use a model that supports a larger context window 3. Summarize earlier conversation turns instead of including full history ### 422 — `provider_unprocessable` **Cause:** The request passed platform validation but the upstream provider could not process it. Typically occurs when the upstream has stricter requirements for field structure, tools, or multimodal inputs. **Resolution:** 1. Review the specific description in the `message` field 2. Check that all parameters are compatible with the selected model 3. Remove optional/advanced parameters and retry with a minimal request ## Model Availability Errors ### 404 — `model_not_available` **Cause:** The model exists but is not currently accessible for your account. **Resolution:** 1. Upgrade to a plan that includes the target model ### 404 — `invalid_model` **Cause:** The specified model identifier does not match any known model. **Resolution:** 1. Check for typos in the model name 2. Refer to `GET /v1/models` for valid identifiers ### 404 — `model_not_supported` **Cause:** The model exists but does not support the API endpoint being called. **Resolution:** 1. Check the model's detail page to confirm which APIs it supports 2. Switch to a model that supports the current API ## Rate Limiting Errors ### 429 — `rate_limit` **Cause:** The request rate has exceeded the allowed limit. This may originate from platform-level or upstream provider rate limiting. **Resolution:** 1. Reduce request frequency 2. Add delays between requests or use exponential backoff 3. Spread peak traffic more evenly over time ## Server and Upstream Errors ### 500 — `internal_server_error` **Cause:** An unexpected platform-side error occurred. **Resolution:** 1. Retry with exponential backoff — most 500 errors are transient 2. If the issue persists, contact support (via the console or support email) with the `X-UniGateway-RequestId` from response headers ### 500 — `provider_error` / `provider_api_error` **Cause:** The request reached the upstream provider, and the issue originated on the provider's side. **Resolution:** 1. Retry with exponential backoff 2. If persistent, contact support with `X-UniGateway-RequestId` ### 502 — `no_provider_available` **Cause:** No upstream provider is currently available to handle the request. Possible reasons: - The specified provider does not exist - All configured upstream providers for this model are experiencing failures - All providers failed after retries **Resolution:** 1. Relax or remove provider-specific routing rules 2. Wait briefly and retry 3. Retry the request ### 503 — `service_unavailable` **Cause:** The platform is temporarily unavailable, typically during maintenance or capacity events. **Resolution:** 1. Retry after a brief delay 2. Check the status page for ongoing incidents ### 524 — `provider_timeout` **Cause:** The upstream provider did not respond within the timeout window. **Resolution:** 1. Retry with exponential backoff 2. Consider using a faster model variant 3. Reduce prompt length if the model is approaching its processing limit ## Streaming-Specific Error Handling When using `stream: true`, errors manifest differently than in non-streaming requests. ### Errors During Streaming If an error occurs after streaming has started, the stream will terminate or emit a failure event within the stream. Clients should handle: - HTTP status code evaluation before stream consumption - Event stream completeness checks (look for `data: [DONE]`) - In-stream error event parsing - Connection interruption handling ### SSE Keep-Alive During long-running requests, you may receive SSE comments: ``` : UNIGATEWAY PROCESSING ``` This is **not an error** — it is a keep-alive signal sent periodically to prevent connection timeouts. Per the SSE specification, your client parser should ignore lines starting with `:`. ### Stream Disconnection If the client disconnects voluntarily (e.g., cancels the request), the server cleans up the in-progress stream. Client-initiated disconnections do not produce error responses. ## Error Handling Best Practices 1. **Always handle errors by the `type` field** — Use `type` for branching logic; `message` is for display only and may change 2. **Implement retry logic for retryable errors:** | Category | Status Codes | |---|---| | Retryable | 429, 500, 502, 503, 524 | | Retryable after fix | 402 | | Non-retryable | 400, 401, 403, 404, 413, 422 | 3. **Save `X-UniGateway-RequestId`** — Include this ID when contacting support to expedite resolution 4. **Handle streaming responses gracefully** — Always handle incomplete responses and connection interruptions 5. **Use exponential backoff for retries** — Gradually increase the interval (e.g., 1s → 2s → 4s → 8s), with random jitter to avoid the "thundering herd" effect ### Example Retry Logic (Python) ```python import time import random from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.unigateway.ai/v1", ) max_retries = 3 for attempt in range(max_retries): try: resp = client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello"}], ) print(resp.choices[0].message.content) break except Exception as e: if attempt < max_retries - 1: delay = min(0.3 * (2 ** attempt), 8) + random.uniform(0, 0.1) time.sleep(delay) else: raise ``` ## FAQ **Q: The same request works for some models but returns 404 for others.** A: Different plans include different model lists. Upgrade your plan for full model access. **Q: I occasionally see 500 errors. Should I be concerned?** A: Occasional 500 errors are normal in distributed systems. Implement automatic retry with exponential backoff. Contact support if the error rate stays consistently high. **Q: What does `UNIGATEWAY PROCESSING` mean during a streaming request?** A: This is an SSE keep-alive comment (prefixed with `:`), sent periodically to indicate the request is still being processed. It is not an error — your SSE client should ignore comment lines per the specification. **Q: How can I tell which upstream provider handled my request?** A: Provide the `X-UniGateway-RequestId` from the response headers to the support team for request tracing.