Skip to main content

OpenAI SDK

The gateway is a drop-in replacement for the OpenAI API. Point the official openai TypeScript SDK at the gateway and all requests are proxied to the model provider with automatic memory.

Before you begin

Complete the OpenAI Developer quickstart first. It covers SDK installation, API keys, and the base project setup.

Configure the client

Create an OpenAI client with your gateway API key and base URL.

TypeScriptsrc/gateway.ts
import OpenAI from "openai";

export const client = new OpenAI({
  apiKey: "YOUR_API_KEY",
  baseURL: "https://gateway-api.mastra.ai/v1",
});

All subsequent examples import this client instance from ./gateway.

Chat completions

Send a standard chat completion request. The gateway routes it to the model provider and returns an OpenAI-compatible response.

TypeScriptsrc/chat.ts
import { client } from "./gateway";

const completion = await client.chat.completions.create({
  model: "google/gemini-2.5-flash",
  messages: [
    { role: "user", content: "What is 2+2? Reply with just the number." },
  ],
  max_tokens: 20,
});

console.log(completion.choices[0].message.content);
// "4"

System messages

Set the model's behavior with a system message in the messages array.

const completion = await client.chat.completions.create({
  model: "google/gemini-2.5-flash",
  messages: [
    { role: "system", content: "You are a calculator. Only respond with numbers, no words." },
    { role: "user", content: "What is 10 * 5?" },
  ],
  max_tokens: 100,
});

console.log(completion.choices[0].message.content);
// "50"

Multi-turn conversations

Pass the full conversation history in the messages array so the model retains context across turns.

const completion = await client.chat.completions.create({
  model: "google/gemini-2.5-flash",
  messages: [
    { role: "user", content: "Remember this word: banana" },
    { role: "assistant", content: "Got it, I will remember it." },
    { role: "user", content: "What word did I ask you to remember? Reply with just the word." },
  ],
  max_tokens: 100,
});

console.log(completion.choices[0].message.content);
// "banana"

Streaming

Pass stream: true to receive chunks incrementally.

TypeScriptsrc/stream.ts
import { client } from "./gateway";

const stream = await client.chat.completions.create({
  model: "google/gemini-2.5-flash",
  messages: [
    { role: "user", content: "Count from 1 to 5, separated by commas." },
  ],
  max_tokens: 50,
  stream: true,
});

for await (const chunk of stream) {
  const delta = chunk.choices[0]?.delta?.content;
  if (delta) {
    process.stdout.write(delta);
  }
}
// "1, 2, 3, 4, 5"

Memory with thread and resource IDs

Pass x-thread-id and x-resource-id headers to enable observational memory. The gateway stores observations per thread and injects them as context on subsequent requests.

TypeScriptsrc/memory.ts
import { client } from "./gateway";

// First request: introduce yourself
await client.chat.completions.create(
  {
    model: "google/gemini-2.5-flash",
    messages: [
      { role: "user", content: "My name is Alex and I prefer concise answers." },
    ],
  },
  {
    headers: {
      "x-thread-id": "my-thread-1",
      "x-resource-id": "user-42",
    },
  },
);

// Second request: the gateway remembers
const response = await client.chat.completions.create(
  {
    model: "google/gemini-2.5-flash",
    messages: [
      { role: "user", content: "What is my name?" },
    ],
  },
  {
    headers: {
      "x-thread-id": "my-thread-1",
      "x-resource-id": "user-42",
    },
  },
);

console.log(response.choices[0].message.content);
// "Alex"

Tool calling

Define tools using the standard OpenAI function calling format. The gateway forwards tool definitions to the provider.

TypeScriptsrc/tools.ts
import OpenAI from "openai";
import { client } from "./gateway";

const helloWorldTool: OpenAI.ChatCompletionTool = {
  type: "function",
  function: {
    name: "helloWorld",
    description: "Returns a greeting for the given name",
    parameters: {
      type: "object",
      properties: {
        name: { type: "string", description: "The name to greet" },
      },
      required: ["name"],
    },
  },
};

