Kimi is Moonshot AI’s model family, served over an Anthropic-compatible endpoint —
shunt injects the Moonshot API key and forwards Claude Code’s Messages request. Non-Anthropic
upstream ids have deferred-tool fields stripped (same rule as OpenRouter stealth slugs). The
kimi preset is built in, so configuration is one upstream entry plus routes.
This page covers two separate Kimi services with separate credentials: the metered Moonshot
API (kimi preset, API key, below) and the Kimi Code subscription (kimi-code preset,
OAuth login, see Kimi Code (OAuth subscription) at the
bottom of this page). They are different endpoints and are not interchangeable.
Quick start
Let a coding agent wire it up for you — shunt add prints an embedded setup blueprint
(offline and read-only; the agent edits the config, the command never does):
shunt add upstream kimi --print | claudeOr follow the manual steps below.
Configure the upstream
The kimi preset supplies kind = "anthropic", base_url = "https://api.moonshot.ai/anthropic",
and API-key auth from MOONSHOT_API_KEY:
[[upstreams]]
name = "anthropic"
provider = "anthropic" # keep Anthropic as the default for unrouted models (e.g. claude-*)
[[upstreams]]
name = "kimi"
provider = "kimi"
[[routes]]
model = "kimi-k3"
provider = "kimi"
[[routes]]
model = "kimi-k2.7-code"
provider = "kimi"Ordered [[upstreams]] replace shunt’s built-in providers, so the config that routes to kimi
must also declare the anthropic default it still points at (server.default_provider defaults
to anthropic); drop the anthropic entry only if you also set default_provider to a declared
upstream.
The legacy [providers.kimi] table form remains supported (older examples used
api_key_env = "KIMI_API_KEY", which still works when set explicitly) — but do not mix
[[upstreams]] and [providers.*] in one file.
Credentials
export MOONSHOT_API_KEY='...'Never write the key into the config. shunt check validates the config’s structure but does not
read the key’s value — if MOONSHOT_API_KEY is unset, the first request routed to kimi returns
an authentication error.
Models
| Model id | Notes |
|---|---|
kimi-k3 |
frontier tier; a client may append Claude Code’s [1m] context marker (kimi-k3[1m]) — shunt strips it before matching, so route the unsuffixed id |
kimi-k2.7-code |
coding-focused tier |
Select a routed id in Claude Code via ANTHROPIC_MODEL, ANTHROPIC_CUSTOM_MODEL_OPTION, or a
subagent’s model: frontmatter. To surface an entry in the /model picker instead, advertise a
claude-prefixed alias with a [models.upstream_model] map — see
Model Discovery.
Verify
shunt check # -> config ok
shunt run
curl -sS http://127.0.0.1:3001/v1/messages \
-H 'anthropic-version: 2023-06-01' \
-H 'content-type: application/json' \
-d '{"model":"kimi-k2.7-code","max_tokens":16,"messages":[{"role":"user","content":"Reply with OK."}]}'Confirm the response’s x-gateway-upstream header names kimi, then
point Claude Code at shunt.
Subagent plugin
The shunt-kimi plugin ships
one ready-made Claude Code subagent per model above:
/plugin marketplace add pleaseai/shunt
/plugin install shunt-kimi@shuntKimi Code (OAuth subscription)
Kimi Code is a separate, subscription-billed service from the metered Moonshot API above —
a different host (api.kimi.com, not api.moonshot.ai) and a different credential (an OAuth
token shunt manages, not MOONSHOT_API_KEY). It also speaks the Anthropic Messages wire shape,
so it uses the same adapter, just a different preset: kimi-code.
Quick start
shunt add upstream kimi-code --print | claudeOr follow the manual steps below.
1. Log in
shunt login kimi --name <account-name>--name is required; --mode, --long-lived, and --manual are not accepted for this login —
the credential is always refreshable and there is no manual-paste fallback. shunt runs an
RFC 8628 device authorization grant: it prints a URL
and a short code, you approve in a browser (on this device or another one), and shunt polls
until approval completes or the code expires. The stored account lands at
~/.shunt/accounts/kimi/<account-name>.json (0600, in a 0700 directory), overridable with
SHUNT_KIMI_ACCOUNTS_DIR.
Kimi rotates the refresh token on every refresh, and its access tokens last only about 15 minutes, so refreshes are frequent. Run one shunt process per Kimi account file — two processes sharing a file will invalidate each other on the first refresh. Provision a separate account for each process instead.
2. Configure the upstream
The kimi-code preset supplies kind = "anthropic", base_url = "https://api.kimi.com/coding",
and auth = "kimi_oauth":
[[upstreams]]
name = "kimi-code"
provider = "kimi-code"
auth = { mode = "kimi_oauth", account = "<account-name>" }
# Declaring [[upstreams]] replaces the built-in provider set, so keep a trailing
# anthropic passthrough — without it `shunt check` rejects the default
# server.default_provider. This is the same entry `shunt init` appends.
[[upstreams]]
name = "anthropic"
provider = "anthropic"kimi_oauth is pool-capable, exactly like claude_oauth and chatgpt_oauth: use
accounts = [...] instead of account to pool several named accounts under one upstream
(the two are mutually exclusive), or omit both to scan the whole shunt-managed Kimi account
store.
Models
shunt does not query Kimi Code’s own model-listing endpoint — it serves /v1/models from
shunt’s built-in catalog. Route the model ids your subscription is actually entitled to:
[[routes]]
model = "<model-id-your-subscription-exposes>"
provider = "kimi-code"Verify
shunt check # -> config ok
shunt run
curl -sS http://127.0.0.1:3001/v1/messages \
-H 'anthropic-version: 2023-06-01' \
-H 'content-type: application/json' \
-d '{"model":"<model-id-your-subscription-exposes>","max_tokens":16,"messages":[{"role":"user","content":"Reply with OK."}]}'Confirm the response’s x-gateway-upstream header names kimi-code.
A 402 Payment Required with "We're unable to verify your membership benefits at this time"
means the login worked but that account has no active Kimi Code membership. The credential is
fine; the subscription is what needs attention.
Pooled accounts and the admin surface
A kimi_oauth pool participates in the same load-balancing, failover, and quota-aware account
rotation as the Claude and Codex pools, and its accounts appear in GET /admin/pool and in the
sanitized GET /usage aggregate when those are enabled. It rotates on one extra condition the
other pools do not have: the 402 membership response above. Because an inactive membership
returns 402 on every request, shunt treats it as an account-level failure — it cools that account
down and tries the next one, rather than handing the 402 to your client while healthy accounts sit
idle. If every account in the pool is inactive, you still get Kimi’s own 402 status and message
back, so the cause stays visible. Browser-driven account provisioning in
the admin web surface does not support
Kimi accounts — that surface’s pool view is read-only for Kimi; provision Kimi accounts with
shunt login kimi on the CLI.