> ## Documentation Index
> Fetch the complete documentation index at: https://docs.valarhq.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Portkey integration

> Run ValarCode through a Portkey gateway so Claude Code's per-engineer client id and routing still reach Valar

If you already run [Portkey](https://portkey.ai/docs) as your AI gateway, you can keep it and still use ValarCode. Claude Code points at Portkey, Portkey forwards to Valar, and Valar's per-engineer routing and attribution work as long as the client id makes it through. The same Portkey provider can also expose every Valar model for direct selection by clients such as LibreChat.

This page covers Claude Code. **Pi** works the same way: it also sends the client id in an `X-Valar-Client-Id` header, so the Portkey setup below applies unchanged. Just connect Pi with `valar pi on --endpoint https://api.portkey.ai --api-key <portkey-api-key>` and restart Pi. Instructions for connecting Cursor through Portkey are coming soon (Cursor carries its client id as a token suffix rather than a header).

## How it fits together

```mermaid theme={"system"}
flowchart LR
  CC["Claude Code"] -->|"X-Valar-* headers"| P["Portkey gateway"]
  P -->|"saved forward_headers allowlist"| V["Valar gateway"]
  V --> RT["Route request<br/>Opus / Sonnet / Haiku / Fable to target model"]
  RT --> MS["Valar model serving"]
```

`valar claude on` writes three Valar headers into `~/.claude/settings.json`: `X-Valar-Client-Id` (attributes usage to an engineer, defaulting to their username), `X-Valar-Harness` (names the tool, from a fixed set of supported values) and `X-Valar-Cli-Version`. Getting them across the Portkey hop is the one thing that matters.

It matters more through a proxy than it does directly. Portkey makes its own request upstream, so the user agent Valar sees is Portkey's, not your harness's. The headers are the only reliable signal left. Without `X-Valar-Harness`, everything on this path is recorded as Claude Code.

An **unrecognised** value does the same thing. `X-Valar-Harness` accepts a fixed set of tool names — `claude`, `codex`, `cursor`, `gemini`, `opencode`, `pi`, `copilot`, `librechat` — and anything outside that set is ignored rather than rejected, so putting your own application or deployment name here also records the traffic as Claude Code. That affects routing as well as reporting: traffic recorded as Claude Code is served on Claude Code's cohort configuration. Send the name of the tool actually making the request, and [talk to us](mailto:support@valarhq.ai) if yours isn't on the list.

Portkey does **not** forward client headers by default. You must name the headers that may pass through. The recommended setup stores that allowlist in a Portkey Config. Some workspaces reject the equivalent per-request `x-portkey-forward-headers` setting as inline configuration. If the allowlist is missing or misspelled, requests can still succeed while the usage arrives at Valar unattributed.

## Add Valar to the Portkey Model Catalog

Valar speaks the Anthropic Messages API at `https://api.valarhq.ai`, so Portkey treats it as an `anthropic` provider pointed at a custom host.

In the Portkey dashboard, go to **Model Catalog → Add Provider** and create a provider with:

| Field | Value |
| - | - |
| Provider | `Anthropic` |
| Custom Host | `https://api.valarhq.ai/v1` |
| API Key | your Valar coding key (`vlrcode_…`) |
| Slug | `valar` |

Set the API key to the `vlrcode_…` key you generated in the [Valar Dashboard](https://app.valarhq.ai). Portkey stores it and authenticates to Valar upstream, so the key never lands on an engineer's machine. This is the same split LiteLLM gives you with a virtual key.

<Note>
  The Custom Host **must** include the `/v1` path segment. Portkey appends the endpoint path (`/messages`) to whatever you give it, so `https://api.valarhq.ai` without the suffix produces a 404 on every request. This is the opposite of the convention everywhere else in these docs, where you pass the Valar root and the client adds `/v1` itself.
</Note>

## Provision every Valar model

A new Anthropic integration starts with Anthropic's own model catalog. Add the Valar models to the integration before you create the Portkey API key.

In Portkey, open the integration you created, then go to **Model Provisioning → Add Model**. Leave **Model Type** on **Custom Model** and add:

1. `valar-auto`, the Valar auto-router.
2. Every model id returned by Valar's Models API for your coding key, except the OpenAI-only ids below.

```bash theme={"system"}
curl https://api.valarhq.ai/v1/models \
  -H "Authorization: Bearer <valar-coding-key>"
```

Use each returned `data[].id` as the Portkey **Model Slug**, preserving its spelling and case. You can also browse the current catalog on the [Models](/models) page.

Skip the ids served only on Valar's OpenAI-compatible APIs. This integration speaks the Anthropic Messages API, and they have no route on it, so selecting one through Portkey returns an error instead of an answer. The Models API does not mark them; currently they are:

`google/gemma-4-26B-A4B-it` · `google/gemma-4-31B-it` · `MiniMaxAI/MiniMax-M3` · `nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B-NVFP4` · `openai/gpt-oss-120b` · `Qwen/Qwen3.5-27B` · `Qwen/Qwen3.5-397B-A17B` · `Qwen/Qwen3.6-35B-A3B`

Portkey combines the provider slug and model id in the request's `model` field:

```text theme={"system"}
@valar/valar-auto
@valar/zai-org/GLM-5.3
@valar/moonshotai/Kimi-K3
@valar/deepseek-ai/DeepSeek-V4-Pro
```

The first value lets Valar choose a model per request. The others select that exact Valar model. The model id can contain another `/`; Portkey treats only the first segment, `@valar`, as the provider selector.

<Note>
  Provisioning a model makes it available through Portkey. It does not route every request to that model. The caller still chooses with `@valar/<model-id>`, unless a Portkey Config fixes the provider or overrides the model.
</Note>

## Generate a Portkey API key

Portkey sits between Claude Code and Valar, so Claude Code no longer talks to Valar directly. Instead it authenticates to Portkey with a **Portkey API key** you create there.

In the Portkey dashboard, go to **API Keys → Generate**, and scope it to the `valar` provider you just created. Copy the key; Claude Code will use it as its bearer token.

What flows through after this:

* Claude Code authenticates to Portkey with the **Portkey API key**.
* Portkey authenticates to Valar with the **Valar coding key** stored in the Model Catalog.
* Claude Code's `X-Valar-*` headers are forwarded to Valar, so routing and attribution behave exactly as in the direct path.

## Create a Portkey Config

Store the forwarding allowlist in a Portkey Config. This works in workspaces that block inline gateway configuration.

In the Portkey dashboard, go to **Configs → Create** and enter:

```json theme={"system"}
{
  "provider": "@valar",
  "forward_headers": [
    "x-valar-client-id",
    "x-valar-harness",
    "x-valar-cli-version",
    "anthropic-beta"
  ]
}
```

Save the Config and copy its ID. Portkey Config IDs start with `pc-`, for example `pc-valar-a1bbcf`.

The Config selects the `@valar` Model Catalog provider and tells Portkey which request headers it may pass to Valar. `anthropic-beta` is included because Claude Code uses Anthropic beta features that fail closed when the header is dropped. Use this pinned Config for clients such as Claude Code that send bare model names. Clients that send `@valar/<model-id>` can select Valar without it.

<Warning>
  **This Config sends every request that carries it to Valar.** `provider` names one fixed target and there is no `strategy`, so Portkey makes no routing decision. A request that named its own provider does not get it: an `@slug/model` string like `@anthropic/claude-sonnet-5`, or an `x-portkey-provider` header, is discarded. Portkey resolves the provider from the Config unless the Config opts out, which is what [`passthrough`](#if-one-portkey-key-serves-other-backends) is for.

  That is what you want here. Claude Code sends bare `claude-*` model strings with no provider prefix, so something has to pick the provider, and the Config is where that happens.

  It is not what you want on a Config other traffic sees. Select this one per request, with the `x-portkey-config` header below, and **do not set it as the default Config on a Portkey API key that other applications share**. [If one key has to serve both](#if-one-portkey-key-serves-other-backends), drop the pin.
</Warning>

## Connect Claude Code

Point Claude Code at Portkey with `valar claude on`, using your Portkey API key:

```bash theme={"system"}
valar claude on --endpoint https://api.portkey.ai --api-key <portkey-api-key>
```

`ANTHROPIC_AUTH_TOKEN` authenticates Claude Code to Portkey. Add the saved Config ID to the `ANTHROPIC_CUSTOM_HEADERS` line in `~/.claude/settings.json`:

```json theme={"system"}
{
  "env": {
    "ANTHROPIC_BASE_URL": "https://api.portkey.ai",
    "ANTHROPIC_AUTH_TOKEN": "<portkey-api-key>",
    "ANTHROPIC_CUSTOM_HEADERS": "X-Valar-Client-Id: jdoe\nX-Valar-Harness: claude\nX-Valar-Cli-Version: 1.4.2\nx-portkey-config: <portkey-config-id>"
  }
}
```

One header goes on each line as `Name: value`. This is the newline-separated format `valar claude on` already writes.

<Note>
  Order does not matter, and neither does which you do first. `valar claude on` rewrites only its own three `X-Valar-*` lines and preserves every other header you set, so the Portkey lines survive re-enables, client-id changes and CLI upgrades. You can add them before or after the first `valar claude on`.
</Note>

Three things to keep straight in that block:

* **`x-portkey-config` selects the saved Config.** Do not also set `x-portkey-provider`; the Config already selects `@valar`. The one exception is the unpinned Config [below](#if-one-portkey-key-serves-other-backends), which deliberately selects nothing.
* **`ANTHROPIC_AUTH_TOKEN` is the Portkey credential.** You do not need to repeat it in an `x-portkey-api-key` custom header on the Model Catalog path.
* **Do not set `ANTHROPIC_DEFAULT_*_MODEL`.** Portkey's generic Claude Code guide tells you to pin model IDs per provider; that advice does not apply here. Valar maps whatever model string Claude Code sends to the target model your cohort resolves to, and `valar claude on` deliberately strips those pins so the gateway's server-side routing stays authoritative. Keep choosing tiers with `/model`.

<Note>
  Claude Code appends `/v1/messages` to the base URL, so pass `https://api.portkey.ai` without a `/v1` suffix. `valar claude on` strips a trailing `/v1` if you include one.
</Note>

### If your workspace allows inline configuration

If your Portkey workspace permits inline configuration, you can skip the saved Config and use these two lines instead of `x-portkey-config`:

```text theme={"system"}
x-portkey-provider: @valar
x-portkey-forward-headers: x-valar-client-id,x-valar-harness,x-valar-cli-version,anthropic-beta
```

The `@` prefix on `@valar` is required. A bare `valar` value is interpreted as a built-in provider name and fails. The forwarding list is comma-separated, and its header names are lowercase.

If Portkey returns this error, your workspace blocks the inline form:

```text theme={"system"}
Inline forward headers are not allowed when block_inline_config is enabled.
Configure forward_headers on a saved provider instead.
```

Use the saved Config setup above. Replace `x-portkey-provider` and `x-portkey-forward-headers` with `x-portkey-config: <portkey-config-id>`.

## If one Portkey key serves other backends

One Portkey key in front of several providers — a LibreChat deployment offering a few backends, say — must not carry the pinned Config as its default. Every request on that key is force-routed to Valar, whatever model it asked for, and the `@slug/` prefix the caller sent is discarded.

**Nothing errors when that happens.** A Valar coding key maps a model string it does not recognise onto the tier your [cohort](/valarcode/routing) resolves to rather than rejecting it, so a request for `@anthropic/eu.anthropic.claude-sonnet-5` or `@z-ai/zai.glm-5` comes back `200`, with a sensible answer, from a Valar-routed model — and on Valar's bill. The symptom is a provider that went quiet, not a failure.

Two ways round it.

**Give ValarCode its own Portkey API key**, with the pinned Config as its default. The shared key keeps its own Config and the two never meet. Take this one unless you have a reason not to.

**Or drop the pin and let each request choose.** A Config can carry the forwarding allowlist and no provider at all:

```json theme={"system"}
{
  "passthrough": true,
  "forward_headers": [
    "x-valar-client-id",
    "x-valar-harness",
    "x-valar-cli-version",
    "anthropic-beta"
  ]
}
```

`passthrough` tells Portkey to resolve the provider from the request rather than from the Config. `@valar/valar-auto` uses Valar's auto-router, `@valar/zai-org/GLM-5.3` selects that model directly, and `@anthropic/…` still reaches Anthropic. The Valar headers are forwarded on whichever hop carries them. A caller that names its provider in the model string, such as LibreChat, needs nothing further.

Claude Code is the exception. It sends `claude-sonnet-…`, with no prefix for Portkey to read, so it has to name the provider itself: add `x-portkey-provider: @valar` to `ANTHROPIC_CUSTOM_HEADERS` alongside `x-portkey-config`. That is inline configuration, which some workspaces block; where it is blocked, give Claude Code its own key.

<Note>
  The unpinned form comes from Portkey's [Config schema](https://portkey.ai/docs/api-reference/inference-api/config-object), and we have not run it end to end ourselves. The pinned Config above is the path we document and test. [Tell us](mailto:support@valarhq.ai) if the unpinned one misbehaves.
</Note>

## Without the Model Catalog

If you would rather not store the Valar key in Portkey, you can pass the upstream host and credential inline instead. This option requires a workspace that allows inline configuration. Drop the `x-portkey-provider: @valar` line and use:

```
x-portkey-api-key: <portkey-api-key>
x-portkey-provider: anthropic
x-portkey-custom-host: https://api.valarhq.ai/v1
x-portkey-forward-headers: Authorization,x-valar-client-id,x-valar-harness,x-valar-cli-version,anthropic-beta
```

Then run `valar claude on --endpoint https://api.portkey.ai --api-key <your-coding-key>` so `ANTHROPIC_AUTH_TOKEN` holds the `vlrcode_…` key. `Authorization` is in the forward list, so Portkey passes that credential through to Valar untouched rather than processing it; `x-portkey-api-key` is what authenticates to Portkey.

The Valar coding key now sits in every engineer's `settings.json` instead of in Portkey. Prefer the Model Catalog path unless you have a reason not to. If `block_inline_config` is enabled, use the Model Catalog and saved Config path above.

## Verify it works

After the first few requests, check:

* **Portkey logs**: the request shows the saved Config ID and provider `valar`, and the response is successful.
* **Valar analytics dashboard**: the engineer appears under their client id (their username by default), and their harness list includes the tool you configured — `claude` for Claude Code.

Portkey's **User** field identifies the Portkey API-key owner. It does not prove that Valar received `X-Valar-Client-Id`. Check Valar analytics for the attribution result.

If usage appears but is not attributed to an engineer, `X-Valar-Client-Id` is not making it through. Check the saved Config's `forward_headers` list first. A missing or misspelled entry is the common cause.

This failure is silent. Portkey's Anthropic transform re-issues the upstream credential as `x-api-key` and adds `anthropic-version` independently of the forwarding allowlist. Authentication can still succeed when the allowlist is wrong. The request returns `200`, the answer is correct, and the only symptom is unattributed Valar usage.

To stop routing through Portkey and restore Claude Code's previous settings:

```bash theme={"system"}
valar claude off
```

`off` restores the whole `settings.json` from its backup, so the Portkey header lines go with it.

## Next steps

<CardGroup cols={2}>
  <Card title="Claude Code" icon="code" href="/valarcode/claude-code">
    The direct connect path and what the CLI writes to `settings.json`.
  </Card>

  <Card title="LiteLLM" icon="arrows-split-up-and-left" href="/valarcode/litellm">
    The same setup for a LiteLLM proxy.
  </Card>

  <Card title="Model routing" icon="route" href="/valarcode/routing">
    How the client id drives cohort assignment and the split.
  </Card>

  <Card title="Analytics" icon="chart-line" href="/valarcode/analytics">
    Where per-engineer attribution shows up.
  </Card>
</CardGroup>
