Docs menu

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 speaksBase URL
OpenAI Chat Completionshttps://sator-api.princep.org/v1
Anthropic Messageshttps://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 at https://sator.princep.org/dashboard. Never invent one.
  • The key is accepted in either header on either endpoint: Authorization: Bearer <key> or x-api-key: <key>.
  • GET https://sator-api.princep.org/v1/models needs no key and returns the exact list of valid model ids.
  • POST /v1/messages requires max_tokens (positive integer) on every request. POST /v1/chat/completions does not.
  • POST /v1/responses is served stateless: never send previous_response_id; send the full input each turn. Not served: /v1/embeddings (answers 404). /v1/messages/count_tokens is served (an estimate, never billed).

2. Choose the base URL#

  1. Determine which wire the client speaks.
  2. If it is an OpenAI-compatible client (OpenAI SDK, openai_compatible settings, "OpenAI base URL" field, OPENAI_BASE_URL, LangChain ChatOpenAI, @ai-sdk/openai-compatible), use https://sator-api.princep.org/v1.
  3. If it is an Anthropic-compatible client (Anthropic SDK, Claude Code, ANTHROPIC_BASE_URL, LangChain ChatAnthropic, Zed anthropic_compatible), use https://sator-api.princep.org with no path.
  4. Exception: the Vercel AI SDK's @ai-sdk/anthropic provider takes https://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 detectedFile / placeExact keys and values
OpenCode (opencode.json)~/.config/opencode/opencode.json or project opencode.jsonprovider.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.jsonenv, or shell envANTHROPIC_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
ClineExtension 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 CodeExtension settings (GUI)Same fields as Cline. Native tool calling is required and provided. Page
Continue (.continue/)~/.continue/config.yamlmodels[] entry with provider: openai, model: <id>, apiBase: https://sator-api.princep.org/v1, apiKey: <key>, roles: [chat, edit]. Page
Aider (.aider*)EnvironmentOPENAI_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.jsonlanguage_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
CursorSettings → 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.tomlmodel = "<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 envbase_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 envbase_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)CodecreateOpenAICompatible({ 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
LangChainCodePython 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#

VariableRead byHeader it becomes
OPENAI_BASE_URLOpenAI SDKs— (base URL, with /v1)
OPENAI_API_KEYOpenAI SDKs, AiderAuthorization: Bearer
OPENAI_API_BASEAider only— (base URL, with /v1)
ANTHROPIC_BASE_URLAnthropic SDKs, Claude Code— (base URL, bare host)
ANTHROPIC_AUTH_TOKENAnthropic SDKs, Claude CodeAuthorization: Bearer
ANTHROPIC_API_KEYAnthropic SDKs, Claude Code, Aiderx-api-key
ANTHROPIC_API_BASEAider (via LiteLLM)— (base URL, bare host)
SATOR_API_KEYNothing 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:

bash
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:

json
{"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:

bash
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]:

json
{"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:

StatusBody code (OpenAI wire)MeaningAction
401invalid_api_keyKey missing, wrong or revokedAsk the user for a valid key
402insufficient_creditsBalance is $0Ask the user to add credit at https://sator.princep.org/dashboard
404model_not_foundNot a catalog idUse an id from GET /v1/models
404unknown_urlWrong path — usually a missing or extra /v1Fix the base URL per section 2
400invalid_body with param: "max_tokens"Anthropic wire without max_tokensAdd max_tokens
429rate_limit_exceededOver 300 requests/minuteWait retry-after seconds

6. Do not#

  • Do not append /v1 to the Anthropic base URL — except for the Vercel AI SDK's @ai-sdk/anthropic.
  • Do not omit /v1 from the OpenAI base URL.
  • Do not configure /v1/embeddings; it is not served. Do not send previous_response_id to /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 /model picker to show Sator models; set ANTHROPIC_MODEL or the ANTHROPIC_DEFAULT_*_MODEL variables.
  • 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_BASE with the OpenAI SDK (it reads OPENAI_BASE_URL); do not use OPENAI_BASE_URL with Aider (it reads OPENAI_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.2
  • glm-5.1
  • kimi-k2.7-code
  • kimi-k2.6
  • mimo-v2.5
  • minimax-m3
  • minimax-m2.7
  • qwen3.7-max
  • qwen3.7-plus
  • qwen3.6-plus
  • hy3
  • longcat-2.0
  • muse-spark-1.2
  • deepseek-v4-flash
  • glm-5.3
  • grok-4.6
  • gpt-5.6-luna
  • kimi-k3
  • mimo-v2.5-pro
  • qwen3.8-max
  • deepseek-v4-pro
  • deepseek-v4-flash-vision-exp

Context windows and max output per id: Models (https://sator.princep.org/docs/models.md).