// Step 1: Send the request with tools
const first = await client.chat.completions.create({
  model: "google/gemini-2.5-flash",
  messages: [
    { role: "user", content: "Use the helloWorld tool to greet Alice." },
  ],
  tools: [helloWorldTool],
  tool_choice: { type: "function", function: { name: "helloWorld" } },
  max_tokens: 200,
});

const toolCall = first.choices[0].message.tool_calls![0];
const args = JSON.parse(toolCall.function.arguments);
// args.name === "Alice"

// Execute the tool locally
const result = { greeting: `Hello, ${args.name}!` };

// Step 2: Send the tool result back
const second = await client.chat.completions.create({
  model: "google/gemini-2.5-flash",
  messages: [
    { role: "user", content: "Use the helloWorld tool to greet Alice." },
    first.choices[0].message,
    {
      role: "tool",
      tool_call_id: toolCall.id,
      content: JSON.stringify(result),
    },
  ],
  tools: [helloWorldTool],
  max_tokens: 200,
});

console.log(second.choices[0].message.content);
// "Hello, Alice!"

Streaming tool calls

Combine stream: true with tools to receive tool call arguments as incremental deltas.

const stream = await client.chat.completions.create({
  model: "google/gemini-2.5-flash",
  messages: [
    { role: "user", content: "Use the helloWorld tool to greet Bob." },
  ],
  tools: [helloWorldTool],
  tool_choice: { type: "function", function: { name: "helloWorld" } },
  max_tokens: 200,
  stream: true,
});

const toolCalls: Record<number, { id?: string; name?: string; args: string }> = {};

for await (const chunk of stream) {
  const delta = chunk.choices[0]?.delta;
  for (const tc of delta?.tool_calls ?? []) {
    const idx = tc.index ?? 0;
    const entry = toolCalls[idx] ?? (toolCalls[idx] = { args: "" });
    if (tc.id) entry.id = tc.id;
    if (tc.function?.name && !entry.name) entry.name = tc.function.name;
    if (tc.function?.arguments) entry.args += tc.function.arguments;
  }
}

const firstCall = toolCalls[0];
console.log(firstCall.name); // "helloWorld"
console.log(JSON.parse(firstCall.args)); // { name: "Bob" }

Responses API

The gateway also proxies the OpenAI Responses API (POST /v1/responses).

TypeScriptsrc/responses.ts
import { client } from "./gateway";

const response = await client.responses.create({
  model: "openai/gpt-5.4-mini",
  input: "What is 3+3? Reply with just the number.",
  max_output_tokens: 10,
});

const message = response.output[0];
if (message.type === "message") {
  const text = message.content[0];
  if (text.type === "output_text") {
    console.log(text.text); // "6"
  }
}

Streaming responses

Pass stream: true to receive response events incrementally.

const stream = await client.responses.create({
  model: "openai/gpt-5.4-mini",
  input: "Count from 1 to 5, separated by commas.",
  max_output_tokens: 50,
  stream: true,
});

for await (const event of stream) {
  if (event.type === "response.output_text.delta") {
    process.stdout.write(event.delta);
  }
}

BYOK pass-through

BYOK is available on the Teams plan and above. To use your own provider API key instead of the gateway's provisioned keys, send both keys:

TypeScriptsrc/gateway.ts
import OpenAI from "openai";

export const client = new OpenAI({
  apiKey: "sk-your-openai-key",    // Your provider key
  baseURL: "https://gateway-api.mastra.ai/v1",
  defaultHeaders: {
    "X-Memory-Gateway-Authorization": "Bearer YOUR_API_KEY",  // msk_ key
  },
});

const completion = await client.chat.completions.create({
  model: "openai/gpt-5.4-mini",
  messages: [
    { role: "user", content: "Hello!" },
  ],
});

The gateway authenticates with the msk_ key, then forwards your provider key to OpenAI. Memory and observational features still work.

  • Features: Observational memory, streaming, BYOK, and gateway tools
  • Models: Supported providers and model routing
  • API reference: Complete endpoint documentation