Add a provider
You can add a language-model provider yourself — no plugin, no restart. It works for any provider that speaks one of two protocols:
| Protocol | Runs through | Typical providers |
|---|---|---|
| Anthropic | the Claude Code CLI | DeepSeek, Moonshot (Kimi), Z.ai, any Anthropic-compatible gateway |
| OpenAI Responses | the Codex CLI | OpenRouter, a vLLM or LM Studio server, any endpoint that serves /v1/responses |
If your provider speaks neither, it needs a provider plugin instead.
Adding or removing a backend is reserved to a superadmin: a backend decides where the prompts of every mission run on it are sent.
From Settings
Open Settings → LLM backends and choose Add a backend.
| Field | What to enter |
|---|---|
| Name | How the backend appears in the model selectors. |
| Protocol | Anthropic or OpenAI Responses. |
| Base URL | For Anthropic, the address the provider documents for ANTHROPIC_BASE_URL, without /v1. For OpenAI Responses, the address ending in /v1. |
| API key | Stored in the secrets vault, never in a file, and never shown again. Leave it empty for a local server that asks for none. |
Test makes one call to the provider's model listing and tells you which of these is true:
- the endpoint answers and accepts the key — the models it lists are proposed alongside the ones you entered;
- the endpoint answers but refuses the key;
- nothing answers at this address;
- the endpoint answers with another status (often
404: it has no model listing). The key could not be checked, which does not mean it is wrong.
Once saved, the backend is selectable immediately, in every place where you choose a model. Choose Edit to change its key, address, models or picture. Leave the key field empty to keep the saved key. Removing a backend also deletes its key from the vault.
You can also add models manually when the provider has no model listing. Enter the exact model ID, give it a label and choose which model should be the default. See backend management for disabling, reordering and editing built-in providers.
When a backend is inactive
An inactive backend is listed with its reason, for example:
vault entry … does not exist— the key it refers to was deleted from the vault;vault entry … has no grant— the key exists but no mission may receive it; grant it toglobalon the Secrets page;dialect … is not installed— the file names a protocol that no installed plugin provides.
From a file
Each backend is one file, ~/.timon/backends/<id>.json. The form writes that file; you can also write it, or copy one from a README. The folder is watched: a file you add, edit or delete takes effect within a second.
{
"id": "deepseek",
"dialect": "claude",
"label": "DeepSeek",
"vendor": "DeepSeek",
"env": {
"ANTHROPIC_BASE_URL": "https://api.deepseek.com/anthropic",
"ANTHROPIC_AUTH_TOKEN": { "secret": "BACKEND_DEEPSEEK_API_KEY" }
},
"defaultModel": "deepseek-v4-pro",
"models": [
{ "slug": "deepseek-v4-pro", "label": "DeepSeek V4 Pro", "contextWindow": 128000 },
{ "slug": "deepseek-v4-flash", "label": "DeepSeek V4 Flash" }
]
}
An OpenAI Responses provider also declares how the Codex CLI reaches it:
{
"id": "openrouter",
"dialect": "codex",
"label": "OpenRouter",
"env": { "BACKEND_OPENROUTER_API_KEY": { "secret": "OPENROUTER_API_KEY" } },
"providerConfig": {
"id": "timon-openrouter",
"name": "OpenRouter",
"baseUrl": "https://openrouter.ai/api/v1",
"envKey": "BACKEND_OPENROUTER_API_KEY",
"wireApi": "responses"
},
"defaultModel": null,
"models": [],
"modelsEndpoint": { "url": "https://openrouter.ai/api/v1/models", "format": "openai" }
}
| Field | Meaning |
|---|---|
id | Lowercase letters, digits and dashes. Must equal the file name. It cannot be the id of a built-in backend (claude, codex, …) or of one a plugin provides. |
dialect | claude (Anthropic protocol) or codex (OpenAI Responses), or a dialect an installed plugin provides. |
label, vendor, brand | Display name, vendor column and logo key in the selectors. |
env | Environment of the mission process. A value is either a plain, non-secret string or a reference to a vault entry: { "secret": "NAME" }. |
providerConfig | codex dialect only. Choose an id that is not one of Codex's built-in providers (openai, ollama, …). envKey names the variable of env that holds the key. |
defaultModel, models | Models offered in the selectors. models may be empty: the model field is then free text. |
modelsEndpoint | Optional. A public, OpenAI-style model listing that replaces models while it answers. |
Keys belong in the vault
Create the key on the Secrets page in inject mode, grant it to global, and reference it by name. A key without a grant is never handed to a mission, and the backend stays inactive.
A key written directly in the file works, but the backend is flagged with a warning: the file is plain text, and it is included when you export your installation.
What a file cannot do
A backend file is data. It chooses an address and a model list, nothing else, and these keys are refused:
cmd,requiredBinary— a file never chooses a program to run;requiredEnv,envFrom— a file never reads the environment of the Timon service.
What a file may set in env
Each protocol reads its own variables, and a file may only name those:
| Protocol (dialect) | Names a file may set |
|---|---|
Anthropic (claude) | ANTHROPIC_* — e.g. ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN, ANTHROPIC_API_KEY |
OpenAI-Responses (codex) | OPENAI_* — e.g. OPENAI_API_VERSION for Azure — and BACKEND_* for the backend's own key, as in BACKEND_OPENROUTER_API_KEY above |
Any other name is refused, with the reason, and the backend stays inactive — a file can never change what an agent runs, where it looks for it, or where it reads its configuration. A protocol added by a plugin brings its own list.
A codex file written from an earlier version of this page, with
OPENROUTER_API_KEY in env and envKey, is now refused: rename that variable
BACKEND_OPENROUTER_API_KEY in both places. The vault entry it points at keeps
its name.
If your provider genuinely needs a variable this leaves out, tell us the provider and the variable: the list is part of the product, and it grows with real ones.
A file that is refused, malformed, or named differently from its id is listed in Settings → LLM backends with the reason. It never prevents Timon from starting.