Skip to content

Connect Pi Agent to Router One with models.json

Pi is a terminal coding agent that runs file and shell tools on your machine. Router One handles its model requests, routing and per-request billing. This guide targets @earendil-works/pi-coding-agent 1.0.4 (released 2026-10-05) from the earendil-works/pi repository, checked on 2026-10-06 against its docs and source. Start with the openai-completions adapter: despite its name, it uses Chat Completions, not the legacy text completions endpoint.

Install Pi and export a dedicated gateway key

Pi 1.0.4 requires Node.js 22.19.0 or newer. Check the Node version before installing the pinned package below. Pi also offers installers, curl -fsSL https://pi.dev/install.sh | sh on macOS or Linux and irm https://pi.dev/install.ps1 | iex in Windows PowerShell; per their source (checked 2026-10-06), they install the latest Pi release, not the version pinned here. Per Pi 1.0.4's quickstart (checked 2026-10-06), the installer pins all of Pi's dependencies, while an npm install does not pin transitive dependencies. Create a dedicated Router One key with a maxSpend cap, then export it in the terminal where Pi will run. ROUTER_ONE_API_KEY is the variable used by this guide; Pi reads it because models.json explicitly references it.

terminal
node --version
npm install -g --ignore-scripts @earendil-works/pi-coding-agent@1.0.4
pi --version
export ROUTER_ONE_API_KEY="sk-your-router-one-key"

Configure Pi Agent to use the Router One base URL

Add router-one inside the providers object of ~/.pi/agent/models.json, preserving any other providers already in that file. If PI_CODING_AGENT_DIR is set, use models.json in that directory instead. apiKey uses the dollar-prefixed environment syntax of Pi 1.0.4. The provider name router-one is a local label; model.id is the complete Router One catalog ID. The example uses a current chat model, keeps reasoning off and sets a 4096-token output cap for the first test. Replace the ID with another current chat model from /models when needed. The compat settings omit store, developer-role and reasoning-effort options in this introductory configuration; they do not declare that every model lacks those features.

~/.pi/agent/models.json
{
  "providers": {
    "router-one": {
      "baseUrl": "https://api.router.one/v1",
      "api": "openai-completions",
      "apiKey": "$ROUTER_ONE_API_KEY",
      "compat": {
        "supportsStore": false,
        "supportsDeveloperRole": false,
        "supportsReasoningEffort": false
      },
      "models": [
        {
          "id": "anthropic/claude-sonnet-5",
          "reasoning": false,
          "input": [
            "text"
          ],
          "maxTokens": 4096
        }
      ]
    }
  }
}

Select the exact model, then test one text response

Run --list-models to check that Pi loaded the provider; this lists local configuration and does not prove the key or endpoint works. Run the text test below from an empty directory. It disables tools, session saving, extensions and project context, while the model request can still incur usage charges. Match the response with Dashboard → Logs by model, time and request_id, then start a normal interactive coding session. Keep --provider and --model separate so a slash inside the catalog ID remains unambiguous. After editing models.json during a session, reopen /model to reload it. Exporting a new environment variable in another shell does not change the environment of an already-running Pi process.

terminal
pi --list-models router-one
pi --provider router-one --model anthropic/claude-sonnet-5 \
  --no-tools --no-session --no-extensions --no-context-files \
  -p -- "Reply with one short greeting."

# Start normal interactive use after the text test succeeds
pi --provider router-one --model anthropic/claude-sonnet-5

Match the API type to its base URL

Pi chooses the request adapter from api; changing only the URL does not translate protocols. Use separate provider entries if you want both Chat Completions and a native protocol. Do not include the final /chat/completions, /messages or /responses segment in baseUrl.

Match the API type to its base URL
Pi api valuebaseUrlRequest and model boundary
openai-completionshttps://api.router.one/v1POST /v1/chat/completions; current chat models
anthropic-messageshttps://api.router.onePOST /v1/messages; compatible Claude and DeepSeek IDs
openai-responseshttps://api.router.one/v1POST /v1/responses; confirm support on the selected model page

Treat local token and cost settings as client configuration

Pi does not populate this custom provider from Router One’s /models endpoint. Add each model ID yourself and update contextWindow, maxTokens, input and reasoning for the selected model. contextWindow defaults to 128000 in Pi 1.0.4; maxTokens limits output, so it is not the context-window field. This example caps output at 4096 and permits text input only. Copy current capabilities from the model detail page before enabling image input or thinking. Omitted cost values default to zero, which can make Pi show zero estimated cost even when Router One bills the request. Router One’s settled Logs are the billing record; Pi’s cost rates are local estimates and do not automatically include account discounts, subscription rules or long-context tiers. Tool turns, retries and context compaction can produce additional model calls.

Which model ID should Pi Agent send?

Copy the exact model ID from /models, preserving case, hyphens, and version suffixes; do not substitute a display name. Open its detail page and match the supported API endpoints, context window, and capabilities such as tool calling to the provider and features selected in Pi Agent. A catalog listing does not mean the client can use every feature of that model. Give each client or application a dedicated API key with a maxSpend cap.

