Docs
Jan Agent
Jan Agent ships no inference engine, so it always calls an endpoint you name. One jan config set line writes Kunavo into ~/.jan/config.toml, and the terminal agent runs on Claude, GPT and Gemini through a single key.
One `jan config set --base-url https://api.kunavo.com/v1` line writes Kunavo into ~/.jan/config.toml, and the Jan Agent preview CLI — which ships no inference engine — runs on that key.
# Jan Agent is a preview on a nightly channel — check your build first.
jan --version
jan config set \
--provider kunavo \
--api-key sk-kn-... \
--base-url https://api.kunavo.com/v1 \
--model claude-sonnet-5 \
--model claude-haiku-4-5 \
--api-type openai
jan config list # configured providers as JSON, keys redacted/v1, on both wire types. Jan appends only the route: the contributing-a-provider page says sign-in “validates the key against GET {base_url}/models”, and the providers page says a configured entry is queried for its GET /models the first time you open /model in a session. Every base URL printed in those docs ends in /v1 — including the Anthropic one in the jan cli models list sample. So --api-type anthropic takes https://api.kunavo.com/v1 as well, which is the opposite of Claude Code and the official Anthropic SDKs, where the same suffix produces /v1/v1/messages and a 404. A doubled path in the error is the tell for which convention you are on.dev pulls from the agent-nightly channel — “expect nightly-quality builds”. There is no tagged release to cite, so run jan --version and write that string down next to this config: the flags below were read from the documentation on the date at the bottom of this page, and a nightly can rename one. A source build (scripts/install-jan-agent.sh --source) does not self-update, which is one way to hold a version still.jan config unset --provider kunavo is the whole undo.<project>/.jan/agent/memory/ rather than a vector store, so nothing in the agent's own loop needs a second kind of model.Step by step
- Create a key at
/app/keysand copy it — it is shown once. - Check what you are configuring:
jan --version. Jan Desktop ships a CLI invoked asjantoo, with a different command set, so confirmjan config set --helplists--base-urlbefore you type the rest. - Run the
jan config setline above.--provideris an id you choose, not a name from a fixed list — the docs' own local-hardware example uses--provider local— and--modelis repeatable, replacing any existing list rather than adding to it. - Confirm it landed with
jan config list(keys redacted) orjan config pathfor the file itself, thenjan cli models listto see what every provider is offering. A model id typed by hand stays even when the endpoint stops listing it;jan cli models refresh --provider kunavotakes the endpoint's list as the truth instead. - Change into a project and run
jan, then pick the model with/model. Preferjan --planfor the first run: read-only, so a protocol mismatch shows up before anything is written to disk. - Give it a task that edits a file. Jan Agent is an agent, so tool calling and streaming are what a first run should exercise — they are what would fail first against an endpoint that only half fits, and a greeting exercises neither.
Checked against Jan Agent's Providers page on September 21, 2026. Third-party settings move; if a field name here no longer matches what you see, that page is the authority, not this one.
Verify before you debug the client
One request settles whether a failure is the endpoint, the key, or the configuration file. If this returns JSON, the same base URL and key work in Jan Agent.
# Settles whether a failure is the endpoint, the key, or the client.
curl -sS https://api.kunavo.com/v1/models \
-H "Authorization: Bearer sk-kn-..."Which model id to put in the field
Every text model is reachable as a model id — the live list is GET /v1/models, and the catalog with prices is on the models page. Rates are USD per 1M tokens, input / output.
| Model id | Kunavo in / out | Where it fits in Jan Agent |
|---|---|---|
claude-sonnet-5 | $2.00 / $10.00 | the default working model — the id to put first under --model |
claude-opus-5 | $2.00 / $10.00 | a plan that would be expensive to get wrong; pair it with jan --plan |
claude-haiku-4-5 | $0.40 / $2.00 | cheap turns: triage, summaries, the loop that runs all day |
gpt-5-6-sol | $2.00 / $12.00 | a second opinion from another family, same key and same base URL |
gemini-3-1-pro | $0.70 / $4.20 | long-context reading, still on the openai api-type |
Two different things are called a Jan API key
They live in the same file and mean opposite things, which is why jan config list can look wrong to someone expecting the other one.
| What | Where it comes from | What it authenticates |
|---|---|---|
jan login | Signing in to Tokamak, the self-hosted backend, from the shell or with /login in the console | Your own Tokamak deployment. Jan Agent writes the key it receives into ~/.jan/config.toml for you |
jan config set --api-key | A credential you already hold for some endpoint — Kunavo's sk-kn- key here | That endpoint, which is billed per request. This is the row this page is about |
Jan Desktop issues neither: it has no account, so there is nothing there to generate. Its local API server takes a key you invent yourself, which is a third meaning again and belongs to a different host.
What Jan Desktop hands down, and where that stops
Jan Agent reads four sources for provider settings, each beating the ones above it, and the second is the one that surprises people.
~/.jan/config.toml— the base, and the only filejan config setwrites.- Jan Desktop's
settings.json— inherit-only. It adds providers you have not configured in the Agent, never overwrites one you have, and is never written back to. - A
[provider]block in a project'sagent.toml— an explicit per-project choice, so it beats both. That file usually gets committed, so keepapi_keyout of it. --provider/--api-keyon the command line, orJAN_API_KEY/<PROVIDER>_API_KEY— the most explicit and the most ephemeral.
Two consequences worth knowing before you debug the wrong thing. jan config list can report no providers while jan cli models list returns plenty — the second includes the inherited Desktop ones, which are not stored in ~/.jan/config.toml. And an inherited provider is never refreshed, because there is no entry here to rewrite: if you want Kunavo's model list kept current, it has to be a jan config set entry of its own, which is what the block above creates.
When two providers offer the same model id
Kunavo serves ids like claude-sonnet-5, and so does a provider entry pointed straight at the vendor. Jan Agent has to pick one, and the documented order is: an exact match in a provider's models list first, then a <provider>/<model> prefix naming a configured provider, and when several offer the same id a credentialed provider wins over a keyless twin. So kunavo/claude-sonnet-5 is how you say which one you meant. The qualifier is for Jan only — it is stripped before the request goes out, because upstreams reject a provider-qualified id.
The same entry, from inside the console
If you would rather not type flags: /settings > providers manages the same ~/.jan/config.toml entries, and a opens an add form whose fields are name, base url, api key and space-separated models. Two things the form does that the flags do not: the base URL must be https:// (or http:// for a localhost endpoint), so a key is never sent over a plaintext remote connection — Kunavo is https://, so this is no obstacle — and on an edit the API key field shows (unchanged) and keeps what is stored unless you type into it. Blanking that field clears the key rather than leaving it alone.
FAQ
How do I point Jan Agent at a custom API endpoint?
With one command: jan config set --provider <id> --api-key <key> --base-url <url> --model <model> --api-type openai. The provider id is one you choose rather than a name from a fixed list, --model is repeatable and replaces any existing list, and --api-type defaults to OpenAI-compatible so it can be left off for an OpenAI-shaped endpoint. The entry is written to ~/.jan/config.toml, which you can also edit through /settings > providers inside the console. Jan's own contributing-a-provider page states that a plain OpenAI-compatible endpoint needs no code at all, only this config surface.
Does the Jan Agent base URL need /v1 on the end?
Yes, and for the Anthropic wire type too. Jan appends only the route: the contributing-a-provider page says sign-in validates the key against GET {base_url}/models, and the providers page says a configured entry is queried for its GET /models when you first open /model in a session. Since the appended path is /models and not /v1/models, the stored base URL has to be the /v1 root already — https://api.kunavo.com/v1 for Kunavo. Every base URL printed in Jan's documentation ends the same way, including the Anthropic entry in its jan cli models list sample. That is the opposite of Claude Code and the official Anthropic SDKs, where adding /v1 yields /v1/v1/messages and a 404.
What is the difference between jan login and jan config set --api-key?
They authenticate different things. jan login signs in to Tokamak, the self-hosted backend, and saves the key it gets back into ~/.jan/config.toml — it is a sign-in to your own deployment. jan config set --api-key stores a credential you already hold for some endpoint, which is what you use for a third-party provider such as Kunavo. Jan Desktop itself issues neither, because it has no account to issue one from; the key its local API server asks for is a string you invent, which is a third meaning of the phrase again.
Why does jan config list show nothing when jan cli models list shows models?
Because the two commands read different sets. jan config list reports only what is stored in ~/.jan/config.toml, while jan cli models list also includes providers inherited from Jan Desktop, which are not stored there. Jan's documentation calls this out directly. The practical consequence is that an inherited provider is never refreshed — there is no entry to rewrite — so if you want a provider's model list kept current, add it with jan config set first.
Can Jan Agent run Claude models without an Anthropic account?
Yes. Jan Agent ships no inference engine, so a model always runs at whatever endpoint you configure, and --api-type names the wire protocol rather than the vendor. A Claude id is resolved at the base URL you set, which means the credentials you hold are that endpoint's. Kunavo serves Claude, GPT and Gemini ids over its OpenAI-compatible surface on one key. This configuration was published from Jan's own documentation rather than from a test run against the client, and Jan Agent is itself a preview on a nightly channel, so treat the flags as true of the build you check with jan --version.
Jan Agent returns 404 on every request. What is wrong?
Almost always the base URL. A missing /v1 makes Jan request /models and /chat/completions at the origin, which 404s rather than failing authentication; a doubled /v1/v1 in the error means the suffix was added to a base that already had it. Settle it outside the client first by calling GET /v1/models on the endpoint with a plain curl and the same key: JSON back means the endpoint and key are fine and the problem is the configured entry, a 401 means the key, a 404 means the URL. Then check jan config path and read the stored base_url directly.