OpenAI Integration Guide
What is the OpenAI API?
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.
██████╗ ██████╗ ███████╗███╗ ██╗ █████╗ ██╗
██╔═══██╗██╔══██╗██╔════╝████╗ ██║██╔══██╗██║
██║ ██║██████╔╝█████╗ ██╔██╗ ██║███████║██║
██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██╔══██║██║
╚██████╔╝██║ ███████╗██║ ╚████║██║ ██║██║
╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝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
| Resource | URL |
|---|---|
| OpenAI API Reference | https://developers.openai.com/api/reference/overview |
| Models overview | https://developers.openai.com/api/docs/models |
| Responses vs. Chat Completions | https://developers.openai.com/api/docs/guides/migrate-to-responses |
| Remote MCP tool (Responses API) | https://developers.openai.com/api/docs/guides/tools-connectors-mcp |
| OpenAI Node SDK | https://github.com/openai/openai-node |
| OpenAI Python SDK | https://github.com/openai/openai-python |
Docs domain note:
platform.openai.com/docs/*links still work but now 301-redirect todevelopers.openai.com. Prefer thedevelopers.openai.comcanonical URLs above.
Models at a glance (verify at the models page)
| Tier | Model ids | Use it for |
|---|---|---|
| Frontier | gpt-5.6-sol (alias gpt-5.6) | Hardest reasoning/coding — but Claude is codeAmani's primary here |
| Balanced | gpt-5.6-terra | General structured output at lower cost |
| Budget | gpt-5.6-luna | Cheap-fast extraction, routing, classification |
| Dedicated reasoning | o3, o4-mini | Chain-of-thought tasks (GPT-5 also reasons via effort levels) |
| Embeddings | text-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.
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 yourOPENAI_API_KEY; the key stays server-side andrequire_approvalgates 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:
# 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
npm install openai # v7.x — requires Node >= 22, zod ^3.25 || ^4import 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):
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
pip install openai # v3.x — requires Python >= 3.10from 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.
(a) Structured output — Responses API (recommended)
Use client.responses.parse() with zodTextFormat() from openai/helpers/zod. The parsed object arrives on response.output_parsed, typed as z.infer<typeof Schema>.
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.
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 keepadditionalProperties: false.
Environment Variables
# 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 OpenAIStore 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:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "node scripts/openai-second-opinion.js"
}
]
}
]
}
}scripts/openai-second-opinion.js:
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:
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"'"}'
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.txtCommon Use Cases
| Use Case | Approach |
|---|---|
| Vision / image understanding | Pass image inputs to gpt-5.6 via the Responses API (input_image) |
| Embeddings for code search | text-embedding-3-small on your codebase |
| Fine-tuning for style | Fine-tune a small model (e.g. gpt-5.6-luna) on your code patterns |
| Structured extraction | responses.parse + zodTextFormat on gpt-5.6-terra / luna |
| Cross-model validation | Claude drafts, GPT-5.6 validates |
Troubleshooting
| Issue | Fix |
|---|---|
| 401 Unauthorized | Check OPENAI_API_KEY value and project permissions |
| Model not available | Check model access at developers.openai.com/api/docs/models and your project tier |
output_parsed is null | The model refused or the schema was invalid — inspect response.output for a refusal item |
| Strict-schema error | Every property must be in required; use z.null() unions for optional fields, additionalProperties: false |
| Rate limits | Use exponential backoff; check tier limits at platform.openai.com/settings/limits |
| Azure OpenAI endpoint | Set OPENAI_BASE_URL=https://<resource>.openai.azure.com |