Which API protocol is Pi Agent using?

OpenAI-compatible describes an interface format; it does not make Chat Completions (/v1/chat/completions), Responses (/v1/responses), and Anthropic Messages (/v1/messages) interchangeable. Check the installed client version, provider configuration, and actual request path against the model detail page and API compatibility fact sheet. A successful plain-text chat does not establish support for hosted tools, conversation state, or file-editing features.

Verify the Pi Agent call in your request trace

Send a simple text request from Pi Agent, then match its trace in Dashboard → Logs by time, model, and request_id: tokens, cost, latency, and status. Next, test streaming, tool calls, and multi-turn history separately. For failures, retain the actual request path, full error message, and request_id. If there is no matching log, either the request never reached the gateway (client configuration or connectivity) or the gateway refused it before calling a model — a 401 for the key, a 402 for funds, a 429 from your key's or account's limits, or a 400 or 404 for the model ID or endpoint; a streamed request that failed before any output may have no log either. The status and error message the client received tell which, so check them before attributing the error to the gateway or upstream.

FAQ

Why did a models.json key stop working after upgrading an older Pi installation?

The environment syntax changed. Pi 1.0.0 and later require apiKey: "$ROUTER_ONE_API_KEY"; per its models docs and its config-value source, a value without the dollar sign is used as a literal, so a bare "ROUTER_ONE_API_KEY" is sent as the key itself and can cause 401. In the older 0.74.2 package, the bare variable name was resolved from the environment, while the new dollar form was not supported by that resolver. Check pi --version and use the syntax documented for your installed version. For this guide, upgrade to 1.0.4 and keep the dollar sign.

Why is router-one missing from /model, or why is Pi still using an old key?

First check the active configuration directory, valid JSON structure and whether ROUTER_ONE_API_KEY was exported before starting Pi. In 1.0.4, a missing referenced environment variable leaves authentication unresolved, and Pi’s models docs say such models stay out of /model until it can resolve credentials. Per the same docs, Pi uses a runtime --api-key first, then a credential stored in auth.json, then the apiKey from models.json, and finally the provider’s environment variables. A key saved under the same router-one provider name can therefore override this configuration. Reopen /model after file edits; restart Pi if its process needs a newly exported environment variable.

Should I change compat settings when a 400 mentions a parameter?

Read the actual error before changing anything. Pi’s OpenAI adapter normally requests stream_options.include_usage and can send either max_completion_tokens or max_tokens through compat.maxTokensField. Keep usage enabled when supported; the token field can stay at its default, because Router One’s Chat Completions accepts max_tokens and max_completion_tokens alike as the output cap (if both are sent, max_completion_tokens wins). Likewise, enable reasoning and its format per model rather than assuming every catalog ID accepts OpenAI reasoning_effort. Provider-level compat applies to all of its models, while model-level compat can override individual settings.

Does this reuse my ChatGPT or Claude subscription, or change Pi’s built-in login?

No. router-one is a separate custom provider billed through your Router One account. Pi’s built-in providers and their /login sign-ins, including Sign in with ChatGPT on the openai provider since 0.99.0, are separate. Keep the custom name instead of replacing openai, openai-codex or anthropic; changing a built-in provider’s base URL can leave its existing credentials and built-in model IDs in use. Router One does not run Pi’s local tools, session storage, skills or extensions.

Which models can Pi Agent use through the gateway?

Choose a current catalog model that supports both the endpoint and the features Pi Agent uses. Check /models and the model detail page for the exact ID, current rates, and capabilities; a family name such as GPT or Claude is not a compatibility guarantee. A client that lists a model has only read the ID, from its own configuration or from GET /v1/models; verify an actual request too.

Models are listed, but requests fail with 400 or 404. What should I check?

Record the actual request path and error message, then check the exact model ID. A 400 can indicate invalid parameters, unsupported tools, or a model/endpoint mismatch; a 404 can indicate an incorrect path or missing resource, so it does not by itself establish that a model was retired. If the error says must be called via, use the named endpoint or select a model supported on the current endpoint. Do not add or remove /v1 or /chat/completions across all clients indiscriminately.

Does this work from Mainland China?

Yes. The gateway is reachable from Mainland China without a VPN, and the configuration is identical to the global setup.

How do I debug a 401/402/403/429?

Start from what your client received: the status, the error body and the X-Request-ID header. A 401 (missing, unknown, revoked or expired key), a 402 (wallet balance, or the key's maxSpend cap) and a 429 from your key's or account's rate limits are refused before any model is called, so they never appear in Dashboard → Logs — nor does a 400 or 404 for a wrong model ID or endpoint. For 401, check that the full key is sent and still active in Dashboard → API Keys; for 402, top up or raise the key's maxSpend; for a 403, keep the full error body. For 429, back off and retry later; a 429 that does show up in Logs came back from upstream after the gateway's retries. Keep the request_id and follow the error-codes reference.