---
title: "Codex SDK — run it on your company's accounts · Selan"
description: "Run the Codex SDK on your company's accounts: install @openai/codex-sdk, set a base URL and token, start a thread, sandbox the agent, or call Responses."
url: https://selan.ai/docs/codex-sdk
updated: 2026-10-13T17:34:17+00:00
---

Build

Docs · Codex SDK

# Codex SDK on your company's accounts

The Codex SDK runs a whole Codex session from your code — it reads the repository, runs commands and edits files. Pointing it at Selan is a base URL and a token. For running Codex at a terminal, start at [the Codex page](https://selan.ai/docs/codex).

## How do I install the Codex SDK?

```bash
npm install @openai/codex-sdk
```

**Two different things share the name Codex.** This SDK drives the agent. If you only want one answer from a model — a classification, a summary — you want the Responses format instead, which is the [last section](#responses) and needs no SDK of ours. And Anthropic-shaped code belongs on [the Claude Agent SDK page](https://selan.ai/docs/claude-agent-sdk); the gateway serves both formats, so there is nothing to gain by rewriting one into the other.

## How do I point the Codex SDK at Selan?

The constructor takes both values. The base URL carries the `/openai/v1` namespace, which names the format the SDK speaks:

```typescript
import { Codex } from "@openai/codex-sdk"

const codex = new Codex({
  baseUrl: "https://gw.selan.ai/openai/v1",
  apiKey: process.env.SELAN_TOKEN,
})
```

Mint the token in Selan under [**Settings → API tokens**](https://control.selan.ai/settings/access). It spends as itself, under an address like `nightly-triage@tokens.selan.ai`, so what the agent costs shows up as the agent in Usage rather than on whoever set it up.

**The base URL has to end at `/openai/v1`.** The SDK appends `/responses` to whatever you give it. Drop the version segment and every call lands on a path the gateway does not serve.

## How do I start a Codex SDK thread?

A thread is one session; `run` is one turn in it. Call it again on the same thread and the agent keeps its context:

```typescript
const thread = codex.startThread({
  model: "gpt-5.6-sol",
  workingDirectory: "/tmp/scratch",
  sandboxMode: "workspace-write",
  approvalPolicy: "never",
  skipGitRepoCheck: true,
})

const result = await thread.run("Fix the failing test in src/money.test.js")

console.log(result.finalResponse)
for (const item of result.items ?? []) {
  if (item.type === "command_execution") console.log("ran:", item.command)
  if (item.type === "file_change") console.log("changed:", item.changes.map((c) => c.path))
}
```

`finalResponse` is what the agent said. `items` is what it did — a typed record of every command it ran and every file it patched, which is how you check the work rather than trusting the reply.

## What may the agent do on disk?

Two settings decide that, and they interact in a way worth knowing before the first unattended run.

**`sandboxMode`** is what the agent may touch. `read-only` lets it look and nothing else; `workspace-write` lets it edit inside `workingDirectory`.

**`approvalPolicy`** is what happens when the sandbox refuses something. The default asks a human. With nobody there to answer, `never` is the setting that works: the request is refused, the agent is told, and it adapts.

**Leave the approval policy at its default and edits are reported but never written.** The turn comes back with `file_change` items naming files that do not exist on disk, because the write was waiting on an approval nobody gave. Nothing errors, and the run looks like it worked.

`skipGitRepoCheck` is for a scratch directory that is not a repository. In a real checkout, leave it off and let the agent see the git history.

## Can I run Codex headless without writing code?

If a shell command is enough, `selan codex exec` runs the same kind of session with nobody watching:

```bash
export SELAN_TOKEN=selanct_...
selan codex exec "update the changelog for the last ten commits"
```

It is the same launch as an interactive `selan codex`: same provider block, same company models, same limits. No sign-in step, because the token is in the environment. Setup is on [the Codex page](https://selan.ai/docs/codex).

## How do I get one answer instead of an agent?

`POST /openai/v1/responses` against the gateway. The body is the one you would send to OpenAI directly — the [Responses API reference](https://platform.openai.com/docs/api-reference/responses) documents every field, and none of them mean anything different here:

```bash
curl https://gw.selan.ai/openai/v1/responses \
  -H "Authorization: Bearer $SELAN_TOKEN" \
  -H "content-type: application/json" \
  -d '{
    "model": "gpt-5.6-sol",
    "input": "Summarise this changelog in one sentence."
  }'
```

The official OpenAI SDKs take the same base URL and your Selan token as the key:

```typescript
import OpenAI from "openai"

const client = new OpenAI({
  baseURL: "https://gw.selan.ai/openai/v1",
  apiKey: process.env.SELAN_TOKEN,
})

const response = await client.responses.create({
  model: "gpt-5.6-sol",
  input: "Summarise this changelog in one sentence.",
})
```

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://gw.selan.ai/openai/v1",
    api_key=os.environ["SELAN_TOKEN"],
)

response = client.responses.create(
    model="gpt-5.6-sol",
    input="Summarise this changelog in one sentence.",
)
```

No provider key anywhere in those. The gateway authenticates you, leases one of your company's connected credentials for the length of the request, streams the answer straight through, and releases it afterwards.

Load the token from wherever you keep secrets rather than committing it. Both examples read it from the environment for that reason.

## Which models can the Codex SDK use?

Ask the gateway which ones this token may name. Under the OpenAI namespace the list comes back in the shape a Responses client expects:

```bash
curl https://gw.selan.ai/openai/v1/models \
  -H "Authorization: Bearer $SELAN_TOKEN"
```

Your company might serve one of these through OpenAI directly or through another connected account, and which credential is spent is decided per request. The id you name is the same either way, so the list is the thing to trust rather than a guess about who is behind it.

## What does my company see?

The same as every other Selan client. Each request is metered against the token that made it, the company-wide per-user spend limit applies, and it appears in Logs under its own address.

One thread is many requests rather than one, so an agent that fails to terminate keeps spending until something stops it. Set a budget on the token before you leave one running unattended.

## Related

-   [How Claude Code and Codex run without API keys on laptops](https://selan.ai/guides/claude-code-without-api-keys): why the token is a Selan one, not an OpenAI key.
-   [Codex](https://selan.ai/docs/codex): the same agent at a terminal, with `selan codex`.
-   [Claude Agent SDK](https://selan.ai/docs/claude-agent-sdk): the Claude Code counterpart of this SDK.
