# Models & provider keys

**You choose which model each role uses, and you can bring your own key.** Every model hlix offers is reached through one model gateway. When your Workspace stores its own OpenRouter key, hlix calls every model with your key. Otherwise hlix uses its own access, and the call counts toward your plan's usage. Either way, the [usage record](/running/metrics/) attributes every call to the role and task that made it.

Two settings do the work. A **provider key** says which account pays for the models. A **model preference** says which model a role uses.

## Prerequisites

- The `owner` or `admin` role in the Workspace. Storing, rotating or removing a key is Workspace administration; a `member` can see whether a key is configured but not change it, and a client collaborator cannot see it at all.
- An OpenRouter key. One key covers every model hlix offers.

## Store a provider key

In the dashboard, **Settings → AI models** shows the model gateway row, with the key state and an enable switch. Over the API:

```bash
curl -sS -X POST "$HLIX_BASE_URL/v1/api/providers" \
  -H "x-api-key: $HLIX_API_KEY" \
  -H "X-Organization-Id: $HLIX_WORKSPACE_ID" \
  -H 'content-type: application/json' \
  -d '{"provider":"openrouter","apiKey":"sk-or-…","enabled":true}'
```

Expected result: `201` and the stored configuration, **without** the key. No endpoint ever returns key material.

The one accepted `provider` value is `openrouter`. A key stored for another provider before this changed is kept but never used.

**Provider routes are not in the OpenAPI contract:** `/v1/api/providers` is not part of the [OpenAPI contract](/api/openapi/), so the generated clients do not include it. The dashboard is the primary way to manage keys; the calls above are stable. Model preferences **are** in the contract — see the [model configuration reference](/api/reference/model-config/).

Keys are stored encrypted. `PATCH /v1/api/providers/{provider}` rotates a key in place, and `DELETE` removes it.

### Saving and testing are separate steps

**Saving stores the key without checking it.** An expired, revoked or mistyped key is stored like a working one, and you only find out when a task fails with the provider's `401`.

Test a key explicitly:

```bash
curl -sS -X POST "$HLIX_BASE_URL/v1/api/providers/openrouter/test" \
  -H "x-api-key: $HLIX_API_KEY" \
  -H "X-Organization-Id: $HLIX_WORKSPACE_ID" \
  -H 'content-type: application/json' \
  -d '{}'
```

Expected result for a working key: `200` and `{"ok": true, "status": 200}`. hlix asks the provider to list its models, using the stored key or an `apiKey` you pass in the body to check a key *before* saving it — which is what the dashboard does.

A key that does not work returns **`502`** with the provider's answer:

```json
{"ok": false, "status": 401, "detail": "…the provider's answer…"}
```

The `502` means the test reached a result, not that hlix failed — read `ok`. Test before you save, and test again after rotating a key.

## Set the model for a role

Seven roles take a model preference:

| Role | What it does |
| --- | --- |
| `classifier` | Sorts incoming work before anything acts on it |
| `intake` | Decides whether a request becomes a cycle, and its risk lane — see [Triage](/concepts/triage/) |
| `planner` | Turns a brief into a roadmap and a cycle into tasks |
| `orchestrator` | The project manager: decides what happens next in a project |
| `worker` | Your Workspace's coder model, used by every coder that reaches models through hlix, including [the hlix coder](/coders/native-hlix/) |
| `reviewer` | Reviews a cycle's finished work |
| `chat` | Answers you in the project conversation |

```bash
curl -sS -X PUT "$HLIX_BASE_URL/v1/api/model-config/worker" \
  -H "x-api-key: $HLIX_API_KEY" \
  -H "X-Organization-Id: $HLIX_WORKSPACE_ID" \
  -H 'content-type: application/json' \
  -d '{"modelIdentifier":"anthropic:claude-opus-5","temperature":0.2}'
```

Expected result: `200` and the stored preference. `temperature` accepts `0`–`2` and `maxTokens` a positive whole number; both are optional. `GET /v1/api/model-config` lists every role's preference, and `DELETE /v1/api/model-config/{role}` clears one so the role uses the default model.

The dashboard shows the same list under **Settings → AI models**, one row per role.

## How coders reach models

| Coder | How it reaches a model |
| --- | --- |
| `hlix`, `claude-code`, `codex`, `opencode`, `goose`, `qwen-code`, `pi` | Through hlix, on a short-lived key created for the task. hlix calls the task's model with your Workspace's key, and meters every call to the task |
| `cursor-agent` | With its own Cursor key, which is not your Workspace key |

No provider key of yours enters a task's sandbox.

### Which model a task runs on

The coder and the model are separate choices: any model runs in any coder. A task runs on its assigned agent's model if it has one, else the `worker` model. When neither is set, Claude Code runs a Claude Sonnet model, Codex an OpenAI GPT model, and every other coder the default `worker` model.

**Claude Code is tuned for Claude models:** Claude Code runs any model you choose, but it is built for Claude models and its tool use is most reliable on them.

## How hlix handles your keys

- **Stored encrypted** and never returned. The dashboard shows *whether* a key exists, never its value.
- **Tested on request** against the provider. Saving alone accepts what you send.
- **Redacted from every record.** Audit entries, run logs and coder output are redacted before they are stored, so a test that prints a key does not expose it.
- **Kept off command lines and out of sandboxes.** Coders reach models through hlix on a task key, never with your provider key.

## If a key or model setting does not behave

- **`{"error":"admin only"}` with `403`.** Storing, rotating, testing or deleting a key needs `owner` or `admin`.
- **`{"error":"agency only"}` with `403`.** A client collaborator called a provider route. Client access never includes Workspace credentials.
- **`{"error":"Invalid provider"}` with `400`.** The provider is not `openrouter`, the one accepted value.
- **`{"error":"Provider not found"}` with `404`.** No key is stored yet. Create it with `POST` before using `PATCH`.
- **`{"error":"No stored credentials"}` with `404` on a test.** No key is stored and none was sent. Send `apiKey` in the body to test a key before saving it.
- **`{"error":"Invalid agent role"}` with `400`.** The role is not one of the seven above.
- **`{"error":"Preference not found"}` with `404`.** That role has no preference set and already uses the default.
- **A key saved but every task fails with a provider `401`.** Run the test call: it reports the same `401` without spending a task. If the test passes and tasks still fail, the key cannot use the model the role is set to — change the model, or the access at the provider.

## Where to go next

| If you want to… | Read |
| --- | --- |
| Choose which coder runs a task | [Connect your agents](/guides/connect-agents/) |
| See the request and response shapes for model preferences | [Model configuration API](/api/reference/model-config/) |
| Understand what each role does | [Agents & orchestration](/concepts/agents/) |
| See what a run cost | [Delivery metrics](/running/metrics/) |

## Next steps

[Connect your agents](/guides/connect-agents/)
  [The hlix coder](/coders/native-hlix/)
  [External coders](/coders/external-acp/)
  [Agents & orchestration](/concepts/agents/)