# Model Gateways

Send Codex models through OpenRouter, Vercel AI Gateway, or Cloudflare AI Gateway, with the agent code unchanged.

## Overview

A `ResponsesGateway` points the Codex client at a third-party Responses API. `OpenAiBuilder::responses_gateway` sets the base URL and model ID prefix, and switches the client to HTTPS with full history replay. The agent code that spawns and prompts stays the same.

Responses gateways serve the Codex models Sol, Luna, and Astra. Claude models keep their own login.

nanocode-wasm has no `ResponsesGateway`. A TypeScript session reaches a gateway through its own boundary: `config.endpoint` is the Codex API base URL, and `dispatch` adds the gateway's credentials. Model ID prefixes and provider routing have no TypeScript equivalent.

#### Rust

```rust
use nanocode::codex::{auth::OpenAiAuth, transport::ResponsesGateway};
use nanocode::{Model, OpenAi};

// Codex requests go to OpenRouter with an OpenRouter key
let openai = OpenAi::builder(OpenAiAuth::api_key(std::env::var(
    "OPENROUTER_API_KEY",
)?))
.responses_gateway(ResponsesGateway::openrouter())
.model(Model::Luna)
.build()?;
```

#### TypeScript

```ts
// Codex requests go to Cloudflare AI Gateway, which holds the provider
// key
await runtime.open(
  {
    endpoint: `https://gateway.ai.cloudflare.com/v1/${account}/${gateway}/openai`,
    model: 'gpt-6-luna',
    provider: 'codex',
    scope: 'owner',
    session_id: sessionUuid(),
    subscription: false,
  },
  {
    dispatch: (request, signal) =>
      fetch(request.endpoint, {
        body: request.body as Uint8Array<ArrayBuffer>,
        headers: [
          ...request.headers,
          [
            'cf-aig-authorization',
            `Bearer ${process.env.CF_AIG_TOKEN}`,
          ],
        ],
        method: 'POST',
        redirect: 'error',
        signal,
      }),
  },
)
```

## Walkthrough

The steps below run one Codex agent through OpenRouter from Rust. `crates/nanocode/examples/responses_gateway.rs` runs the same program for every gateway.

### Build the Client

`OpenAiAuth::api_key` signs requests with the gateway's key. `app_title` sets the application name that OpenRouter shows.

```rust title="src/main.rs"
use nanocode::codex::{auth::OpenAiAuth, transport::ResponsesGateway};
use nanocode::{Codex, Model, Nanocode, OpenAi, Tools};

type Result<T> =
    std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

#[tokio::main]
async fn main() -> Result<()> {
    // Read the key from the environment and route Codex through
    // OpenRouter
    let openai = OpenAi::builder(OpenAiAuth::api_key(std::env::var(
        "OPENROUTER_API_KEY",
    )?))
    .responses_gateway(
        ResponsesGateway::openrouter().app_title("my-app"),
    )
    .model(Model::Luna)
    .build()?;
```

### Run the Agent

Pass the client to the runtime builder as usual.

```rust title="src/main.rs"
    // 1. The runtime and agent code do not depend on the gateway
    let runtime = Nanocode::builder()
        .openai(openai)
        .tools(Tools::builder().without_defaults().build()?)
        .without_cua()
        .runner_management(false)
        .build_async()
        .await?;

    // 2. Spawn a Codex agent and run one turn
    let (agent, _events) = runtime
        .spawn()
        .backend::<Codex>()
        .model(Model::Luna)
        .build()
        .await?;
    let turn = agent
        .prompt("Reply with one sentence about gateways.")
        .await?;
    println!("{}", turn.result().await?.final_message());

    runtime.shutdown().await?;
    Ok(())
}
```

## Recipes

### Pick a Gateway

Each gateway has a constructor and its own credentials. The example program reads these environment variables.

| Gateway                | Constructor                                              | Credentials                                                              |
| ---------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------ |
| OpenRouter             | `ResponsesGateway::openrouter()`                         | `OPENROUTER_API_KEY`                                                     |
| Vercel AI Gateway      | `ResponsesGateway::vercel()`                             | `AI_GATEWAY_API_KEY` or `VERCEL_OIDC_TOKEN`                              |
| Cloudflare unified API | `ResponsesGateway::cloudflare(account)`                  | `CLOUDFLARE_API_TOKEN`, with an optional `.gateway_id(id)`               |
| Cloudflare AI Gateway  | `ResponsesGateway::cloudflare_gateway(account, gateway)` | `OPENAI_API_KEY`, or `CF_AIG_TOKEN` with `OpenAiAuth::gateway_managed()` |

```rust
use nanocode::OpenAi;
use nanocode::codex::{auth::OpenAiAuth, transport::ResponsesGateway};

// Cloudflare AI Gateway with provider keys stored in the gateway
let gateway =
    ResponsesGateway::cloudflare_gateway(account_id, gateway_id)
        .gateway_token(std::env::var("CF_AIG_TOKEN")?);
let openai = OpenAi::builder(OpenAiAuth::gateway_managed())
    .responses_gateway(gateway)
    .build()?;
```

### Route Providers

`provider_routing` passes a JSON object to the gateway: OpenRouter's provider preferences or Vercel's `providerOptions.gateway`. `base_url` replaces the gateway's base URL, for example with a custom domain.

```rust
use serde_json::json;

// OpenRouter provider preferences in OpenRouter's format, sent through
// a custom domain
let gateway = ResponsesGateway::openrouter()
    .provider_routing(json!({ "order": ["openai"] }))
    .base_url("https://llm.example.com/api/v1");
```

### Store Responses

Requests default to `store: false` for every authentication mode and gateway. `.store(true)` lets an API-key session continue stored responses where the endpoint supports them: the OpenAI API, Vercel, and Cloudflare's OpenAI proxy.

```rust
// Continue stored responses with previous_response_id on Vercel
let openai = OpenAi::builder(OpenAiAuth::api_key(key))
    .responses_gateway(ResponsesGateway::vercel())
    .store(true)
    .build()?;
```

The builder rejects `.store(true)` for ChatGPT authentication, OpenRouter, and Cloudflare's unified API.

### Use the CLI

`--gateway` routes Codex models through a gateway for one `nc` process. The CLI reads keys from the provider's environment variable and never saves them.

```bash
# 1. OpenRouter
OPENROUTER_API_KEY=... nc --gateway openrouter -m gpt-6-luna

# 2. Cloudflare AI Gateway
nc --gateway cloudflare-gateway --cloudflare-account ACCOUNT --cloudflare-gateway GATEWAY
```

`--gateway-base-url` overrides the gateway base URL. The gateway values are `openrouter`, `vercel`, `cloudflare`, and `cloudflare-gateway`.

### Know the Limits

A Responses gateway changes some built-in behavior:

* **Built-in tools:** Web search and image generation return an unsupported result without sending a request.
* **Other models:** GLM, Kimi, and MiMo use `ChatGateway`, which is native only and cannot combine with a Responses gateway.
* **Compaction:** Codex inline compaction works only through Cloudflare's provider-native proxy.

## More

- [MPP Payments](/docs/guides/mpp-payments): Pay for model requests through an HTTP layer.

- [Agents and Turns](/docs/guides/agents): Build a runtime and run a first turn.

- [ResponsesGateway](/docs/api/rust/ResponsesGateway): Reference for gateway constructors and options.

- [OpenAi](/docs/api/rust/OpenAi): Reference for the Codex client builder.
