Follow along: pick your OS and your tool
Every step below is copy and paste. One-click setup isn't ready yet, so for now you edit one config by hand — in order, under ten minutes.
Anthropic's command-line coding assistant. No Claude account needed — once configured it runs on your quota here, with Prism picking the model.
Step 1:Before you start: get a key
Sign in to the console, pick a plan, then click New on the Keys page. The key is shown only once — copy it right away and paste it into the box above. Then open PowerShell: press the Windows key, type
PowerShell, press Enter. Paste every command below into that window (right-click pastes) and press Enter.Step 2:Install Claude Code
Already installed? Skip this. Otherwise copy the line below, paste it and press Enter, then wait for it to finish.
PowerShell irm https://claude.ai/install.ps1 | iex
Success looks like:Type
claude --versionand press Enter; you see a version number. If it says the command isn't found, close the window and open a new one — newly installed programs only show up in new windows.Step 3:Write the config
Copy the whole block (the Copy button, top right), paste it and press Enter. It writes
%USERPROFILE%\.claude\settings.json; if that file already exists, the old one is backed up first with a.baksuffix.autohands model choice to Prism, by task difficulty. The last line turns off a few Claude Code experiments that other vendors' models reject with a 400.PowerShell This block contains a key placeholder. Paste your key at the top of the page before copying, or replace the placeholder yourself afterwards.
New-Item -ItemType Directory -Force "$HOME\.claude" | Out-Null if (Test-Path "$HOME\.claude\settings.json") { Copy-Item "$HOME\.claude\settings.json" "$HOME\.claude\settings.json.bak" -Force } [IO.File]::WriteAllText("$HOME\.claude\settings.json", @' { "env": { "ANTHROPIC_BASE_URL": "https://agiplan.dev", "ANTHROPIC_AUTH_TOKEN": "ap_live_replace_with_your_key", "ANTHROPIC_MODEL": "auto", "ANTHROPIC_DEFAULT_OPUS_MODEL": "auto", "ANTHROPIC_DEFAULT_SONNET_MODEL": "auto", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "auto", "CLAUDE_CODE_SUBAGENT_MODEL": "auto", "CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS": "1" } } '@)Rather not use a command? Create the file by hand
Run the first block; it opens
%USERPROFILE%\.claude\settings.jsonin Notepad (click Yes when it asks to create the file). Paste the second block into Notepad and save withCtrl + S. Don't use Save As — Notepad will quietly add.txt.PowerShell New-Item -ItemType Directory -Force "$HOME\.claude" | Out-Null notepad "$HOME\.claude\settings.json"
%USERPROFILE%\.claude\settings.json This block contains a key placeholder. Paste your key at the top of the page before copying, or replace the placeholder yourself afterwards.
{ "env": { "ANTHROPIC_BASE_URL": "https://agiplan.dev", "ANTHROPIC_AUTH_TOKEN": "ap_live_replace_with_your_key", "ANTHROPIC_MODEL": "auto", "ANTHROPIC_DEFAULT_OPUS_MODEL": "auto", "ANTHROPIC_DEFAULT_SONNET_MODEL": "auto", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "auto", "CLAUDE_CODE_SUBAGENT_MODEL": "auto", "CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS": "1" } }Step 4:Start it and say something
Go to your project folder first (e.g.
cd path/to/project), then typeclaudeand press Enter. The first time it asks whether you trust the folder — say yes. Then say anything.PowerShell claude
Getting an error? First check the key and the network
Paste this line and press Enter. A list of models with
"id"fields means the key and network are fine and the tool's config is off — go back and paste the previous step again.401means the key is wrong or deleted; nothing at all or a connection error means a network problem.PowerShell This block contains a key placeholder. Paste your key at the top of the page before copying, or replace the placeholder yourself afterwards.
curl.exe -s https://agiplan.dev/v1/models -H "Authorization: Bearer ap_live_replace_with_your_key"
Success looks like:It answers normally. Type
/statusand theAnthropic base URLline shows our address — so it's running on us, not on an official account.
If it doesn't work
- Asked to choose a sign-in method on launch? Run
claude updatefirst — older versions ignore the config file and stop at the first-run welcome screen. If it still happens after updating, the config file isn't where it should be; rerun the whole Write the config block. - 401: the key is mistyped or you deleted it in the console. Paste the key into the box above again and rerun the whole Write the config block.
- 404: you edited the address by hand and it now ends in
/v1. Claude Code's address has no/v1. - Using the Claude Code extension in VS Code? The extension doesn't take its sign-in from this file. Add
claudeCode.environmentVariablesto your VS Code settings (JSON) with theANTHROPIC_BASE_URLandANTHROPIC_AUTH_TOKENentries from the config above. - Set
ANTHROPIC_*variables in.bashrc/.zshrcor system environment variables before? Remove them so they don't fight with this file. - Don't name the model
xxx[1m]to unlock a 1M context. Contexts beyond 400K are far more likely to be throttled or cut off upstream, and cost much more; letting it auto-compact around 200K is steadier for long tasks.
Followed every step and it still fails? Open a ticket under Support in the console with your OS, your tool and the exact error text (never the key). A person answers.
Writing your own code: two things
The guide above is for ready-made tools. For your own code or an official SDK you need exactly two things, the endpoint and the key; write the rest the way your SDK expects.
# OpenAI-style SDKs take this (with /v1) https://agiplan.dev/v1 # Anthropic-style SDKs take this (no /v1 — the SDK appends it) https://agiplan.dev
Check both with one curl first. A list of models means it works; 401 means the key is wrong; a connection error means the address is mistyped.
curl -s https://agiplan.dev/v1/models -H "Authorization: Bearer ap_live_your_key"
About the /setup script. The home page used to recommend curl …/setup | sh. All it does is write those two values to ~/.config/agiplan/env (mode 600), plus OPENAI_BASE_URL / OPENAI_API_KEY in the same file — once sourced, anything that reads those names routes through us. And Claude Code, Codex and OpenCode don't read that file, so you still had to edit each tool's config afterwards. We no longer recommend it; for tools, follow the guide above. The script is still there (open https://agiplan.dev/setup in a browser to read all of it) if you want it to manage env vars for your own code.
Authentication
Every /v1/* endpoint takes a bearer token; /v1/messages also accepts the x-api-key header the Anthropic SDK sends, but every other endpoint accepts only Authorization: Bearer. Keys are created in the console and the plaintext is shown exactly once — we store only a SHA-256 hash, so a key can't be recovered, only rotated.
Authorization: Bearer ap_live_xxxxxxxxxxxx
Keys can be scoped, bound to a custom model, IP-restricted and given an expiry. When rotating, the old key can keep a grace period rather than being cut off instantly.
Each key also has a routing preference (chosen at creation, changeable any time): Auto (default, balances per request type), Speed (fastest first token, unit price may be slightly higher), Cost (slower responses for a lower unit price — when a model is served by several routes, CU is billed at the rate of the route that actually served you), or Cache (keep one route per conversation to reduce cache rewrites). The preference only affects how routes are chosen; failover and circuit breaking behave the same.
Signing in to the console is separate from calling the API: the API only takes keys, while the console uses an emailed code — or a passkey (fingerprint, face or phone unlock) you add under Account & security, after which tapping the email field on the sign-in page is enough. Email codes always keep working; adding or removing a passkey asks you to confirm it's you first, and every change sends you a notification email.
Which protocols
The same base URL serves compatible endpoints for three mainstream protocols. Which one you use depends on your SDK — you don't have to tell us.
| Endpoint | Protocol | Notes |
|---|---|---|
| /v1/chat/completions | OpenAI-style | stream, tools, response_format |
| /v1/messages | Anthropic-style | stream, tools, system |
| /v1/responses | OpenAI Responses | stream, tools, reasoning; Codex's custom_tool_call / compaction / encrypted reasoning pass through untouched; previous_response_id avoids resending context. Naming Claude in Codex runs Claude: apply_patch and namespace tools are converted automatically; OpenAI-hosted tools such as web search aren't available on Claude. Only when the history holds items Claude can't take (compaction compacted history, web_search_call search results, local_shell_call) does it switch to an equally capable GPT model, and x-agiplan-model-actual plus the call record say which item triggered it |
| /v1/models | Any | Returns the names you can put in model right now: auto, auto-fast, every model you can call by name, and the image, video and audio models. Your own custom models are not listed; see “Custom models” in the console |
| /v1/quota | Any | Read the three windows without making a call; needs usage:read |
| /v1/images/{generations,edits} | OpenAI-style | Generate / edit images, billed per image; see Advanced |
| /v1/videos | Async jobs | Submit a video job, poll, fetch the clip; billed per second; see Advanced |
| /v1/assets | AGI Plan extension | Asset library: upload reference images and audio once, reuse them; see Advanced |
| /v1/media/{id} | AGI Plan extension | Links to generated results; no key needed, kept 7 days; see Advanced |
Any tool that lets you change its endpoint works directly, with no plugin and no code changes. Differences between the request formats are handled inside the gateway, so use whichever protocol you already know.
/v1/responses is the only stateful one: on every call (with or without previous_response_id), that turn's input and output are stored on our side for at most 5 hours so your next turn can continue with previous_response_id; a scheduled job deletes them when they expire. If you don't want this storage, use /v1/chat/completions or /v1/messages instead. The privacy policy states the same.
A few endpoints are deliberately not in the table: /v1/device/code, /v1/device/token and /v1/keys/current are the CLI's transport (used by agiplan login and agiplan use), not something you call directly. /v1/traces/{id} is covered under tracing below.
Images work as inline data: URLs (base64) only — remote http(s) image links are not built yet. A remote link is currently replaced with a text placeholder. When that happens the response carries x-agiplan-dropped-params: content[].image_url, so you won't assume the model saw the image. Audio and file parts behave the same way — currently dropped, and listed in the same header.
For the fine print (request and response headers, strict output caps, capability flags, the full image and video APIs, exact billing) see Advanced usage & API reference.
Name a model, or hand it to Prism
One model field, several ways to use it: a model's real name pins that model — it fails over among that model's own lines, but never switches to a different model; a capability tier name routes and fails over among that tier's equivalent models; auto or your own custom model name hands it to Prism, which routes by task difficulty; auto-fast is speed first, then cost — same rules and failover as auto, which first sets the minimum tier the task needs, then picks the fastest tier at or above it right now (time to first token plus output speed), taking the cheaper one when two are within 20% of each other. You pay the chosen tier's price.
"model": "glm-5.3" // real model name: pinned (fails over among its own lines), Prism stays out "model": "domestic" // tier name: routed among that tier's equivalent models, with failover "model": "auto" // handed to Prism "model": "auto-fast" // speed first, then cost (never below auto's tier) "model": "my-coding-model" // your own custom model
glm-5.3 above is only an example: the real model names you can use are whatever GET /v1/models returns (the console's “Models & pricing” shows the same list). The tier names are frontier, balanced, domestic and fast. Names like frontier-a or domestic-a seen in older docs are aliases for a tier — they behave exactly like the tier name, do not pin a single model, and are not listed by /v1/models.
To override the difficulty judgement on a single request, add a header: X-AGIPlan-Difficulty: 0.9. Tier coefficients are listed at /models.
Streaming
All three protocols stream SSE in their own native format, behaving exactly as you'd expect. The one thing to know: billing settles when the stream ends.
On arrival we reserve quota: the input side from the tokens already countable in the request, the output side from the max_tokens you declared, capped at 16k. A normal finish, a client abort and a model-service error all run the same teardown, reconciling against actual usage and returning the difference, so disconnecting midway never bills you the worst case. Output beyond the reservation is billed as used, so a window can go slightly over its limit: all in-flight requests together can exceed the 5-hour window by at most 10%. To guarantee that, when max_tokens is above 16k the cap sent to the model may be lowered. Once over, the window shows 0 remaining and new requests wait for the reset. When a window is nearly used up and what is left cannot cover 16k of output, the request still goes through as long as it covers the input plus 4k of output: output is reserved from what remains and the cap sent to the model is lowered to match. Only below that do you get a 429.
Reading the quota response
Every response from the three text-protocol endpoints carries the remaining percentage and reset time for all three windows (image and video endpoints don't; see Advanced). Absolute usage is in the console usage detail and the monthly statement. On rate limiting you get a 429 that says which window is full and when it recovers — the 429 carries the same three headers. An invalid key (401) carries none of them, because the account can't be identified at that point.
X-AGIPlan-Quota-Session: remaining=68%; resets=2026-08-15T09:00:00.000Z X-AGIPlan-Quota-Week: remaining=34%; resets=2026-08-18T00:00:00.000Z X-AGIPlan-Quota-Month: remaining=81%; resets=2026-09-05T00:00:00.000Z Retry-After: 1740 // seconds, only on 429: time to the window reset, or 2 when concurrency is full
The size of the three windows follows your plan. You change plans in the console's Plans panel, with two choices of timing for an upgrade: now, where the difference for the remaining days is charged and the new quota applies immediately; or from next period, where this period's quota and price stay as they are, nothing is charged or refunded, and the subscription renews as the plan you picked at its price on that day, with the new quota starting then. Downgrades are from next period only: you keep your current plan for this period and nothing is refunded. The billing page shows which plan renews next and how much it will charge.
Errors and retries
| Status | Error code | Meaning | What to do |
|---|---|---|---|
| 401 | invalid_api_key | Key invalid or revoked | Use a different key; do not retry |
| 404 | model_not_found | Images endpoint: no such image model | See the entries with kind image in /v1/models |
| 410 | model_retired | The model you named has been retired. The message gives the retirement date and suggested replacements | Retrying will not help. Change model to the replacement in the message (for example auto-fast); retired models are also listed at the bottom of Models & prices in the console |
| 503 | size_unavailable | Images endpoint: no route supports that size right now | Try another size or retry later. Failed calls are not billed |
| 400 | quality_unavailable | Images endpoint: none of this model's routes for that size accept the quality you sent | Retrying will not change the result. Use one of the values listed in the error, or auto |
| 404 | unknown_model | model is not a real model name, a tier name, or one of your custom models | Check GET /v1/models, or create a custom model in the console first |
| 404 | session_expired | /v1/responses: the session behind previous_response_id is past its 5-hour retention | Drop previous_response_id and resend the full context |
| 404 | session_not_found | /v1/responses: no such previous_response_id, or it isn't yours | Check the id came from a response on the same account |
| 404 | trace_expired | /v1/traces/{id}: the trace is past its 30-day retention | The trace has been deleted and can't be fetched |
| 404 | trace_not_found | /v1/traces/{id}: no such trace, or it isn't yours (a key without the usage:read scope gets 403 insufficient_scope first and never reaches this) | Check the trace id and the key |
| 401 | key_rotation_required | Key is older than the org's rotation policy | Rotate a new one in the console |
| 400 | capability_unavailable | No connected model supports a capability the request declares (vision, tools…). If the named model lacks it but another model has it, the gateway switches to that model and says so in the headers and usage log instead of failing | Retrying will not help. Drop the requirement, or wait for a model that supports it to be connected |
| 400 | context_too_small | The model you named by its raw ID has a smaller context window than this request's min_context_k | Retrying will not change the result. Shorten the context, or pick a model with a larger window — /models lists the window for every model |
| 400 | upstream_rejected_request | The model service rejected the request itself (parameters or content), or the input exceeds the context window of every available line (when one line cannot fit it, the gateway first moves to a line that can) | Another model gives the same answer. Check the request (shorten the context if it is too long); if you believe it is valid, send us the trace id |
| 400 | upstream_rejected | Images / video: the model service rejected this generation (prompt or reference assets not allowed, or size / parameters not accepted) | Another route gives the same answer. Change the prompt or size and retry; failed calls are not billed |
| 400 | request_exceeds_mix_cap | Worst-case estimate exceeds the max_cu_per_request set in your custom model | This is your own cap. Lower max_tokens, shorten the context, or raise the value |
| 403 | insufficient_scope | Key lacks the required scope | Use a key that has it |
| 403 | plan_excludes_model | Your custom plan doesn't include this model or group | Use a model in your plan, or contact us to change it |
| 400 | strict_max_tokens_unavailable | You sent X-AGIPlan-Strict-Max-Tokens, and every route that could serve the request does not guarantee max_tokens | Retrying will not change the result. Drop the header (output beyond the cap is billed at actual usage), or use auto / another tier |
| 403 | no_subscription | No active subscription, and pay-as-you-go is off (or the balance is empty) | Subscribe in the console, or enable pay-as-you-go and top up |
| 429 | quota_exceeded | A window's quota is exhausted | Wait per Retry-After, or move to a cheaper capability tier. If you hold a quota reset for that window, the error says so; use it from the console overview to restore the quota at once |
| 429 | concurrency_exceeded | In-flight requests hit your plan's ceiling | Wait for an in-flight request to finish before sending another. Upgrading resolves this; topping up does not, since it is not a quota problem |
| 429 | upstream_busy | Every model service that can take this request is full right now (rate-limited or at its concurrency limit); the gateway already waited a moment for a free slot | Retry after Retry-After (the shortest wait the model services gave, or 10 seconds). Not your quota or concurrency; failed attempts are not billed |
| 429 | rate_limited | Too many requests from the same source: CLI device-code requests and signing of result links are capped | Wait for Retry-After and try again. Normal use never hits this; looping scripts do |
| 451 | region_not_available | No model in that tier is offered in your region | Retrying will not change the result. Pick another tier, or see the region column at /models. If you picked the wrong region, change it under Account & security; it takes effect immediately |
| 503 | no_supply_configured | No model service is currently available for that capability tier | Retrying will not change the result. Pick another tier, or use auto — /models shows how many lines each tier has right now |
| 503 | policy_unavailable | The custom model you called no longer compiles, usually because a model it pins was withdrawn | The error message names the pool and the model. In the console under Custom models, point that pool at another model or a tier; it takes effect as soon as you save |
| 503 | model_unavailable | The model you named by its raw ID has no working route right now | The gateway will not substitute another model. Retry later, or use a capability tier name (e.g. domestic) so the gateway can switch between equivalent models |
| 503 | all_upstreams_failed | Every model in the tier was tried and none worked | The body carries the full record of attempts. Failed attempts are not billed |
| mid-stream | upstream_interrupted | The model service dropped after output had started. The status is already 200, so this arrives as an error event inside the stream, with no normal end marker after it | Output already sent is billed by actual tokens; a stream that drops before producing anything is not billed. Switching models mid-answer would make it inconsistent, so the gateway does not fail over here; resend the request |
Long waits on a stream start with comment lines. Failover or a long prompt can delay the first event; if nothing has arrived after 20 seconds, the gateway sends 200 with the streaming headers, then an SSE comment : keepalive every 15 seconds (ignored by clients per the SSE spec) so idle timeouts in clients or proxies do not cut the connection. If such a request then fails, the error comes as an in-stream error event (codes as in the table above), and the response headers carry no quota or trace headers — the trace id is in a : trace tr_… comment line.
You don't need to retry 5xx yourself. Failover already ran inside the gateway — a 503 means every model in the tier was tried.
This table lists the codes the gateway really returns. Running out of quota is still a 429; with pay-as-you-go on, the request draws from your balance instead of being refused. If the balance can't cover it either, it is still a 429 and the message says which gate failed (not enabled, ceiling reached, insufficient balance).
Branch on code, not on message. code is the stable contract and changes are announced; message is a sentence for a human and its wording will change. message is currently Chinese only; code and the HTTP status are unaffected.
Trace IDs
Successful responses carry a trace ID and the model that actually ran. If failover happened, that's marked too. Refused responses carry no trace ID, because the call never became a usage record. On a 503 the chain is in the response body as attempts; send that along when you report the problem.
X-AGIPlan-Trace: tr_01J8FQ3M7X X-AGIPlan-Model-Actual: deepseek-v4-pro X-AGIPlan-Failover: true
Fetch the full trace with your own key: GET /v1/traces/{id}, structured JSON, retained 30 days. Include the trace ID when reporting a problem.
Docs for AI agents
When an agent such as Claude Code or Codex does the integration for you, hand it any of these. It gets Markdown without navigation or scripts, with the same numbers as the web page:
| URL | What you get |
|---|---|
| https://agiplan.dev/llms.txt | Site index: base URL, the three protocols, a Markdown link for every public page |
| https://agiplan.dev/llms-full.txt | Every public page as one Markdown file |
Any public page + .md | e.g. /en/docs.md, /en/pricing.md, /en/models.md; home is /en.md |
Header Accept: text/markdown | The page URL itself returns Markdown |
Command line
The CLI has no distribution channel yet: it is not on npm and there is no download, so running agiplan gives command not found. The table below records implementation status, not whether you can run a command today.
| Command | What it does | Status |
|---|---|---|
| agiplan login | device-code auth: confirm in the browser, key delivered to the terminal | Available |
| agiplan status | remaining percentage in each of the three windows | Available |
| agiplan models | available models and capability groups | Available |
| agiplan use <name> | bind a custom model to the current key; --none unbinds | Available |
| agiplan env | print evaluable environment variables | Available |
| agiplan keys rotate <config> | rotate a key | Declined, see below |
| agiplan uninstall | delete the config file setup wrote (--yes to actually delete) | Available |
keys rotate is not offered, for security reasons. If a key could issue its own successor, an attacker with a leaked key could rotate first, and the new key would survive when you revoke the old one. Rotation only happens through browser confirmation: rotate in the console, or run agiplan login again.
A per-project .agiplan file is supported. It holds only the profile name, never a key, so committing it is safe.
Migrating from another service
If you already use an official SDK or another aggregator, only two things change: the endpoint and the key. Where model names differ, a custom model name gives you a mapping layer and your application code stays untouched.
Two behaviours differ from the official API. First, if you omit max_tokens we substitute 4096 and send it to the model, so long answers may be truncated at 4096 with finish_reason: "length"; when we do, the response carries x-agiplan-max-tokens-default: 4096. Second, when max_tokens is above 16k, or when the window is nearly used up, the cap sent to the model may be lowered, to bound how far a single request can go over quota (see the Streaming section above). When that happens, the trace records the value before and after.
GLM models think, and the thinking tokens count against max_tokens. Give it a small budget and you get an empty answer with finish_reason: "length", billed as usual. With auto we steer around it: a request that did not ask for thinking and allows fewer than 1024 output tokens will not land on GLM. If you name a GLM model, leave max_tokens roomier than usual (at least 1024); to turn thinking on explicitly, send thinking: { "type": "enabled", "budget_tokens": 2048 }, accepted in both request formats.
One more thing that affects your bill: when a request needs a capability the tier you picked cannot provide, we lift it to a tier that can — and bill the tier it landed on. The common case is images: your custom model selects the fast tier, no model there has vision, so the call lands on the domestic flagship tier and is billed at that tier's higher coefficients. We only ever lift, never drop, so a downgrade never happens silently. Every text-endpoint response carries x-agiplan-billed-group naming the tier actually billed, matching what appears in your usage detail. To avoid being lifted, don't send that custom model content its tier can't handle, or pick a tier that supports it.
- Create a custom model named after the model name you use today, routing to the matching capability tier.
- Change the base URL and key, then run your tests.
- Check the coefficients at /models and reconcile your first day against the usage detail in the console.
JMRAI legacy balance migration
Legacy JMRAI (jmrai.net) users can migrate their balance: sign in at jmrai.net, get your migration link, then confirm it on this page. Amounts are fixed by the frozen snapshot taken when the legacy service stopped; one legacy account binds to exactly one AGI Plan account, permanently.
The migrated amount lands as bonus balance: valid for 90 days from credit, usable for pay-as-you-go (not for buying or renewing plans), non-refundable, non-withdrawable and not invoiceable. After any paid purchase you also get a 5% bonus on your legacy top-ups (migrated amount included), credited automatically after a 7-day confirmation window with the same 90-day validity.
- Migration entry: jmrai.net (legacy password or WeChat verification code).
- The migration window closes automatically on 2027-10-12; unclaimed balances are forfeited after that.
- For amount or eligibility disputes, open a support ticket with your legacy order records.
If something is missing or unclear, email [email protected] or open a ticket in the console.