FAQ
Can I use my console login as an API key?
No. Console JWTs are accepted only on /api/*. Clients must use an API key (sk-…) on /v1/* and /servers/*. The reverse is also true: API keys are rejected on /api/*.
My key is lost — can I recover it?
No. Keys are bcrypt-hashed and shown once at creation. Create a replacement, switch clients, delete the old key. There is no rotate endpoint, but rotation without downtime is simply: create new → switch → delete old.
Can one model name map to several providers?
Yes — that is the point of provider mappings. Give each mapping a priority; higher priority is preferred, and failures fall back down the chain. See Models & Pricing.
Why does cost_usd show as null?
Null means unknown, not free: no rate is configured for the model/mapping, or the request had nothing billable. Configure pricing (manually or via the models.dev sync) to get real numbers. See Cost Control.
Why did I get HTTP 200 with an error JSON body?
The non-streaming keepalive had already committed a 200 before the failure surfaced. The real error is in the logs; the body contains the error JSON. This only happens for non-streaming requests that outlive the grace period (60 s by default).
A request returned 403 model_not_allowed. Where do I fix it?
Two allowlists exist: the user's and the key's, and the effective access is their intersection. Check Users & Roles and API Keys. An explicitly empty allowlist denies all models.
Does the proxy support vision / tools / reasoning / JSON mode?
Yes for all four, provided the selected upstream supports them — the proxy translates between protocols. Capability flags on the model record drive routing and the catalog; they do not block non-chat endpoints. See Create chat completion and Tools & Reasoning.
How are files and media mapped when I route to another provider?
The proxy renders each part in whatever form the upstream accepts, so the same input_image / input_file / input_audio part may arrive as inline bytes, a remote URI, or an uploaded-file reference. Worth knowing:
file_idis passed through as-is. There is no/v1/filesstore, so an id must have been issued by the upstream it is sent to. An OpenAI Files-API id sent to Gemini (or the reverse) fails upstream.- HTTP(S) image URIs are sent as native URL sources where the upstream has one (Anthropic
imageURL sources, Geminifile_data.file_uri, OpenAI / OpenRouterimage_url); where it does not, the URL is downloaded and inlined before forwarding, so any publicly reachable URL works. SSRF protection applies to the download path. - Document URLs on Chat Completions are provider-specific. Chat Completions has no single
fileshape and not every upstream accepts a URL: OpenRouter carries the URL infile.file_data, Zhipu / Z.AI (GLM) infile.file_url, and Mistral in a flatdocument_url. OpenAI and the genericopenai-compatibleadapter accept no file URL, so a URL document routed there is downloaded and inlined as base64 (subject to the same SSRF protection) when the upstream accepts the media type; a PDF becomes afilepart, while a non-PDF still degrades because OpenAI'sfilepart is PDF-only. DeepSeek'sfilepart carries images and Files-API ids only, so a document routed there degrades. OpenRouter, Zhipu and Mistral accept the document types they document. - Zhipu / Z.AI (GLM) cannot mix a
filepart with an image or video part. A message carrying both is split into separate messages (fileon one,image_url/video_urlon the other) so the upstream does not reject the request; text parts stay with the neighbouring part and ordering is preserved. - Video URIs are not downloaded. Gemini fetches video itself and reliably accepts only YouTube URLs (or File API /
gs://URIs); an arbitraryhttps://…/v.mp4is rejected upstream. - Anthropic has no
filecontent block. A Responsesinput_fileis mapped onto the block Anthropic actually accepts:application/pdf(base64 or URL) and plain text (.txt/.md/.csv/ … decoded into atextsource) becomedocumentblocks, image payloads becomeimageblocks, andfile_idbecomes adocument/imagefilesource. Media types Anthropic cannot represent (.docx,.xlsx, …) follow theunsupported_block_policy. - Anthropic has no audio content block. Audio routed there degrades to an
[Audio: …]text placeholder (never anaudioblock the API rejects). - Audio has no OpenAI Responses content type. The Responses API accepts message content of
input_text/input_image/input_fileonly, so audio routed to the built-inopenaiprovider (which targets/responses) degrades to an[Audio: …]text placeholder instead of being sent as aninput_audiopart the API rejects. Audio passes through verbatim on the native Responses passthrough path, and Chat Completions upstreams keep theirinput_audiosupport. - Multimodal tool results stay structured where the upstream supports it (Responses
function_call_output.output, Gemini 3functionResponse.parts); elsewhere the media is degraded to a text placeholder rather than dropped. - A block the target provider cannot represent at all follows the
unsupported_block_policy; the default Provider default resolves todropfor every provider today, whiledegradekeeps a text placeholder ([Image: …],[File: …]) and never silently drops — a block with no specific placeholder gets a generic[<TypeName> block]marker.
Can I export logs to my own system?
There is no export endpoint in v0.2.3; use GET /api/logs with cursor pagination (max 200 per page) or scrape container logs. Langfuse tracing (per user) covers LLM-level observability.
Is there an OpenAPI/Swagger page?
Not at runtime — the FastAPI docs (/docs, /redoc, /openapi.json) stay disabled. A generated OpenAPI 3.1 document covering the /v1 surface is published in this site instead: OpenAPI Specification. It is rendered from docs/public/v1-openapi.json, which uv run llm-proxy-openapi regenerates from the app itself, so the request schemas match what the server validates. Together with the Endpoint Index, that is the reference.
Can I run multiple replicas?
Yes, with PostgreSQL and REDIS_ENABLED=true + REDIS_RATE_LIMIT_ENABLED=true. Without Redis, rate limits, circuit breaker state, and provider statistics are per-process. Migrations run at startup per process — migrate once before scaling. See PostgreSQL & Redis.
Is response keepalive on by default?
Yes. With no stored configuration the runtime uses enabled / 60 s grace / 15 s interval, and the Settings form shows those same values, so the panel reflects what is actually in effect. Turn it off there if a client cannot tolerate the whitespace heartbeats.
Does the proxy inject MCP tools into model requests?
No. MCP servers are re-exposed as MCP endpoints at /servers/<name>/mcp for MCP clients; they are not spliced into /v1/* model requests. See MCP Servers.
What happens to a request when the client disconnects?
The upstream call is cancelled and the request is logged as 499 — visible in the logs instead of silently succeeding. Budgets count what was actually used at that point.
I'm locked out of the console. How do I get back in?
- Login lockout is off by default, so a wrong password normally just returns 401. If you enabled it and are locked out, wait out the 15-minute window — or restart the proxy, since the lockout counters live in process memory.
- Another admin can reset your password from Team (which forces a change at next sign-in).
- If you are the last admin with a forgotten password: there is no CLI recovery command — edit the database directly. Passwords are stored as bcrypt hashes in the
users.password_hashcolumn, so replacing the hash is the supported-by-hand route.
Which languages does the console support?
English and 中文, switchable in Settings → General → Preference (browser-local, per user).
Where do feature requests and bugs go?
GitHub issues — see the triage labels in the repository for how issues are handled.