← Back to dashboard

OpenAI Integration Guide

What is the OpenAI API?

The real model

Multi-modal stateless API with tools, structured outputs, and a realtime channel.

GPT-5 / GPT-4o / o-series live behind a single API. Use `response_format: { type: "json_schema", schema: ... }` for guaranteed-shape outputs — no Zod re-parsing. Tools are first-class: `tools: [{ type: "function", function: {...} }]` with parallel calls. The Realtime API streams bidirectional audio over WebRTC/WebSocket for voice agents. For multi-step jobs, the Assistants API gives you durable threads + file search. For codeAmani, OpenAI is the structured-output and function-calling specialist; Claude takes the harder reasoning.

Five OpenAI primitives

Same API surface across models. Pick the right tier and the right output mode.

Text
 ██████╗ ██████╗ ███████╗███╗   ██╗ █████╗ ██╗
██╔═══██╗██╔══██╗██╔════╝████╗  ██║██╔══██╗██║
██║   ██║██████╔╝█████╗  ██╔██╗ ██║███████║██║
██║   ██║██╔═══╝ ██╔══╝  ██║╚██╗██║██╔══██║██║
╚██████╔╝██║     ███████╗██║ ╚████║██║  ██║██║
 ╚═════╝ ╚═╝     ╚══════╝╚═╝  ╚═══╝╚═╝  ╚═╝╚═╝

OpenAI Integration Guide

Focus: Integrating OpenAI models and APIs alongside Claude Code workflows — dual-provider pipelines, the Responses API's built-in MCP tool, and cross-model automation.

Overview

Claude Code and OpenAI are complementary. OpenAI's models — the GPT-5.6 family (gpt-5.6, with the sol / terra / luna variants) plus the older GPT-5, GPT-4.1, GPT-4o and o-series models — are consumed inside Claude Code via the openai SDK. New work should target the Responses API (client.responses), which OpenAI now recommends as the default surface for all new projects; Chat Completions (client.chat.completions) remains fully supported. This lets you build hybrid workflows — e.g., route code and reasoning tasks to Claude, structured-extraction and vision tasks to GPT-5.6 — all from one Claude Code session.

Here is the simple mental model of where OpenAI sits as the secondary provider — reach for it when structured output or function calling matters, while Claude stays primary for reasoning.

Official Documentation

