# TypeScript

> Use the OpenAI JavaScript client with Router in a server-side application.

## Install the client

```bash
pnpm add openai
```

Keep the API key in a server environment. Do not instantiate this client in browser code.

## Create a client

```typescript
import OpenAI from "openai";
import { randomUUID } from "node:crypto";

const client = new OpenAI({
  baseURL: process.env.ROUTER_BASE_URL,
  apiKey: process.env.ROUTER_API_KEY,
  timeout: 45_000,
  maxRetries: 0,
});
```

The base URL must include `/v1`. Setting `maxRetries: 0` keeps retry decisions visible in your own application flow.

## List model editions

```typescript
const models = await client.models.list();

for (const model of models.data) {
  console.log(model.id);
}
```

## Create a completion

```typescript
const requestId = randomUUID(); // Reuse for an uncertain retry or receipt lookup.
const response = await client.chat.completions.create({
  model: "gemma3:1b",
  messages: [
    { role: "system", content: "Answer in plain language." },
    { role: "user", content: "What is edge inference?" },
  ],
  max_tokens: 160,
  temperature: 0.4,
}, {
  headers: { "Idempotency-Key": requestId, "X-Max-Charge-Credits": "5.000000" },
});

console.log(response.choices[0]?.message.content);
console.log(response.usage);
```

## Optionally choose a provider

Review a public offer with [choosing a provider](https://router.optimai.network/docs/choosing-a-provider.md), then extend the typed request body with Router's `provider_selection`. The [SDK preserves extra request properties](https://github.com/openai/openai-node#undocumented-request-params). Replace the synthetic UUID and revision with that offer's current values.

```typescript
type SelectedChatRequest = OpenAI.ChatCompletionCreateParamsNonStreaming & {
  provider_selection: { offer_id: string; pricing_revision: number };
};

const selectedBody: SelectedChatRequest = {
  model: "gemma3:1b",
  messages: [{ role: "user", content: "Explain retrieval briefly." }],
  max_tokens: 160,
  provider_selection: {
    offer_id: "00000000-0000-4000-8000-000000000001",
    pricing_revision: 3,
  },
};
const selectedRequestId = randomUUID();
const selectedResponse = await client.chat.completions.create(selectedBody, {
  headers: {
    "Idempotency-Key": selectedRequestId,
    "X-Max-Charge-Credits": "5.000000",
  },
});
console.log(selectedResponse.choices[0]?.message.content);
```

Omit `provider_selection` for Auto. Router never silently changes a manual selection. A different offer or price revision needs a new request UUID; an uncertain retry keeps the original UUID and unchanged body.

## Stream output

```typescript
const streamRequestId = randomUUID();
const stream = await client.chat.completions.create({
  model: "gemma3:1b",
  messages: [{ role: "user", content: "Write a two-line welcome." }],
  stream: true,
}, {
  headers: { "Idempotency-Key": streamRequestId, "X-Max-Charge-Credits": "5.000000" },
});

let completed = false;

for await (const chunk of stream) {
  const choice = chunk.choices[0];
  process.stdout.write(choice?.delta.content ?? "");
  if (choice?.finish_reason) completed = true;
}

if (!completed) throw new Error("The response stream ended before completion");
```

## Stop receiving from your application

The SDK accepts an abort signal in its request options. Aborting stops local response handling; accepted paid generation and settlement may continue. Check the saved receipt before starting another paid request.

```typescript
const controller = new AbortController();
const cancelRequestId = randomUUID();

const request = client.chat.completions.create(
  {
    model: "gemma3:1b",
    messages: [{ role: "user", content: "Explain inference." }],
  },
  {
    signal: controller.signal,
    headers: { "Idempotency-Key": cancelRequestId, "X-Max-Charge-Credits": "5.000000" },
  },
);

// Abort local receiving when the user leaves. Accepted work may continue and charge.
const response = await request;
```

See [errors](https://router.optimai.network/docs/api/errors.md) for the status and code combinations your application should handle.
