# Provider presets

> How to pick a curated API provider without memorising URLs, model ids, or auth headers — and how to extend the catalogue with custom endpoints.

How to pick a curated API provider without memorising URLs, model ids, or
auth headers — and how to extend the catalogue with custom endpoints.

<!-- SOURCE: src/kin/harness/presets.py, src/kin/harness/backends/factory.py,
src/kin/harness/backends/anthropic.py, src/kin/harness/backends/responses.py, src/kin/harness/settings/,
src/kin/tui/cli.py, src/kin/tui/modals/, src/kin/tui/commands_mixin.py,
src/kin/harness/commands.py, src/kin/harness/cli/connect.py, settings.toml.example -->

## What a preset gives you

A preset bundles provider identity, the **wire**, endpoint selection, and—when
curated—the **default model id**. The direct OpenAI/Anthropic rows deliberately
leave the endpoint and model at their SDK/operator defaults so a model route can
name an exact account-available model. Auth is bundled too: MiniMax and Z.ai
send `Authorization: Bearer <key>`. The preset chooses the correct form so only
one authentication header rides the request.

Use a preset when you don't want to memorise:

- `base_url` (e.g. `https://api.minimax.io/anthropic`)
- the right `provider` (`openai` vs `anthropic`)
- the right `wire_api` (OpenAI `chat` vs opt-in `responses`)
- the default `model` id
- the right auth header

A preset folds into the override chain via `setdefault` semantics — an
explicit CLI flag, env var, or file entry always wins over the preset's
default for the same knob. The preset never silently overwrites something
you set.

These built-ins and custom `[[providers]]` rows are also Outpost **execution
profiles**. Beam discovers their public catalogues from the remote machine and
submits only a profile/model reference; endpoint URLs and keys stay on
Outpost. The reserved `default` profile means that machine's active settings
snapshot. A vanished profile fails honestly rather than selecting another
model.

## Built-in presets

| id | label | wire API | `base_url` | default model | models | `key_url` |
|---|---|---|---|---|---|---|
| `openai` | OpenAI | Chat (Responses selectable) | SDK default (`api.openai.com`) | explicit | explicit | `https://platform.openai.com/api-keys` |
| `anthropic` | Anthropic | Messages | SDK default (`api.anthropic.com`) | explicit | explicit | `https://console.anthropic.com/settings/keys` |
| `minimax` | MiniMax | Messages | `https://api.minimax.io/anthropic` | `MiniMax-M3[1m]` (1M ctx) | `MiniMax-M3[1m]`, `MiniMax-M3`, `MiniMax-M2.7`, `MiniMax-M2.7-highspeed`, `MiniMax-M2.5`, `MiniMax-M2.5-highspeed`, `MiniMax-M2.1`, `MiniMax-M2.1-highspeed`, `MiniMax-M2` | `https://api.minimax.io/usercenter/charge` |
| `zai` | Z.ai | Messages | `https://api.z.ai/api/anthropic` | `glm-5.2` (1M ctx) | `glm-5.2`, `glm-5-turbo`, `glm-4.7` | `https://z.ai/manage-apikey/apikey-list` |

