On this page
Getting started
Policy and operations
Reliability
Sophy documentation
Build on a governed AI gateway
Use supported OpenAI client methods at https://sophy.in/v1. Each Sophy key is bound to one primary model and carries its policy, limits, and optional knowledge server-side.
Introduction
Sophy is an OpenAI-compatible gateway and operator console. Applications send input in familiar wire formats; Sophy authenticates the key, resolves its centrally managed configuration, calls the bound model through that key’s project-owned Vercel AI Gateway credential, and records project-attributed usage.
Compatibility is intentionally scoped. Chat Completions and Responses cover text, streaming, multimodal input, and function-tool loops. Dedicated routes cover audio transcription, embeddings, image generation, short-lived file uploads, and the primary model bound to a key. The sections below call out differences from the full OpenAI platform.
https://sophy.in/v1Supported API surfaces
| Method | Route | Required capability | Compatibility boundary |
|---|---|---|---|
| POST | /v1/chat/completions | Language generation | Text, streaming, multimodal input, function tools |
| POST | /v1/responses | Language generation | Stateless; full input required on every call |
| POST | /v1/audio/transcriptions | Transcription | Multipart audio; JSON with raw or policy-processed text |
| POST | /v1/embeddings | Embedding | String input; float or base64 vectors |
| POST | /v1/images/generations | Image generation | Base64 image output only |
| POST | /v1/files | Any | 201 response; 4 MiB; cleanup-eligible after 24 hours |
| GET | /v1/models | Any | Returns only the primary model bound to this key |
One key, one primary model
Bind keys around workloads and call a route the primary model’s capabilities support. The client-sent model never switches that model. A transcription key may separately name a language model for key-owned transcript processing.
Authentication
Send the Sophy key as a Bearer token. Keys look like mw_live_… and are issued inside a project in the Sophy console. Sign in with any verified email to create a renameable My Project, or join an existing project through an Admin invitation.
Authorization: Bearer mw_live_…Missing Bearer authentication returns 401 missing_api_key. A malformed, unknown, revoked, expired, or inactive-project key returns 401 invalid_api_key. Keys are checked against the database on every request, so edits and revocation apply immediately.
Two credentials, separate jobs
A Sophy API key authenticates your application to Sophy. A Project Admin separately connects a Vercel AI Gateway key that pays for and routes every AI operation in that project. Sophy never returns the stored Gateway secret.
Quickstart
Change the base URL and API key, then call one of the supported methods. The placeholder model is required by some OpenAI SDK methods but Sophy uses the model bound to the key.
from openai import OpenAI
client = OpenAI(
base_url="https://sophy.in/v1",
api_key="mw_live_…",
)
resp = client.chat.completions.create(
model="sophy", # ignored — the key owns the model
messages=[{"role": "user", "content": "Hello, Sophy!"}],
)
print(resp.choices[0].message.content)import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://sophy.in/v1",
apiKey: "mw_live_…",
});
const resp = await client.chat.completions.create({
model: "sophy", // ignored — the key owns the model
messages: [{ role: "user", content: "Hello, Sophy!" }],
});
console.log(resp.choices[0].message.content);curl https://sophy.in/v1/chat/completions \
-H "Authorization: Bearer mw_live_…" \
-H "Content-Type: application/json" \
-d '{
"model": "sophy",
"messages": [{"role": "user", "content": "Hello, Sophy!"}]
}'Chat Completions
https://sophy.in/v1/chat/completions| Field | Type | Notes |
|---|---|---|
| messages | array | Required and non-empty. Supports user, assistant, and tool turns plus text, image, and file content. System/developer handling depends on Agent mode. |
| stream | boolean | Optional. Emits OpenAI chat.completion.chunk SSE frames. |
| stream_options | object | Optional. { include_usage: true } adds a final usage-only chunk. |
| tools | array | Optional function definitions. Sophy returns calls but never executes them. See Tool calling. |
| tool_choice | string | object | auto, none, required, or one named function. |
Key-owned or ignored: model, temperature, top_p, max_tokens, max_completion_tokens, response_format, n, and user. Sophy applies the key’s configured model and generation parameters instead.
Buffered response
{
"id": "chatcmpl-…",
"object": "chat.completion",
"created": 1735689600,
"model": "anthropic/claude-sonnet-4.6",
"choices": [{
"index": 0,
"message": { "role": "assistant", "content": "Hello! How can I help?" },
"finish_reason": "stop"
}],
"usage": { "prompt_tokens": 9, "completion_tokens": 7, "total_tokens": 16 }
}Streaming
A stream begins with an assistant-role chunk, emits text and/or tool-call deltas, finishes with a reason chunk, optionally emits usage, and ends with data: [DONE].
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","created":1735689600,"model":"anthropic/claude-sonnet-4.6","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","created":1735689600,"model":"anthropic/claude-sonnet-4.6","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","created":1735689600,"model":"anthropic/claude-sonnet-4.6","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]Responses API
https://sophy.in/v1/responsesUse client.responses.create(...) for a Responses-compatible text, multimodal, streaming, or function-tool workflow. Sophy is stateless: send the full conversation and any prior function calls/results in input on every request.
| Field | Type | Notes |
|---|---|---|
| input | string | array | Required. Must yield at least one usable message or tool item; send the full state for this call. |
| stream | boolean | Optional. Emits numbered, typed Responses SSE events. |
| tools | array | Optional flat function-tool definitions. Non-function Responses tool types are ignored. |
| tool_choice | string | object | Optional. auto, none, required, or one named function. |
| instructions | string | Used only when the key has Agent mode enabled; otherwise dropped. |
| previous_response_id | string | Not supported. Returns 400 stateful_unsupported. |
| store | boolean | Ignored. Sophy does not provide OpenAI-hosted response state and returns store: false. |
Key-owned or ignored: model, temperature, top_p, max_output_tokens, and text.format.
from openai import OpenAI
client = OpenAI(base_url="https://sophy.in/v1", api_key="mw_live_…")
resp = client.responses.create(
model="sophy",
input="Write a haiku about gateways.",
)
print(resp.output_text)curl https://sophy.in/v1/responses \
-H "Authorization: Bearer mw_live_…" \
-H "Content-Type: application/json" \
-d '{"model":"sophy","input":"Write a haiku about gateways."}'{
"id": "resp_…",
"object": "response",
"created_at": 1735689600,
"status": "completed",
"model": "anthropic/claude-sonnet-4.6",
"store": false,
"output": [{
"id": "msg_…",
"type": "message",
"status": "completed",
"role": "assistant",
"content": [{ "type": "output_text", "text": "Silent routes converge…", "annotations": [] }]
}],
"usage": {
"input_tokens": 11,
"input_tokens_details": { "cached_tokens": 0 },
"output_tokens": 9,
"output_tokens_details": { "reasoning_tokens": 0 },
"total_tokens": 20
}
}Streaming uses events such as response.created, response.output_text.delta, response.function_call_arguments.delta, and response.completed, each with an increasing sequence_number. There is no [DONE] sentinel.
Multimodal input
Language keys can send public image/file URLs or URLs returned by POST /v1/files, provided the bound model supports the content type. Sophy ownership-checks its own uploads; public external URLs pass through to the model provider.
uploaded_url = "https://…/uploads/<project>/<key>/invoice-….pdf"
uploaded_filename = "invoice.pdf"
resp = client.chat.completions.create(
model="sophy",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "Summarize this document."},
{
"type": "file",
"file": {
"file_url": uploaded_url,
"filename": uploaded_filename,
},
},
],
}],
)uploaded_url = "https://…/uploads/<project>/<key>/photo-….png"
resp = client.responses.create(
model="sophy",
input=[{
"role": "user",
"content": [
{"type": "input_text", "text": "What is shown here?"},
{"type": "input_image", "image_url": uploaded_url},
],
}],
)Model support still matters
The model bound to the key must accept the media you send. The model catalog in the console exposes capabilities such as image analysis and file input.
Tool / function calling
Sophy passes function definitions to the bound language model and maps its requested calls back to the selected OpenAI wire format. Sophy never executes a tool. Your application runs it, then sends the result and full conversation state on the next call.
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]
first = client.chat.completions.create(
model="sophy",
tools=tools,
messages=[{"role": "user", "content": "Weather in Paris?"}],
)
call = first.choices[0].message.tool_calls[0]
second = client.chat.completions.create(model="sophy", tools=tools, messages=[
{"role": "user", "content": "Weather in Paris?"},
first.choices[0].message,
{"role": "tool", "tool_call_id": call.id, "content": "18°C, sunny"},
])tools = [{
"type": "function",
"name": "get_weather",
"description": "Get weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}]
first = client.responses.create(
model="sophy",
tools=tools,
input="Weather in Paris?",
)
call = next(item for item in first.output if item.type == "function_call")
second = client.responses.create(model="sophy", tools=tools, input=[
{"role": "user", "content": "Weather in Paris?"},
{
"type": "function_call",
"call_id": call.call_id,
"name": call.name,
"arguments": call.arguments,
},
{
"type": "function_call_output",
"call_id": call.call_id,
"output": "18°C, sunny",
},
])Only function tools are supported; other Responses tool types are ignored. Chat uses the nested { type: "function", function: { ... } } shape; Responses uses the flat { type: "function", name, parameters } shape. Legacy Chat functions returns 400 functions_unsupported.
Tools and structured output are mutually exclusive
A key with a JSON Schema rejects tools with 400 tools_unsupported because both features compete for the model’s output channel.
Structured output
An operator can attach a JSON Schema to a language key. Sophy normalizes the schema across supported model providers. Buffered calls use constrained generation, tolerant JSON extraction and retry, then validate the completed object; streaming uses schema-constrained generation with post-hoc validation.
The schema lives on the key. Client-sent Chat response_format and Responses text.format do not replace it. Successful JSON is returned as the assistant text, so parse choices[0].message.content or response.output_text.
Buffered and streaming failure behavior differs
A buffered response that still fails validation returns 502 schema_validation_failed. Streamed bytes cannot be retracted after a 200 begins; Sophy records a validation failure after completion, but the client may already have received non-conforming text.
Audio transcription
https://sophy.in/v1/audio/transcriptionsBind the key’s primary model to a transcription model and send audio as multipart/form-data. Sophy returns raw text when no processing policy is set, or applies centrally managed instructions in a second language-model step and returns only that processed text.
| Field | Type | Notes |
|---|---|---|
| file | File | Required, non-empty, and at most 4 MiB (4,194,304 bytes). |
| model | string | Optional for wire compatibility and ignored. The key’s primary transcription model wins. |
| language | string | Optional two-letter ISO-639-1 language hint, such as en. |
| response_format | json | Optional; defaults to json. Other OpenAI transcription formats are not supported in this version. |
| advanced transcription options | unsupported | Client prompt, temperature, logprobs, stream, timestamps, diarization, include, chunking, and known-speaker fields are rejected with 400 unsupported_transcription_option. |
Accepted audio formats are MP3/MPEG/MPGA, MP4/M4A, WAV, WebM, FLAC, and OGG. Sophy identifies the format from the file bytes instead of trusting its name or client-declared MIME type. Files outside that allowlist return 400 unsupported_audio_format; files over the limit return 413 file_too_large.
from openai import OpenAI
client = OpenAI(base_url="https://sophy.in/v1", api_key="mw_live_…")
with open("meeting.m4a", "rb") as audio:
resp = client.audio.transcriptions.create(
model="sophy",
file=audio,
language="en",
response_format="json",
)
print(resp.text)curl https://sophy.in/v1/audio/transcriptions \
-H "Authorization: Bearer mw_live_…" \
-F "file=@meeting.m4a" \
-F "model=sophy" \
-F "language=en" \
-F "response_format=json"{
"text": "Decisions: launch on Friday and send the checklist today.",
"processed": true,
"language": "en",
"duration": 8.4
}Processing policy controls what the client receives
With a blank key system prompt, text and transcript are identical and processed is false. With a non-blank system prompt, the operator must also set params.transcriptProcessorModel to a language model. Sophy transcribes first, then applies the key-owned instructions to produce text. The raw transcript field is omitted so a client cannot bypass centrally managed processing such as redaction. language and duration appear only when the transcription provider returns them.
Compatibility boundary
This route returns JSON only. Client prompts, temperature, logprobs, streaming, timestamp granularities, diarization, chunking, known-speaker hints, subtitle text, and verbose transcription formats are not supported in this version.
One uploaded clip consumes one RPM slot and counts as one client request. Sophy attributes transcription and processing to their actual models, while the processor component does not add a second client request. If content logging is enabled on the key, Sophy stores the filename, byte count, language hint, raw transcript, and final text—but never the audio bytes. Turn content logging off when unprocessed text must not be retained.
Embeddings
https://sophy.in/v1/embeddingsBind the key to an embedding model and use the standard OpenAI embeddings method. The key-owned model is returned in the response, and usage records input tokens and estimated cost.
| Field | Type | Notes |
|---|---|---|
| input | string | string[] | Required. One non-empty string or 1–2,048 non-empty strings. |
| encoding_format | float | base64 | Optional; defaults to float. Base64 contains little-endian float32 bytes. |
| dimensions | integer | Optional, 1–100,000, and supported only for openai/* embedding models. Provider limits may be narrower. |
| model | string | Ignored. The embedding model bound to the key wins. |
| user | string | Ignored. |
Pre-tokenized integer arrays are not portable across a gateway-bound model and return 400 token_input_unsupported.
from openai import OpenAI
client = OpenAI(base_url="https://sophy.in/v1", api_key="mw_live_…")
resp = client.embeddings.create(
model="sophy",
input=["First document", "Second document"],
encoding_format="float",
)
print(resp.data[0].embedding)curl https://sophy.in/v1/embeddings \
-H "Authorization: Bearer mw_live_…" \
-H "Content-Type: application/json" \
-d '{
"model": "sophy",
"input": ["First document", "Second document"],
"encoding_format": "float"
}'{
"object": "list",
"data": [
{ "object": "embedding", "index": 0, "embedding": [0.012, -0.044, 0.008] },
{ "object": "embedding", "index": 1, "embedding": [-0.031, 0.017, 0.052] }
],
"model": "openai/text-embedding-3-small",
"usage": { "prompt_tokens": 5, "total_tokens": 5 }
}Image generation
https://sophy.in/v1/images/generationsBind the key to a supported image model. Generated images are returned inline as base64 and are not stored by Sophy.
| Field | Type | Notes |
|---|---|---|
| prompt | string | Required and non-empty. |
| n | integer | Optional, from 1 to 10. |
| size | string | Optional WIDTHxHEIGHT syntax with 2–5 digits per side, such as 1024x1024; provider support varies. |
| response_format | b64_json | Optional. b64_json is the only supported format; url is rejected. |
| quality / style | string | Optional provider-specific options. |
| background / output_format | string | Optional provider-specific options; unsupported values may be ignored upstream. |
| model / user | string | Ignored. The image model bound to the key wins. |
from openai import OpenAI
import base64
client = OpenAI(base_url="https://sophy.in/v1", api_key="mw_live_…")
resp = client.images.generate(
model="sophy",
prompt="A red panda coding at a desk, watercolor",
n=1,
size="1024x1024",
)
with open("out.png", "wb") as f:
f.write(base64.b64decode(resp.data[0].b64_json))curl https://sophy.in/v1/images/generations \
-H "Authorization: Bearer mw_live_…" \
-H "Content-Type: application/json" \
-d '{"prompt":"A red panda coding at a desk, watercolor","size":"1024x1024"}'{
"created": 1735689600,
"data": [{ "b64_json": "iVBORw0KGgoAAAANSUhEUgAA…" }]
}Files
https://sophy.in/v1/filesUpload a file as multipart/form-data using the field name file. Success returns 201. Use the returned URL in a Chat file/image part or a Responses input_file/input_image part.
Accepted content types are PDF, PNG, JPEG, WebP, GIF, plain text, CSV, and JSON. The maximum size is 4 MiB (4,194,304 bytes). Uploads become eligible for batched cleanup after 24 hours, so they are request staging—not durable file storage or a precise expiry service.
curl https://sophy.in/v1/files \
-H "Authorization: Bearer mw_live_…" \
-F "file=@./invoice.pdf"{
"id": "https://…/uploads/<project>/<key>/invoice-….pdf",
"url": "https://…/uploads/<project>/<key>/invoice-….pdf",
"pathname": "uploads/<project>/<key>/invoice-….pdf",
"filename": "invoice.pdf",
"bytes": 48213,
"contentType": "application/pdf"
}Upload URLs are public but unguessable so model providers can fetch them; treat each URL as a bearer URL, not a secret store. When a Sophy URL is submitted back through the API, Sophy checks its issuing key, and cross-key reuse returns 403 file_access_denied.
Models
https://sophy.in/v1/modelsReturns exactly the primary model currently bound to the authenticated key in OpenAI list shape. This route does not return the full console catalog or an optional transcript processor, and it does not select a model for a later request.
{
"object": "list",
"data": [
{
"id": "anthropic/claude-sonnet-4.6",
"object": "model",
"created": 0,
"owned_by": "sophy"
}
]
}Key-owned policy
The key is Sophy’s unit of configuration. Operators can change its policy without changing or redeploying the client. The next request reads the latest values.
| Field | Type | Notes |
|---|---|---|
| model | model id | One primary model id. Use API routes supported by its catalog capabilities. |
| system prompt | string | Authoritative prompt for language calls and optional transcript processing. Client prompts are dropped or rejected. |
| transcript processor model | language model id | Optional params.transcriptProcessorModel. Required when a transcription key has a non-blank system prompt. |
| generation params | object | Temperature, top-p, and max output tokens for language calls, including transcript processing. |
| output schema | JSON Schema | Optional structured output policy for language calls. |
| knowledgebase | reference | Optional grounding source for Chat and Responses. |
| RPM / monthly cap | number | Optional request-rate and proxy-traffic cost controls. |
| content logging | boolean | Captures buffered Chat, buffered/streaming Responses, embedding inputs, and text from audio transcription. Audio bytes are never stored. Streaming Chat and image prompts record usage metadata only. |
| owner / status | policy | Console scope, active/revoked views, rotation, and immediate revocation. |
Client values for model and generation parameters are silently ignored. Chat and Responses prompt behavior is controlled separately by Agent mode. Audio transcription rejects a client prompt; only the key-owned system prompt can request post-processing.
When a language or transcript-processing call has an effective system prompt, Sophy prepends a fixed platform security preamble before the key and any applicable Agent-mode client instructions. Eligible multi-turn Anthropic requests also receive an ephemeral prompt-cache breakpoint; cache reads appear in Responses usage as cached_tokens.
Agent mode
Standard keys are governed mode: Sophy drops client system and developer messages plus Responses instructions, then uses the key-owned prompt.
For a trusted server-side agent whose instructions must vary per request, an operator can enable Agent mode on the key. Sophy keeps the key prompt ahead of client instructions (after Sophy’s fixed security preamble), then appends:
- Chat: consecutive leading
system/developermessages, up to the first non-system turn. - Responses:
instructions, followed by leading system/developer input items.
Later system-role items in the conversation are still dropped. Model, generation parameters, schema, knowledge, budgets, and rate limits remain key-owned.
Trust boundary
Enable Agent mode only for server-side applications that construct their own message array. Client instructions become operator-level input; never forward end-user-authored leading system/developer content on an Agent-mode key.
Knowledgebases
Project Admins can create project-scoped knowledgebases from PDF, DOCX, Markdown, plain text, CSV, and JSON documents up to 4 MiB each. Sophy extracts, chunks, and embeds sources in the background, then lets keys in that project attach to the same collection.
On Chat or Responses requests with usable user text, Sophy embeds the latest user text (capped at its first 8,000 characters), retrieves the six closest chunks, and adds them to the effective prompt. Image- or file-only turns have no retrieval query. A transient retrieval timeout can continue without grounding. Missing, invalid, or billing-blocked project Gateway credentials fail closed before the model call; they never fall back to Sophy’s deployment credentials or another project.
Knowledgebase ingestion and query-embedding spend is tracked separately from client proxy traffic in the console.
Operator console
One passwordless identity can belong to multiple projects with an independent Admin or Editor role in each. The project in the URL is the tenant boundary, and every read, mutation, key, knowledgebase, log, evaluation, and audit event stays inside it.
Models
Search and compare the catalog by provider, type, capabilities, context window, and estimated pricing.
Projects and Gateway access
Switch projects, choose a personal default, invite members, rename the project, and connect or rotate its encrypted Vercel Gateway credential.
Sophy keys and knowledge
Create, edit, rotate, revoke, bulk-update, assign project-member ownership, and attach project knowledgebases.
Usage and logs
Filter requests, tokens, estimated cost, latency, model mix, errors, and source-tagged spend.
Champion vs challenger evals
Shadow successful live text requests, blind-judge results, and compare quality, cost, latency, and projected impact.
Evaluation behavior and privacy
Evals observe eligible successful text requests only; requests containing media/file content or supplying tools are skipped. While a run is active, replayable sample content is temporarily captured even when normal content logging is off, then purged when the run ends. Sophy recommends a winner but never switches the key automatically.
Errors and retries
Before a response stream starts, Sophy-generated failures use the OpenAI error envelope below with Cache-Control: no-store. Field validation errors may use code: null and identify the field in param.
{
"error": {
"message": "Invalid API key.",
"type": "authentication_error",
"param": null,
"code": "invalid_api_key"
}
}| Status | Type | Code | When |
|---|---|---|---|
| 400 | invalid_request_error | functions_unsupported | Legacy Chat Completions `functions` was sent; use `tools`. |
| 400 | invalid_request_error | stateful_unsupported | Responses `previous_response_id` was sent; send the full input each call. |
| 400 | invalid_request_error | tools_unsupported | Function tools were sent to a key configured for structured output. |
| 400 | invalid_request_error | invalid_content_type | Audio transcription was not sent as multipart/form-data. |
| 400 | invalid_request_error | invalid_multipart_form | The audio transcription multipart body could not be parsed. |
| 400 | invalid_request_error | missing_file | The transcription multipart body did not contain a `file` field. |
| 400 | invalid_request_error | invalid_file | The transcription `file` field was not a valid uploaded file. |
| 400 | invalid_request_error | empty_file | The uploaded audio file contained no bytes. |
| 400 | invalid_request_error | unsupported_audio_format | The uploaded bytes were not recognized as an allowed audio format. |
| 400 | invalid_request_error | invalid_language | The optional transcription `language` value was invalid. |
| 400 | invalid_request_error | unsupported_transcription_option | A client `prompt`, streaming, or timestamp option was sent. |
| 400 | invalid_request_error | model_not_transcription | The key’s known model cannot handle buffered audio transcription, including realtime-only models. |
| 400 | invalid_request_error | transcript_processor_required | The key has transcript-processing instructions but no processor model. |
| 400 | invalid_request_error | processor_model_not_language | The configured transcript processor is a known non-language model. |
| 400 | invalid_request_error | invalid_url | A Chat or Responses image/file part contains a malformed URL. |
| 400 | invalid_request_error | model_not_image | Image generation was requested with a known non-image key model. |
| 400 | invalid_request_error | unsupported_response_format | Image output requested anything but `b64_json`, or transcription requested anything but `json`. |
| 400 | invalid_request_error | model_not_embedding | Embeddings were requested with a known non-embedding key model. |
| 400 | invalid_request_error | token_input_unsupported | Pre-tokenized embedding input was sent; use strings. |
| 400 | invalid_request_error | unsupported_encoding_format | Embedding `encoding_format` was not `float` or `base64`. |
| 400 | invalid_request_error | dimensions_unsupported | `dimensions` was sent to a non-OpenAI embedding model. |
| 400 | invalid_request_error | unsupported_file_type | The uploaded file content type is outside the allowlist. |
| 400 | invalid_request_error | upstream_invalid_request | The model provider rejected the request as invalid; its actionable detail is returned. |
| 401 | authentication_error | missing_api_key | No valid Bearer Authorization header was supplied. |
| 401 | authentication_error | invalid_api_key | The key is malformed, unknown, revoked, expired, or belongs to an inactive project. |
| 402 | insufficient_quota | quota_exceeded | The key reached its monthly proxy-traffic cost cap. |
| 403 | invalid_request_error | file_access_denied | A request references a Sophy upload owned by another key. |
| 413 | invalid_request_error | file_too_large | A file or audio upload exceeds 4 MiB (4,194,304 bytes). |
| 429 | rate_limit_error | rate_limit_exceeded | The key RPM limit or an upstream provider rate/quota limit was reached. |
| 502 | api_error | schema_validation_failed | A buffered structured response could not satisfy the key schema. |
| 502 | api_error | upload_failed | Sophy could not persist a file upload. |
| 502 | api_error | upstream_error | A transient upstream network or server failure occurred. |
| 503 | api_error | project_gateway_unavailable | The key is valid, but its project has no usable Vercel AI Gateway credential. |
Upstream client-input rejections (400/413/422) become 400 upstream_invalid_request with a safe, capped message. Upstream rate limits preserve 429 and forward Retry-After when present. A valid Sophy key whose project credential is missing, disconnected, invalid, billing-blocked, or undecryptable receives 503 project_gateway_unavailable; Sophy makes no fallback upstream call. Transient network and server failures are hidden behind 502 upstream_error.
Errors after streaming starts
Once an SSE response has begun with 200, a later model failure cannot be replaced by a JSON error envelope. Chat still attempts a final [DONE] but may lack its normal finish/usage chunks; Responses may omit response.completed.
Rate limits and monthly quota
Chat, Responses, audio transcription, embeddings, and image generation enforce the key’s optional requests-per-minute limit and monthly cost cap before the paid model call. /files and /models do not consume those request counters.
- RPM uses a fixed 60-second window. Exceeding it returns
429 rate_limit_exceededwithRetry-Afterin seconds. - Monthly quota uses the current UTC calendar month and counts client-attributable spend, including transcript processing. Reaching it returns
402 quota_exceeded. - Evaluation and knowledgebase spend is recorded under separate sources for visibility and does not consume a key’s proxy-traffic cap.
Console cost values are gateway estimates for operational decisions, not a billing-grade ledger.
Need a key or a policy change? Sign in to the Sophy console.