Agent setup
This page is written for an AI agent configuring a tool or codebase to use Princeps Sator. Follow it literally. Do not infer anything not stated here; where a value is needed that this page does not give, fetch it from the URL named.
1. Facts#
- Production API host:
https://sator-api.princep.org - Two endpoints, two base URLs:
| If your tool speaks | Base URL |
|---|---|
| OpenAI Chat Completions | https://sator-api.princep.org/v1 |
| Anthropic Messages | https://sator-api.princep.org (no /v1 — the SDK appends it) |
- Every model id works on both endpoints.
- The API key looks like
sk-sator-v1-followed by 24 alphanumeric characters. The user must supply it; it is created athttps://sator.princep.org/dashboard. Never invent one. - The key is accepted in either header on either endpoint:
Authorization: Bearer <key>orx-api-key: <key>. GET https://sator-api.princep.org/v1/modelsneeds no key and returns the exact list of valid model ids.POST /v1/messagesrequiresmax_tokens(positive integer) on every request.POST /v1/chat/completionsdoes not.POST /v1/responsesis served stateless: never sendprevious_response_id; send the fullinputeach turn. Not served:/v1/embeddings(answers404)./v1/messages/count_tokensis served (an estimate, never billed).
2. Choose the base URL#
- Determine which wire the client speaks.
- If it is an OpenAI-compatible client (OpenAI SDK,
openai_compatiblesettings, "OpenAI base URL" field,OPENAI_BASE_URL, LangChainChatOpenAI,@ai-sdk/openai-compatible), usehttps://sator-api.princep.org/v1. - If it is an Anthropic-compatible client (Anthropic SDK, Claude Code,
ANTHROPIC_BASE_URL, LangChainChatAnthropic, Zedanthropic_compatible), usehttps://sator-api.princep.orgwith no path. - Exception: the Vercel AI SDK's
@ai-sdk/anthropicprovider takeshttps://sator-api.princep.org/v1.
3. Decision table by harness#
Identify the harness from the repository or the user's request, then apply exactly the row that matches. Full pages are linked; the essentials are in the row.
| Harness detected | File / place | Exact keys and values |
|---|---|---|
OpenCode (opencode.json) | ~/.config/opencode/opencode.json or project opencode.json | provider.sator.npm = "@ai-sdk/openai-compatible", provider.sator.options.baseURL = "https://sator-api.princep.org/v1", provider.sator.options.apiKey = "{env:SATOR_API_KEY}", provider.sator.models.<id>.limit = {context, output} from the model table, model = "sator/<id>". Page |
Claude Code (.claude/, CLAUDE.md, claude CLI) | ~/.claude/settings.json → env, or shell env | ANTHROPIC_BASE_URL = "https://sator-api.princep.org", ANTHROPIC_AUTH_TOKEN = <key>, ANTHROPIC_MODEL = <id>, ANTHROPIC_DEFAULT_OPUS_MODEL = <id>, ANTHROPIC_DEFAULT_SONNET_MODEL = <id>, ANTHROPIC_DEFAULT_HAIKU_MODEL = <id> (e.g. deepseek-v4-flash for all four), CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "1". A model variable is mandatory: the picker does not list Sator ids. Page |
| Cline | Extension settings (GUI) | API Provider OpenAI Compatible; Base URL https://sator-api.princep.org/v1; API Key; Model ID <id>; Context Window Size and Max Output Tokens from the model table. Page |
| Roo Code | Extension settings (GUI) | Same fields as Cline. Native tool calling is required and provided. Page |
Continue (.continue/) | ~/.continue/config.yaml | models[] entry with provider: openai, model: <id>, apiBase: https://sator-api.princep.org/v1, apiKey: <key>, roles: [chat, edit]. Page |
Aider (.aider*) | Environment | OPENAI_API_BASE = https://sator-api.princep.org/v1, OPENAI_API_KEY = <key>, run aider --model openai/<id>. Page |
Zed (.zed/, settings.json with language_models) | Zed settings.json | language_models.openai_compatible.sator.api_url = "https://sator-api.princep.org/v1" with available_models[] (name, max_tokens, max_output_tokens, capabilities); key via env SATOR_API_KEY, never in the file. Anthropic form: language_models.anthropic_compatible.Sator.api_url = "https://sator-api.princep.org". Page |
| Cursor | Settings → Models (GUI) | OpenAI API key = <key>; Override OpenAI Base URL = https://sator-api.princep.org/v1; add custom model <id>. Reported, not verified. Page |
Codex CLI (.codex/, config.toml with wire_api) | ~/.codex/config.toml | model = "<id>", model_provider = "sator", and [model_providers.sator] with name = "Sator", base_url = "https://sator-api.princep.org/v1", env_key = "SATOR_API_KEY", wire_api = "responses"; key via env SATOR_API_KEY. Page |
OpenAI SDK (openai package) | Code or env | base_url / baseURL = https://sator-api.princep.org/v1, api_key / apiKey = <key>; or env OPENAI_BASE_URL (not OPENAI_API_BASE) and OPENAI_API_KEY. Page |
Anthropic SDK (anthropic / @anthropic-ai/sdk) | Code or env | base_url / baseURL = https://sator-api.princep.org, auth_token / authToken = <key>; or env ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN. Page |
Vercel AI SDK (ai package) | Code | createOpenAICompatible({ name: 'sator', baseURL: 'https://sator-api.princep.org/v1', apiKey, includeUsage: true }); or createAnthropic({ baseURL: 'https://sator-api.princep.org/v1', apiKey }) — /v1 on both. Page |
| LangChain | Code | Python ChatOpenAI(model, base_url="https://sator-api.princep.org/v1", api_key); JS new ChatOpenAI({ model, apiKey, configuration: { baseURL } }); Python ChatAnthropic(model, base_url="https://sator-api.princep.org", api_key, max_tokens); JS new ChatAnthropic({ model, apiKey, anthropicApiUrl, maxTokens }). Page |
4. Environment variable names#
| Variable | Read by | Header it becomes |
|---|---|---|
OPENAI_BASE_URL | OpenAI SDKs | — (base URL, with /v1) |
OPENAI_API_KEY | OpenAI SDKs, Aider | Authorization: Bearer |
OPENAI_API_BASE | Aider only | — (base URL, with /v1) |
ANTHROPIC_BASE_URL | Anthropic SDKs, Claude Code | — (base URL, bare host) |
ANTHROPIC_AUTH_TOKEN | Anthropic SDKs, Claude Code | Authorization: Bearer |
ANTHROPIC_API_KEY | Anthropic SDKs, Claude Code, Aider | x-api-key |
ANTHROPIC_API_BASE | Aider (via LiteLLM) | — (base URL, bare host) |
SATOR_API_KEY | Nothing built in; the conventional name for the key in your own shell, {env:…} references and Zed's sator provider | — |
5. Verify#
Run exactly this, with the user's key in SATOR_API_KEY:
curl -s https://sator-api.princep.org/v1/chat/completions \
-H "Authorization: Bearer $SATOR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"Reply with exactly: ok"}],"max_tokens":8}'Expected: HTTP 200 and a JSON object of this shape. The id prefix varies by model (router-…, chatcmpl-…); prompt_tokens includes chat-template overhead and is far above the word count; a reasoning model adds reasoning_content beside content and may hit max_tokens while reasoning — judge success by object and by choices[0].message, never by the token counts or the exact wording:
{"id":"router-841ccc4fe2eb6c57ce12fa44e2709764","object":"chat.completion","created":1787596599,"model":"deepseek-v4-flash","choices":[{"index":0,"finish_reason":"stop","message":{"role":"assistant","content":"ok","reasoning_content":"…"}}],"usage":{"prompt_tokens":88,"completion_tokens":12,"total_tokens":100}}If the harness uses the Anthropic wire, verify with:
curl -s https://sator-api.princep.org/v1/messages \
-H "x-api-key: $SATOR_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek-v4-flash","max_tokens":8,"messages":[{"role":"user","content":"Reply with exactly: ok"}]}'Expected: HTTP 200 and a JSON object of this shape. A thinking block precedes the text block only when the request enabled thinking — find the block with type: "text" rather than reading content[0]:
{"id":"msg_ca0525a31544409394c9c37b48200d05","type":"message","role":"assistant","model":"deepseek-v4-flash","content":[{"type":"text","text":"ok"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":88,"output_tokens":12,"cache_read_input_tokens":0,"cache_creation_input_tokens":0}}Interpret failures by status:
| Status | Body code (OpenAI wire) | Meaning | Action |
|---|---|---|---|
| 401 | invalid_api_key | Key missing, wrong or revoked | Ask the user for a valid key |
| 402 | insufficient_credits | Balance is $0 | Ask the user to add credit at https://sator.princep.org/dashboard |
| 404 | model_not_found | Not a catalog id | Use an id from GET /v1/models |
| 404 | unknown_url | Wrong path — usually a missing or extra /v1 | Fix the base URL per section 2 |
| 400 | invalid_body with param: "max_tokens" | Anthropic wire without max_tokens | Add max_tokens |
| 429 | rate_limit_exceeded | Over 300 requests/minute | Wait retry-after seconds |
6. Do not#
- Do not append
/v1to the Anthropic base URL — except for the Vercel AI SDK's@ai-sdk/anthropic. - Do not omit
/v1from the OpenAI base URL. - Do not configure
/v1/embeddings; it is not served. Do not sendprevious_response_idto/v1/responses; it is stateless. - Do not invent a model id. Use one from the list below or from
GET /v1/models. - Do not rely on Claude Code's
/modelpicker to show Sator models; setANTHROPIC_MODELor theANTHROPIC_DEFAULT_*_MODELvariables. - Do not put the key into a file the user has said to keep secret-free; prefer environment variables and
{env:…}references where the harness supports them. - Do not use
OPENAI_API_BASEwith the OpenAI SDK (it readsOPENAI_BASE_URL); do not useOPENAI_BASE_URLwith Aider (it readsOPENAI_API_BASE). - Do not trust a client's own cost display; the charged amount is at
https://sator.princep.org/dashboard.
7. Model ids#
Generated from the catalog at build time. GET https://sator-api.princep.org/v1/models returns the same list live.
glm-5.2glm-5.1kimi-k2.7-codekimi-k2.6mimo-v2.5minimax-m3minimax-m2.7qwen3.7-maxqwen3.7-plusqwen3.6-plushy3longcat-2.0muse-spark-1.2deepseek-v4-flashglm-5.3grok-4.6gpt-5.6-lunakimi-k3mimo-v2.5-proqwen3.8-maxdeepseek-v4-prodeepseek-v4-flash-vision-exp
Context windows and max output per id: Models (https://sator.princep.org/docs/models.md).