The direct rows are primarily stable trust handles for
[`model_routes`](/docs/kin/guide/models-and-providers/#model-routes). Set their exact model in
the route (or use the legacy top-level `model` setting); Kin does not pretend
that every account has one universal OpenAI/Anthropic model catalogue. In
`/providers openai` or `/providers anthropic`, enter an exact model to switch the
MCA too, or leave the model blank after pasting a key to save that credential
for future routed calls without changing the live MCA backend. Running children
retain the prior route snapshot.

The OpenAI row exposes a **Wire API** selector. Chat Completions is selected by
default for broad compatibility; choose Responses only for an endpoint you
intend to use through that API. Saving the row persists `wire_api` and swaps
the live backend. Selecting Anthropic clears a stale OpenAI wire choice because
Messages is implied internally by the provider.

### Cloud-plan subagent capacity

`/providers minimax` and `/providers zai` also show the subscription plan.
The saved `provider_tiers` choice sizes ordinary foreground and background
subagent capacity for every session on that provider; local and custom
endpoints keep the conservative default of 3.

| Provider plan | Kin max parallel/background agents | Basis |
|---|---:|---|
| MiniMax Plus (default) | 4 | Provider publishes 3–4 concurrent agents |
| MiniMax Max | 5 | Provider publishes 4–5 |
| MiniMax Ultra | 7 | Provider publishes 6–7 |
| Z.ai Lite (default) | 4 | Conservative Kin ceiling; provider manages concurrency dynamically |
| Z.ai Pro | 5 | Conservative Kin ceiling; live-gated at five concurrent calls |
| Z.ai Max | 7 | Conservative Kin ceiling; provider still owns dynamic throttling |

MiniMax's values follow its [Token Plan comparison](https://platform.minimax.io/subscribe/token-plan).
Z.ai documents only the ordering `Max > Pro > Lite`, recommends subagents, and
states that actual limits vary with capacity in its [Coding Plan usage policy](https://docs.z.ai/devpack/usage-policy).
Kin's Z.ai numbers are therefore client-side runaway guards, not promises about
the service. A provider rate-limit response still follows the normal bounded
retry path.

> **Z.ai Coding Plan tool policy**
>
> Z.ai's current [usage policy](https://docs.z.ai/devpack/usage-policy) says
> Coding Plan benefits are limited to its supported tools and may be
> restricted when used elsewhere. Kin speaks the documented compatible
> endpoint but is not an endorsement or entitlement guarantee; review Z.ai's
> current supported-tool list before relying on a Coding Plan subscription
> here.

The Anthropic-preset rows (MiniMax, Z.ai) carry a `thinking.type` knob shaped
like real Anthropic's, but each provider's vocabulary is narrower: MiniMax M3
documents `adaptive` / `enabled` / `disabled` (M2.x silently ignores
`disabled`); Kin's Z.ai Anthropic path uses top-level `reasoning_effort`
`max` / `high`. Neither provider documents Anthropic's
`output_config.effort`, although both configured endpoints tolerated that
legacy field in the live gate. The `/effort` picker uses the provider-real
paths — see [/effort](/docs/kin/guide/slash-commands/#effort) and the
[Effort picker table](/docs/kin/guide/models-and-providers/#effort-picker). The
construction-time default stays `adaptive` (change with
`KIN_THINKING_TYPE=...` env or `thinking_type = "..."` in `settings.toml`, or
flip live with `/settings on`).

> **Note**
>
> The `[1m]` suffix means **opposite things** on the two presets.
> Z.ai: the suffix is a Claude Code SDK convention, *not* a wire-level
> model id — a raw SDK call with it returns `model_not_found`. Use
> plain `glm-5.2` and set `context_window = 1000000`; that id serves
> the 1M tier. MiniMax: `MiniMax-M3[1m]` *is* the documented wire-level
> long-context id (1,048,576 tokens) — plain `MiniMax-M3` only
> guarantees 512K. Kin defaults to the live-verified `[1m]` id; pick the
> plain id only when you deliberately want the smaller window.

> **MiniMax Token Plan keys**
>
> MiniMax's flat-rate Token Plan uses a distinct **Subscription Key**
> that is explicitly *not interchangeable* with pay-as-you-go API
> Keys. Both ride the same header on the same URL, so the harness
> cannot tell which kind you pasted — a wrong-kind key fails (or draws
> the wrong balance) in a way that looks like a harness bug but isn't.
> Z.ai has no such split: once the account owns a GLM Coding Plan, the
> Anthropic-compat endpoint bills against it automatically.

Kin recognizes each provider's subscription error codes too. Exhausted
MiniMax 5-hour windows and exhausted/expired Z.ai Coding Plans are reported as
non-retryable with the reset/renewal action; bad keys name the correct key kind;
temporary Z.ai overload remains retryable with bounded backoff, while fair-use
and package-type restrictions point to the provider console instead of retrying.

## Connections in `/models`

The complete key-entry walkthrough and provider screenshot live in [Connect a
model and first run](/docs/kin/getting-started/first-run/#fast-path-models).
This page records the catalogue contract: `/models connections` lists built-ins
and custom rows, stages provider-specific credentials and fields, and applies
the complete configuration fail-closed. `/providers [name]` is a deep link to
that section and preselects the named connection.

From the shell, [`kin connect`](/docs/kin/reference/cli/#kin-connect) creates or
re-keys one custom OpenAI-compatible connection and activates it without
opening the TUI — the key comes from a masked prompt, never argv or the
environment, and the write goes through the same locked hub transaction. Its
`--vision-route` flags can additionally create and assign a hosted vision
route on the same connection in that one save.

Stored keys and extra-header values are never loaded into the form. Status and
masked placeholders show what exists; blank retains, replacement is explicit,
and removal requires confirmation. **Check connection** calls only the
authenticated models endpoint, never generates output, and can import its
bounded sanitized model ids into a custom connection draft. A failed check does
not block saving.

### Search credentials stay separate

`/providers` now deep-links into the staged model-configuration hub, so search
credentials are not mixed into its Connections list. `/brave-key` (alias
`/search-key`) opens the dedicated key picker directly on **Brave Search**
(`brave-search`). That picker shows where to get a key and whether one is
already set (env or file); typing a key and hitting ++enter++ saves it to
Kin's 0600 `~/.kin/credentials.toml` store (`brave_api_key`) and registers the tools it unlocks —
`web_search` + `web_context` — on the **running session**, no restart. A search
pick never touches model/provider configuration, and an empty key field warns
instead of saving. See [Web tools](/docs/kin/guide/web-tools/).

For the full key reference, see [Slash commands](/docs/kin/guide/slash-commands/); for
the modal's row shortcuts, see [Keybindings & cursor mode](/docs/kin/guide/keybindings/).

### `/models` vs `/model`

Persistent policy and current-session selection remain separate:

| Surface | When to use | Writes |
|---|---|---|
| `/models` (`/providers` deep link) | Stage connections, credentials, jobs, routes, and assignments as one transaction | The existing provider/route/assignment schema, atomically |
| `/model <id>` (inline) | Swap to a model id you already know; silent when a key resolves, else a one-field key prompt opens at the point of need | `provider_preset` + `model`; a prompted key lands via the hub's locked transaction |
| `/model` (bare, picker) | Browse across providers with the keyboard; the `✓ key` badge shows which are ready, unkeyed picks prompt inline | `provider_preset` + `model`; a prompted key lands via the hub's locked transaction |

`/models` validates and prepares the full catalog before
`save_model_configuration(...)`, then shares `/reload`'s application path.
When a `/model` pick has no resolvable key, the inline prompt collects one and
commits it through that same locked transaction before swapping — esc cancels
the swap and writes nothing.

## Other surfaces

Three surfaces reach the same preset machinery, in this precedence order:

| Surface | Example | When to use |
|---|---|---|
| CLI flag | `uv run kin --preset minimax` | A one-off override (highest precedence) |
| Env var | `KIN_PRESET=zai uv run kin` | A shell-level override (above the file) |
| Settings key | `provider_preset = "minimax"` in `~/.kin/settings.toml` | Persistent baseline |

The modal writes the file layer **and** swaps the live backend in place.
The file is still what new sessions read; `/reload` re-applies an
edited `settings.toml` to a running session without a restart too.

> **A raw `base_url` default isn't switchable-back-to**
>
> A plain top-level `base_url` + `model` in `settings.toml` (e.g. a local
> vLLM/llama.cpp endpoint with no `provider_preset`) works fine as your
> launch default, but it's **not a catalogue entry** — `/model` and
> `/providers` only list built-ins + `[[providers]]` rows. Swap away from it
> via `/providers <other>` and there's nothing left pointing back at it. If
> you want to swap back and forth, give it a `[[providers]]` row too (see
> [Custom endpoints](#custom-endpoints) below) — an auth-less local server
> just omits the key (`/providers` treats an empty field as fine to save; no
> key is ever required, only merged in when typed).

## Custom endpoints

The modal's **Custom endpoint…** path writes the `[[providers]]` schema shown
below. The task flow is in [First
run](/docs/kin/getting-started/first-run/#alternative-connection-methods); this is
the field reference for catalogue maintainers and managed configurations.

```toml
[[providers]]
id = "openrouter"
label = "OpenRouter"
provider = "openai"
wire_api = "chat"                         # or "responses" (explicit opt-in)
base_url = "https://openrouter.ai/api/v1"
default_model = "anthropic/claude-3.7-sonnet"
models = ["anthropic/claude-3.7-sonnet"]
# extra_headers = { "X-Tenant-ID" = "acme" }    # optional
# reasoning_effort = "high"                     # optional endpoint default; on a
#                                               # Responses row also opts the stream
#                                               # into reasoning-summary deltas
```

A custom row's `id` can shadow a built-in (so operators can override a
built-in's URL without code changes), but the built-in lookup runs
first so the curated catalogue wins for the common case. After saving,
`/providers openrouter` opens the modal pre-selected on the custom row.

`wire_api` is optional on an OpenAI row and defaults to `chat`. Its only public
values are `chat` and `responses`; either implies `provider = "openai"`. Do not
put `wire_api` on an Anthropic row—Messages is selected by the provider. Invalid
providers, wire values, and Anthropic/OpenAI-wire combinations are rejected
instead of guessed.

### Row keys: `model` vs `default_model` vs `models`

The three keys do different things, and a row that confuses them renders
an empty group in `/model` (the header shows but there's nothing to pick):

| Key | What it does |
|---|---|
| `default_model` | The model id the factory pre-fills when this preset is active and no other model was set. Also seeds `models` if no `models` list was given (see below). |
| `models` | The catalogue the `/model` picker iterates for this row. Multiple ids → multiple pickable rows. |
| `model` | Convenience alias for `default_model` — same key, just shorter. Honored ONLY when `default_model` is unset. Useful for one-line rows like a vLLM stable-alias setup: `model = "default"`. |

Two safety nets so the one-line shape still surfaces a pickable row:

- `model = "default"` is accepted as an alias for `default_model`.
- When neither `models` nor `default_model` is set, but `model` is, the
  model id seeds `models = (model,)` too — the picker renders one row.

```toml
# Minimal: single-model / stable-alias custom endpoint.
[[providers]]
id = "vllm"
label = "vLLM (staged serve)"
provider = "anthropic"
base_url = "http://model-host:8000/v1"
model = "default"                # alias for default_model; seeds models = ["default"]
```

`reasoning_profile = "poolside"` is the one model-family-specific row key.
It is accepted only with `provider = "anthropic"` and makes `/effort` use
Laguna's `chat_template_kwargs.enable_thinking` control while preserving
signed reasoning across tool rounds. It is deliberately explicit: Kin never
guesses a request profile from a model id or hostname. See
[Laguna S 2.1 on vLLM](/docs/kin/guide/models-and-providers/#laguna-s-21-on-vllm).

If you want the picker to show every model your server actually serves, prefer the explicit two-key form — or wire `/providers`'s "Custom endpoint…" form, which writes both `default_model` and (when known) `models` for you.

## The curated catalogue is not a silent cap

The built-in lists cover the verified ids from each provider's official
docs. A model id not in the catalogue is still reachable:

- `KIN_MODEL=<id> uv run kin` (overrides the preset's default)
- `--model <id>` (CLI override)
- A `[[providers]]` row that names it

The catalogue is the curated subset the harness is willing to ship
**defaults** for; nothing stops you from picking another model id on the
same wire.

> **Preset auth is provider-specific**
>
> MiniMax and Z.ai send `Authorization: Bearer <key>` because both reject
> the Anthropic SDK's default `X-Api-Key` header. The factory routes the
> resolved key into the SDK's `auth_token=`
> slot (not `api_key=`) so only Bearer rides the wire. A non-preset
> `AnthropicBackend(api_key=...)` still uses x-api-key as before — no
> behaviour change for users who aren't using presets.

## The GLOBAL_ONLY safety boundary

`provider_preset` and `providers` are `global-only` keys — honored from
the global file, the environment, or a CLI flag, but stripped (with a
warning) from any project `.kin/settings.toml`. A cloned repo's project
file can't silently retarget your wire to a third-party endpoint and
silently route your global `api_key` through it. The threat model is
identical to `base_url` / `api_key` being global-only — see
[settings.toml keys](/docs/kin/reference/settings-toml/) for the rest of the
denylist and the rationale.
