> This page is for Gateway.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.merge.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.merge.dev/_mcp/server.

# Pi

> Connect Pi to Gateway with a custom provider in models.json, route through a model alias, or generate the model list from the catalog.

[Pi](https://pi.dev) builds its model picker only from `~/.pi/agent/models.json`, never querying Gateway, so new catalog models appear only once you add them or regenerate the file. Use an alias entry to let a routing policy choose, or list models by hand or from the catalog. Shared setup: [Building with coding agents](/merge-gateway/coding-agents/overview).

## Configure Pi

Export `MERGE_GATEWAY_API_KEY`, then add a provider with an alias entry and a fixed model:

**`~/.pi/agent/models.json`**

```json title="~/.pi/agent/models.json"
{
  "providers": {
    "merge-gateway": {
      "name": "Merge Gateway",
      "baseUrl": "https://api-gateway.merge.dev/v1/openai",
      "api": "openai-completions",
      "apiKey": "$MERGE_GATEWAY_API_KEY",
      "compat": { "supportsReasoningEffort": false, "sendSessionAffinityHeaders": true },
      "models": [
        {
          "id": "@alias/coding-heavy",
          "name": "Gateway coding policy",
          "contextWindow": 200000,
          "maxTokens": 32000,
          "input": ["text", "image"]
        },
        {
          "id": "anthropic/claude-sonnet-5",
          "name": "Claude Sonnet 5",
          "reasoning": true,
          "contextWindow": 1000000,
          "maxTokens": 64000,
          "input": ["text", "image"]
        }
      ]
    }
  }
}
```

`apiKey` reads your exported key with Pi's `$VAR` syntax. Pi reloads the file whenever you open the picker: run `pi`, then `/model`, type `merge`, and select an entry.

## Gotchas

* Set `contextWindow`, `maxTokens`, and `input` on every entry, or Pi assumes 128K context, 16K output, and text only
* For an alias or `default_routing` entry, set `contextWindow` to the smallest context window among its routable models. Pi compacts against it, so a larger value can overflow the serving model.
* Gateway clamps `maxTokens` to the serving model's output limit rather than letting the vendor reject it, so one value works across a policy's models
* `supportsReasoningEffort: false` stops Pi sending `reasoning_effort`, which some vendors reject. `"reasoning": true` labels reasoning models in the picker.
* `sendSessionAffinityHeaders: true` groups each Pi session into one thread in Logs

## Generate the model list from the catalog

#### Script: build models.json from GET /v1/models

This writes every chat model Gateway offers you, with limits, modalities, and prices from each model's first listed vendor, alias entry first. It merges into your `models.json`, keeping other providers and replacing the `merge-gateway` list.

```bash
mkdir -p ~/.pi/agent
[ -f ~/.pi/agent/models.json ] || echo '{"providers":{}}' > ~/.pi/agent/models.json

curl -s "https://api-gateway.merge.dev/v1/models?limit=500" \
  -H "Authorization: Bearer $MERGE_GATEWAY_API_KEY" \
| jq '{
  providers: {
    "merge-gateway": {
      name: "Merge Gateway",
      baseUrl: "https://api-gateway.merge.dev/v1/openai",
      api: "openai-completions",
      apiKey: "$MERGE_GATEWAY_API_KEY",
      compat: { supportsReasoningEffort: false, sendSessionAffinityHeaders: true },
      models: ([
        { id: "@alias/coding-heavy", name: "Gateway coding policy",
          contextWindow: 200000, maxTokens: 32000, input: ["text", "image"] }
      ] + [
        .data[]
        | . as $m
        | (.vendors | to_entries[0].value) as $v
        | select($v.capabilities.output | index("text"))
        | {
            id: $m.model,
            name: $m.display_name,
            contextWindow: $v.context_window,
            maxTokens: $v.max_output_tokens,
            input: [$v.capabilities.input[] | select(. == "text" or . == "image")],
            reasoning: $v.capabilities.supports_reasoning,
            cost: { input: $v.pricing.input_per_million, output: $v.pricing.output_per_million }
          }
      ])
    }
  }
}' > /tmp/merge-gateway-models.json

jq -s '.[0] * .[1]' ~/.pi/agent/models.json /tmp/merge-gateway-models.json \
  > /tmp/pi-models-merged.json && mv /tmp/pi-models-merged.json ~/.pi/agent/models.json
```

`limit=500` is the largest page `/v1/models` returns. If the response has `"has_more": true`, pass its `next_cursor` as `?cursor=` for the rest.

## Next steps

#### [Building with coding agents](/merge-gateway/coding-agents/overview)

Keys, models, aliases, and project scoping

#### [Model aliases](/merge-gateway/routing/model-aliases)

Change Pi's model without editing models.json