Skip to content

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, then extend the typed request body with Router's provider_selection. The SDK preserves extra request properties. 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 for the status and code combinations your application should handle.