Docs domain note: platform.openai.com/docs/* links still work but now 301-redirect to developers.openai.com. Prefer the developers.openai.com canonical URLs above.


Models at a glance (verify at the models page)

TierModel idsUse it for
Frontiergpt-5.6-sol (alias gpt-5.6)Hardest reasoning/coding — but Claude is codeAmani's primary here
Balancedgpt-5.6-terraGeneral structured output at lower cost
Budgetgpt-5.6-lunaCheap-fast extraction, routing, classification
Dedicated reasoningo3, o4-miniChain-of-thought tasks (GPT-5 also reasons via effort levels)
Embeddingstext-embedding-3-small (1536), text-embedding-3-large (3072)RAG / semantic search

GPT-5.6 models take a reasoning: { effort: "low" | "medium" | "high" | ... } control instead of a separate reasoning model. Pin a dated snapshot (e.g. gpt-5.6-2026-…) for reproducible CI; use the floating alias for product code.


MCP integration — the Responses API mcp tool

OpenAI adopted the Model Context Protocol; the integration point is a built-in tool on the Responses API. You give a GPT-5 model the URL of a remote MCP server and it calls that server's tools itself — no separate account-bridge package needed.

TypeScript
import OpenAI from "openai";

const client = new OpenAI();

const resp = await client.responses.create({
  model: "gpt-5.6",
  tools: [
    {
      type: "mcp",
      server_label: "dmcp",
      server_description: "A dice-rolling MCP server.",
      server_url: "https://dmcp-server.deno.dev/mcp",
      require_approval: "never", // approve tool calls in production instead
    },
  ],
  input: "Roll 2d4+1",
});

console.log(resp.output_text);

Direction of travel: this makes the OpenAI model an MCP client. To go the other way — expose your own resources to Claude Code as MCP tools — build a standard MCP server (see the repo's mcp-server/ guide), not an OpenAI-specific bridge. Never hand a third-party remote MCP server your OPENAI_API_KEY; the key stays server-side and require_approval gates tool execution.

Full guide: https://developers.openai.com/api/docs/guides/tools-connectors-mcp


Calling OpenAI from a Claude Code session

The openai Python package's legacy openai api … CLI has been removed from the docs; the SDK is the supported interface. For a quick one-off from a Claude Code shell, hit the Responses endpoint directly:

Bash
# Second opinion from GPT-5.6 without leaving the session
curl https://api.openai.com/v1/responses \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-5.6","input":"Review this code for memory leaks: ..."}'

The reply text is at .output_text in the JSON response.


OpenAI SDK Integration

You are about to wire up the core request flow — here is how a Responses call travels from your app through the SDK to the model and back.

Node.js / TypeScript

Bash
npm install openai   # v7.x — requires Node >= 22, zod ^3.25 || ^4
TypeScript
import OpenAI from "openai";

const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

// Responses API (recommended default)
const response = await client.responses.create({
  model: "gpt-5.6",
  instructions: "You are a TypeScript expert.",
  input: "Convert this class to use composition over inheritance.",
});

console.log(response.output_text);

Chat Completions is still supported if you need that shape (e.g. cross-provider code via the Vercel AI SDK):

TypeScript
const completion = await client.chat.completions.create({
  model: "gpt-5.6",
  messages: [
    { role: "system", content: "You are a TypeScript expert." },
    { role: "user", content: "Convert this class to use composition over inheritance." },
  ],
});
console.log(completion.choices[0].message.content);

Python

Bash
pip install openai   # v3.x — requires Python >= 3.10
Python
from openai import OpenAI

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

response = client.responses.create(
    model="gpt-5.6",
    input="Generate unit tests for this function.",
)
print(response.output_text)

Structured output & function calling

This is the reason OpenAI earns its place as the secondary provider. When you need the model to return data your code can trust — not prose you have to regex — reach for Structured Outputs. With a strict JSON schema the model is guaranteed to emit JSON that matches, and the SDK hands you a fully typed object — no JSON.parse, no validation boilerplate. Structured Outputs work on GPT-4o (2024-08-06+) and every GPT-5 model, so with gpt-5.6 the old snapshot caveat is a non-issue.

Here is the flow: you define a Zod schema, the SDK ships it as a strict JSON schema, the model is constrained to match, and you get a typed object back.

Use client.responses.parse() with zodTextFormat() from openai/helpers/zod. The parsed object arrives on response.output_parsed, typed as z.infer<typeof Schema>.

TypeScript
import OpenAI from "openai";
import { zodTextFormat } from "openai/helpers/zod";
import { z } from "zod";

const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

// e.g. extract structured order data from a free-text M-Pesa SMS
const OrderExtraction = z.object({
  amount_kes: z.number().int(),       // Daraja amounts are integer KES
  phone: z.string(),                   // normalise to 254XXXXXXXXX downstream
  reference: z.string(),
  confidence: z.enum(["high", "medium", "low"]),
});

const response = await client.responses.parse({
  model: "gpt-5.6",
  input: [
    { role: "system", content: "Extract the payment fields from the message." },
    { role: "user", content: "Got KES 1500 from 0712345678 ref INV-204" },
  ],
  text: { format: zodTextFormat(OrderExtraction, "order_extraction") },
});

const order = response.output_parsed;
if (order) {
  console.log(order.amount_kes, order.phone, order.confidence); // fully typed
}

(b) Tool / function call — Responses API

Declare function tools with the flat Responses shape (type: "function" at the top level, strict: true). The model returns function_call items in response.output; each carries name, a call_id, and arguments as a JSON string.

TypeScript
import OpenAI from "openai";

const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

const response = await client.responses.create({
  model: "gpt-5.6",
  input: "Charge 0712345678 KES 500 for order INV-9",
  tools: [
    {
      type: "function",
      name: "initiate_stk_push",
      description: "Start an M-Pesa STK push.",
      strict: true,
      parameters: {
        type: "object",
        additionalProperties: false,
        required: ["amount_kes", "phone", "account_ref"],
        properties: {
          amount_kes: { type: "integer" },
          phone: { type: "string" },
          account_ref: { type: "string" },
        },
      },
    },
  ],
});

for (const item of response.output) {
  if (item.type === "function_call" && item.name === "initiate_stk_push") {
    const args = JSON.parse(item.arguments) as {
      amount_kes: number; phone: string; account_ref: string;
    };
    // now call your real lib/mpesa-stk.ts with typed, schema-validated args
    console.log(args.amount_kes, args.phone, args.account_ref);
  }
}

(c) Chat Completions equivalent (still supported)

If you're on the Chat Completions shape, the helpers are zodResponseFormat() (whole-response schema) and zodFunction() (tool arguments) via client.chat.completions.parse(); results land on choices[0].message.parsed and tool_calls[].function.parsed_arguments. See examples/structured-output.ts.

Gotcha: every field in a strict schema is required by default. To make a field optional, model it as z.union([T, z.null()]) (nullable) rather than .optional() — strict mode does not allow omitted keys — and keep additionalProperties: false.


Environment Variables

Bash
# Required
OPENAI_API_KEY=sk-proj-...

# Optional
OPENAI_ORG_ID=org-...
OPENAI_PROJECT_ID=proj_...
OPENAI_BASE_URL=https://api.openai.com/v1    # default; change for Azure OpenAI

Store in .env and load with dotenv or python-dotenv. The key is server-side only — never ship it to the browser.


Automation Workflows

Dual-Provider Review Hook

Use a Claude Code Stop hook to send the session summary to OpenAI for a cross-model review:

.claude/settings.json:

JSON
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "node scripts/openai-second-opinion.js"
          }
        ]
      }
    ]
  }
}

scripts/openai-second-opinion.js:

JavaScript
import OpenAI from "openai";
import { readFileSync } from "fs";

const client = new OpenAI();
const sessionLog = readFileSync(".claude/session.log", "utf-8");

const result = await client.responses.create({
  model: "gpt-5.6",
  instructions: "Review this Claude Code session for potential issues.",
  input: sessionLog,
});

console.log("GPT-5.6 review:", result.output_text);

Slash Command: Route to GPT-5.6

.claude/commands/gpt.md:

Markdown
Use the Bash tool to call OpenAI's Responses API with this prompt: $ARGUMENTS

Command:
```bash
curl https://api.openai.com/v1/responses \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-5.6","input":"'"$ARGUMENTS"'"}'
Text

Usage: `/project:gpt "What are the tradeoffs of this architecture?"`

### CI/CD: OpenAI Code Quality Gate

```yaml
# .github/workflows/ai-review.yml
name: AI Code Review
on: [pull_request]

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Get diff
        run: git diff origin/main...HEAD > diff.txt
      - name: OpenAI review
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: |
          pip install openai
          python scripts/review.py diff.txt

Common Use Cases

Use CaseApproach
Vision / image understandingPass image inputs to gpt-5.6 via the Responses API (input_image)
Embeddings for code searchtext-embedding-3-small on your codebase
Fine-tuning for styleFine-tune a small model (e.g. gpt-5.6-luna) on your code patterns
Structured extractionresponses.parse + zodTextFormat on gpt-5.6-terra / luna
Cross-model validationClaude drafts, GPT-5.6 validates

Troubleshooting

IssueFix
401 UnauthorizedCheck OPENAI_API_KEY value and project permissions
Model not availableCheck model access at developers.openai.com/api/docs/models and your project tier
output_parsed is nullThe model refused or the schema was invalid — inspect response.output for a refusal item
Strict-schema errorEvery property must be in required; use z.null() unions for optional fields, additionalProperties: false
Rate limitsUse exponential backoff; check tier limits at platform.openai.com/settings/limits
Azure OpenAI endpointSet OPENAI_BASE_URL=https://<resource>.openai.azure.com