Connect Kilo Code through an OpenAI-compatible provider
Kilo Code is an open-source coding agent for VS Code, JetBrains IDEs and the terminal, and it bills through whichever provider you connect. Its 7.x releases share one configuration file, kilo.jsonc, between the VS Code extension and the kilo CLI, and they follow OpenCode closely: 7.8.0 took in OpenCode 1.18.21 through 1.18.26, per its release notes. A custom provider with the OpenAI Compatible API pointed at https://api.router.one/v1 puts the Claude, GPT, Gemini, Grok and DeepSeek chat models in the model picker behind one key. Every agent turn, subagent task, session title and compaction summary then reaches Router One as POST /v1/chat/completions and appears in Dashboard → Logs. The Custom provider dialog covers the base URL, the key and the model IDs; context limits, image input and the helper models that run in the background belong in kilo.jsonc. Checked against Kilo Code 7.8.1 (VS Code extension and CLI, September 25, 2026) on 2026-09-29 and re-checked on 2026-10-09 against 7.8.8 (October 7, 2026), per the Kilo docs and release notes.
Install Kilo Code and create a dedicated key
Install Kilo Code from the VS Code Marketplace; the 7.8.8 release (October 7, 2026) covers the VS Code extension and the kilo CLI, and the JetBrains plugin ships on its own schedule (7.1.9 on October 8). Then create a Router One key for Kilo alone and give it a maxSpend cap. An agent task sends one request per model turn, including turns that only hand a tool result back to the model, and subagents, titles and compaction add requests of their own, so a long task can bill many requests without further input from you. The cap is the hard stop, and a dedicated key lets you filter Dashboard → Logs down to Kilo's requests.
Configure Kilo Code to use the Router One base URL
Open Settings (the gear icon), go to the Providers tab, scroll to the bottom and click Custom provider. Enter a Provider ID such as router-one, which becomes the first half of every model reference (router-one/<model-id>), a display name, Provider API OpenAI Compatible, Base URL https://api.router.one/v1 and your key. Once the URL and key are valid, Kilo fetches GET /v1/models (7.8.3 and later also have a Fetch models button) and offers a searchable list. That list holds every catalog ID, image-generation models included, so add chat models only, preferably IDs whose model page lists tool calling, because Kilo's agents work through tool calls; you can also type exact IDs from /models. Click Submit and the models appear in the model picker. The dialog has no fields for context limits or image input, and Kilo's docs send you to kilo.jsonc for those. The file below defines the same provider there, with the key read from an environment variable:
{
"$schema": "https://app.kilo.ai/config.json",
"model": "router-one/anthropic/claude-sonnet-5",
"small_model": "router-one/anthropic/claude-haiku-4.5",
"provider": {
"router-one": {
"npm": "@ai-sdk/openai-compatible",
"name": "Router One",
"options": {
"baseURL": "https://api.router.one/v1",
"apiKey": "{env:ROUTER_ONE_API_KEY}"
},
"models": {
"anthropic/claude-sonnet-5": {
"name": "Claude Sonnet 5",
"tool_call": true,
"attachment": true,
"modalities": { "input": ["text", "image"], "output": ["text"] },
"limit": { "context": 1000000, "output": 32000 }
},
"anthropic/claude-haiku-4.5": {
"name": "Claude Haiku 4.5",
"tool_call": true,
"limit": { "context": 200000, "output": 16000 }
}
}
}
}
}Set limit.context and limit.output for every Router One model
Kilo takes a model's limits from your config, then from its built-in snapshot of models.dev for IDs it recognizes, and otherwise sets both to 0. The catalog IDs of a custom provider match no snapshot entry, so without a limit block both are 0, and Kilo's docs list the effects: compaction is disabled, so the conversation grows until a request is rejected for its length; the reply cap falls back to 32,000 tokens; and context usage is not tracked. Set limit.context from the model's page on /models, which shows the window rounded (1.05M, 200K), as a whole number rounded down: 1000000 for anthropic/claude-sonnet-5, openai/gpt-5.6-sol and deepseek-v4-flash, 200000 for anthropic/claude-haiku-4.5. Set limit.output to the longest reply you want to allow. Kilo sends it as max_tokens, which Router One's Chat Completions accepts as the output cap, and caps it at 32,000 by default; the KILO_EXPERIMENTAL_OUTPUT_TOKEN_MAX variable changes that cap. An optional limit.input makes compaction trigger before the full window. A larger window lets Kilo send longer prompts: on a plan, a request whose input exceeds a model's long-context threshold draws more than one plan request (see /pricing).
Declare image input and tool calling
For a model with no catalog match, Kilo assumes text input and text output only, so a pasted screenshot is not sent to a Router One model until you declare image input. Add modalities with image in input, plus attachment: true, and only for IDs whose model page lists image input: a text-only ID such as deepseek-v4-flash gets HTTP 400 with has no chat candidate that supports requested capabilities: vision. Set tool_call: true on the IDs whose page lists tool calling. Kilo's agents edit files and run commands through tool calls, and if the gateway answers HTTP 400 with has no chat candidate that supports requested capabilities: tool_calling, the chosen ID cannot take them; switch models. When you would rather have a short name in the picker, the id field sets the name sent to the API: a key sonnet with id anthropic/claude-sonnet-5 is selected as router-one/sonnet.
The helper models: titles, subagents and compaction
Three kinds of background request can run on models you did not pick. The small model writes session titles and commit messages and enhances prompts. Set small_model to a cheaper Router One ID: when it is unset, Kilo looks for a small variant on the current provider, then falls back to kilo-auto/small on the Kilo Gateway if you are signed in to it, in which case those requests never reach Router One, and otherwise reuses the main model (per Kilo's model-selection docs). Subagents launched by the task tool inherit the parent session's model unless subagent_model or agent.<name>.model names another, and you can ask for a different model or reasoning effort for a single subagent task. Compaction summaries use agent.compaction.model when it is set and the session's model otherwise. Each of these requests appears in Dashboard → Logs under its own model ID, which is how you find where a task's spend went.
Agents and per-agent defaults
Kilo's built-in agents are code (the default), ask, plan (similar to the old extension's Architect mode) and debug; orchestrator is deprecated, because agents with full tool access now launch subagents themselves. Set a default model per agent under Settings → Models or with agent.<name>.model in kilo.jsonc, and a global default with the top-level model key. In VS Code and the CLI the precedence is the model you picked for that agent in the current session or draft, then per-agent config, then the global default, then your organization's recommendation or, for an individual account, the most recently used available model, and only then Kilo's Auto Free tier, which routes to free models through the Kilo Gateway; new sessions start from config, so setting the global model to a Router One ID keeps a fresh agent from starting there (per Kilo's model-selection docs, checked 2026-10-09; 7.8.8 moved remembered picks below config). Every model on the router-one provider uses the same key, so a cheaper model for plan or ask and a stronger one for code show up side by side in Dashboard → Logs.
Other Provider API choices: Responses and Anthropic Messages
The dialog's Provider API list also offers OpenAI Responses, which sends POST /v1/responses through @ai-sdk/openai, and Anthropic Messages, which uses @ai-sdk/anthropic. On Router One, Responses serves the GPT-family, DeepSeek and Grok chat IDs whose model pages list it; a Claude-family ID sent there is rejected with HTTP 400 model '<id>' must be called via /v1/messages or /v1/chat/completions, and Gemini IDs are not served there either. Anthropic Messages takes Claude-family and DeepSeek V4 IDs, with Base URL https://api.router.one/v1, because that package appends /messages to it. OpenAI Compatible serves every chat model with one entry, which is why this guide uses it; if you add the others, give each its own Provider ID so their model lists stay separate.
What stays outside Router One
Inline autocomplete has its own provider setting in Kilo and is not part of this setup. Codebase indexing needs an embedding provider, and Router One has no embeddings endpoint (POST /v1/embeddings returns 404), so point indexing at a provider that serves embeddings; indexing stays off until you enable it. Models on the Kilo Gateway, such as the kilo-auto tiers, bill through Kilo rather than your Router One key. Kilo also accepts a full endpoint URL ending in /chat/completions for gateways with unusual paths, but Router One uses the standard layout, so the /v1 base URL is all it needs.
Which model ID should Kilo Code 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 Kilo Code. 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 Kilo Code 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 Kilo Code call in your request trace
Send a simple text request from Kilo Code, 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
Can each Kilo Code agent use a different model?
Yes. Set per-agent defaults for code, ask, plan and debug under Settings → Models, or with agent.<name>.model in kilo.jsonc, and within a session or draft Kilo remembers the model you picked for each agent; a new session starts from config. Every model on the router-one provider runs on the same Router One key, so a cheaper model for routine edits and a stronger one for planning appear side by side in Dashboard → Logs, each under its own model ID.
Why does a long session never compact, then fail on its length?
Because the model has no limit.context. Kilo recognizes none of a custom provider's IDs, so the window resolves to 0, which turns compaction off; the conversation then grows until a request is rejected for its length; since 7.8.3 (October 1, 2026) Kilo then compacts and retries when the provider's error says the request exceeded the context length (per its release notes), but it still never compacts ahead of time. Add limit.context (the model page's window as a whole number, rounded down) and limit.output to each model in kilo.jsonc, and Kilo compacts before the window fills.
Why are pasted screenshots not sent to the model?
Kilo treats a model it does not recognize as text-only. Add modalities with image in input and attachment: true to that model in kilo.jsonc, and only for IDs whose model page lists image input; a text-only ID would answer HTTP 400 with has no chat candidate that supports requested capabilities: vision.
Why are there requests in Logs that I didn't send?
Titles, commit messages, subagent tasks and compaction summaries are separate model requests. Set small_model to a cheap Router One ID for titles and commit messages, subagent_model or agent.<name>.model for subagents, and agent.compaction.model for compaction; each request then shows up under that model's ID. With small_model unset and a Kilo Gateway sign-in, the title and commit-message requests go to kilo-auto/small and never reach Router One.
My {env:ROUTER_ONE_API_KEY} reference doesn't authenticate. Why?
Kilo resolves {env:…} and {file:…} references only in trusted config: the global ~/.config/kilo file, a config passed through KILO_CONFIG or KILO_CONFIG_CONTENT, or managed configuration. In a project-level kilo.json committed to a repository the reference is ignored with a warning, so the provider sends no key. Keep the provider definition, or at least its key, in the global file.
Does the base URL need /v1?
Use https://api.router.one/v1, the standard form Kilo documents. Kilo also accepts a full endpoint URL ending in /chat/completions for gateways with unusual paths, but Router One follows the standard layout, so the /v1 base is all it needs.
I'm on an older Kilo Code build. Where are these settings?
Builds before the 7.x line expose the same three values directly under API Provider → OpenAI Compatible: Base URL, API Key and Model ID. The values are the same as above. The kilo.jsonc limits, small_model and per-agent settings in this guide describe 7.x.
Which Kilo Code version is this guide for?
Kilo Code 7.8.1 for VS Code and the kilo CLI (September 25, 2026), checked on 2026-09-29 against Kilo's docs on OpenAI-compatible providers, custom models, model selection and agents, and re-checked on 2026-10-09 against 7.8.8 (October 7, 2026). The JetBrains plugin follows its own version line (7.1.9 on October 8).
Which models can Kilo Code use through the gateway?
Choose a current catalog model that supports both the endpoint and the features Kilo Code 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.