OpenAI SDK (Node)

Drop-in. Point baseURL at odnoga with the /v1 suffix.

Setup

import OpenAI from 'openai';

export const ai = new OpenAI({
  apiKey: process.env.AIROUTER_API_KEY!,
  baseURL: `${process.env.AIROUTER_BASE_URL}/v1`,
  defaultHeaders: {
    'x-airouter-end-user': process.env.USER_ID ?? 'anonymous',
  },
});

Per-request end user (preferred — defaultHeaders is global):

await ai.chat.completions.create(
  { model: 'gpt-4o-mini', messages: [{ role: 'user', content: 'hi' }] },
  { headers: { 'x-airouter-end-user': userId } },
);

Read response headers

const res = await ai.chat.completions.create(
  { model: 'gpt-4o-mini', messages: [...] },
  { headers: { 'x-airouter-end-user': userId } },
).withResponse();

console.log(res.response.headers.get('x-airouter-request-id'));
console.log(res.response.headers.get('x-airouter-cost-usd'));
const completion = res.data;

Streaming

const stream = await ai.chat.completions.create({
  model: 'gpt-4o-mini', stream: true,
  messages: [{ role: 'user', content: 'count to 5' }],
}, { headers: { 'x-airouter-end-user': userId } });

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
}

Managed prompt (use body)

The OpenAI SDK doesn't know about prompt. Inject it via the raw body:

await ai.chat.completions.create({
  model: 'gpt-4o-mini',
  messages: [],
  // @ts-expect-error – odnoga extension
  prompt: { slug: 'welcome-email', label: 'production', variables: { name: 'Ada' } },
}, { headers: { 'x-airouter-end-user': userId } });

For full type-safety + _meta, use @odnoga/node instead.