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

# Node.js SDK

> Install, initialize, and attribute calls with the Orbitrage Node / TypeScript SDK.

The Node SDK mirrors the Python one: it points your OpenAI client at the
Orbitrage gateway and tags every request with a trace id. **Zero dependencies**,
and works in Node, Bun, Deno, Cloudflare Workers, and Next.js.

Orbitrage is **OpenAI-format only** — to use Claude, Gemini, Grok, etc., just
name the model.

<CardGroup cols={2}>
  <Card title="npm" icon="npm" href="https://www.npmjs.com/package/orbitrage">
    `npm install orbitrage`
  </Card>

  <Card title="License" icon="scale-balanced">
    Apache-2.0 · Node >= 18
  </Card>
</CardGroup>

## Install

```bash theme={null}
npm install orbitrage@latest
```

`openai` (>=4) is the only peer you need:

```bash theme={null}
npm install orbitrage@latest openai
```

## Initialize

Call `init()` **once, at the very top of your program, before importing your LLM
client.** It returns a promise — `await` it.

```typescript theme={null}
import { orbitrage } from "orbitrage";
await orbitrage.init({ apiKey: process.env.ORBITRAGE_API_KEY });

import OpenAI from "openai";
const client = new OpenAI();          // baseURL + trace headers set for you
await client.chat.completions.create({
  model: "minimax-m3",
  messages: [{ role: "user", content: "hi" }],
});
```

You can import the named functions instead of the namespace:

```typescript theme={null}
import { init, setUser } from "orbitrage";
await init({ apiKey: process.env.ORBITRAGE_API_KEY });
```

### What `init()` does

1. Generates a stable **trace id** and adds it to every request as
   `x-orbitrage-run-id`.
2. Installs a global `fetch` interceptor that injects trace + user headers on any
   request to an Orbitrage host. This is the primary mechanism and works even in
   frozen-ESM environments like Next.js.
3. Best-effort patches the `OpenAI` constructor and sets `OPENAI_BASE_URL` so
   no-arg clients point at the gateway. **Your explicit `baseURL` or `apiKey`
   always wins.**

It loads no OTel/Traceloop and exports nothing.

### `OrbitrageConfig`

<ParamField path="apiKey" type="string" required>
  Your `orb_` key. Falls back to `ORBITRAGE_API_KEY`.
</ParamField>

<ParamField path="userId" type="string">
  End-user id to attribute calls to (sets `x-orbitrage-end-user-id`).
</ParamField>

<ParamField path="baseUrl" type="string">
  Override the gateway URL. Defaults to `https://api.orbitrage.ai/v1` (or
  `ORBITRAGE_BASE_URL`).
</ParamField>

<ParamField path="enabled" type="boolean" default="true">
  Set `false` to make `init()` a no-op.
</ParamField>

<ParamField path="quiet" type="boolean" default="false">
  Suppress the startup log line.
</ParamField>

<Note>
  `init()` is idempotent. Legacy v0.4 options (`disableBatch`, `captureContent`,
  `instruments`, …) are accepted and ignored.
</Note>

## Per-end-user attribution

```typescript theme={null}
// One user for the whole process:
await orbitrage.init({ apiKey, userId: currentUser.id });

// Switch users per request:
orbitrage.setUser(currentUser.id);

// Or scope to one block of work (restores the previous user after):
await orbitrage.withAssociationProperties({ user_id: currentUser.id }, async () => {
  // calls in here are tagged with this user
});
```

## Forcing a model

```typescript theme={null}
// Pin a model (no scoring):
await client.chat.completions.create({ model: "glm-5.2", messages: [...] });

// Route to the cheapest capable model:
await client.chat.completions.create({ model: "auto", messages: [...] });
```

<Note>
  Pinning a frontier model (`claude-*`, `gpt-*`, `gemini-*`, `grok-*`) requires an
  enabled key for that vendor — the call runs on your provider account and
  Orbitrage bills \$0. Without one it throws a `403` with code `byok_key_required`.
  See [BYOK](/concepts/byok).
</Note>

## API surface

| Symbol                                 | Purpose                                             |
| -------------------------------------- | --------------------------------------------------- |
| `init(config)`                         | Set up the SDK (async).                             |
| `setUser(userId \| null)`              | Bind/unbind the end-user id for subsequent clients. |
| `withAssociationProperties(props, fn)` | Scoped user binding around an async block.          |
| `flush(timeoutMs?)` / `shutdown()`     | No-ops — the gateway persists synchronously.        |
| `workflow`, `task`, `tool`, `agent`    | No-op decorators kept for v0.4 compatibility.       |

## Using a framework?

LangChain, the Vercel AI SDK, Mastra, and others use the OpenAI client (or
`fetch`) under the hood, so they're captured transparently. See
[Integrations](/integrations/openai).
