MainSDK
Search⌘K
Integrations

OpenAI

Use the OpenAI API for MainSDK AI calls and embeddings - API keys, main.yaml, the model allowlist, data-use rules, local testing and swapping.

1 interface1 driverWritten guide
Updated 2026-10-28

OpenAI makes the GPT models and embedding models, and its API is one of the drivers behind ai. Your app calls ai.generate() and ai.embed(); the driver sends the redacted prompt to the OpenAI API. Pick it when your features are tuned for OpenAI models, when you want OpenAI embeddings for search, or as a fallback next to Amazon Bedrock, the default driver.

Interfaces it serves

Pick OpenAI in main.yaml for any of these. Your calls stay the same, and you can swap later.

Before you start

You need an OpenAI platform account with API access.

  1. Create a project and an API key. In the OpenAI platform settings, create a project for your app and a key scoped to it. Use separate keys per environment so you can revoke one at a time.
  2. Set usage limits on the project. MainSDK caps spend in your app; a provider-side limit is a second line of defence.
  3. Review the data controls for your organisation. ai.data requires no training and no retention. Check how OpenAI handles API inputs for your organisation, and whether the retention your main.yaml asks for needs an arrangement with OpenAI.

Store the key in your secrets manager, never in code or a committed env file.

Configure

main.yaml
ai:
  driver: openai
  key: secret:OPENAI_API_KEY         # suggested name; [to be decided] key field name for drivers that need one
  models: [openai/gpt-*, openai/text-embedding-*]   # allowlist
  data: { training: never, retention: zero }
  limits: { per-user: $1/day, total: $500/month }

The key is always a secret: reference; a literal key fails mainsdk check. OpenAI's API host comes from the driver's declared hosts, so you don't add it to network.allow.

Use it

Generate text:

lib/classify-ticket.ts
import { ai } from "mainsdk";

export async function classifyTicket(text: string, user: { id: string }) {
  return ai.generate({
    model: "openai/gpt-4o-mini",               // [to be decided] model identifiers per driver
    feature: "ticket-triage",
    user,
    prompt: `Reply with one word, billing, bug, account or other, for this ticket:\n${text}`,
  });
}

Embed text for search:

lib/embed-products.ts
import { ai } from "mainsdk";

export async function embedProducts(descriptions: string[]) {
  return ai.embed({
    model: "openai/text-embedding-3-small",    // [to be decided] model identifiers per driver
    input: descriptions,
    feature: "product-search",
  });                                          // number[][]
}

Local development and testing

  • No key: set ai.driver: local in main.env.dev.yaml to use the built-in local fake.
  • Real calls in development: use a development key with a low cap in the dev overlay, for example limits: { total: $10/month }.
  • Tests: createTestApp() from @mainsdk/testing runs ai on its in-memory fake with redaction, caps and audit enforced. chaos.on("ai:openai", { outage: true }) simulates an OpenAI outage. The spec's conformance suite runs the OpenAI driver against recorded responses.
lib/classify-ticket.test.ts
import { it } from "node:test";
import { createTestApp, chaos } from "@mainsdk/testing";
import { classifyTicket } from "./classify-ticket";

it("keeps triage working when openai is down", async () => {
  createTestApp({ main: "./main.yaml" });   // with a second driver under fallback.ai
  chaos.on("ai:openai", { outage: true });
  await classifyTicket("I was charged twice", { id: "u_1" });
});

What MainSDK adds on top

  • The API key never in your code, only a secret: reference.
  • Providers, models or account modes that train on or keep prompts are refused; a declaration that doesn't meet ai.data fails mainsdk check with MAIN-AI-0001.
  • Personal data redacted before the model sees it, and restored only inside your app.
  • Spend limits per user, per feature and in total, checked before the call (MAIN-AI-0007 at the limit).
  • Every call, embeddings included, audited with feature, model, tokens and cost, never the prompt.

See AI calls for the full contract.

Limits and notes

  • The OpenAI driver comes with the ai spec (phase 1, milestone 0.3).
  • The name of the key field for keyed AI drivers is not final.
  • How "no retention" is proven for a provider is an open question in the spec.
  • If prompts must stay in a MainSDK region (US, EU or India), check where OpenAI processes data for your organisation, or use Bedrock with a region set.
  • Embeddings from different models are not interchangeable. If you swap drivers, re-embed stored vectors with the new model.
  • ai.raw() returns the OpenAI client for features ai does not wrap. mainsdk check lists every use.

Swap to or from it

AI calls are stateless, so a swap is instant once mainsdk swap has run the contract tests:

main.yaml
ai:
  driver: openai                     # was bedrock
  key: secret:OPENAI_API_KEY         # [to be decided] key field name

Update ai.models to models the new driver serves. Stored embeddings are the one piece of state: vectors made by one model must be rebuilt with the new one before search uses them.

FAQ

Can I use OpenAI for embeddings and another provider for chat? One ai block names one driver. Running two drivers side by side is not in the ai@1 contract; fallback.ai covers failover only.

Where does my OpenAI key go? In your secrets manager, referenced as secret:OPENAI_API_KEY, read on the server only.

Does streaming work? Token-by-token streaming is not in the ai@1 contract yet.

Next

Open questions

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet