| name | imagen |
| description | Use when calling the OpenAI Responses API for image generation via tool_choice, including payload structure, SSE streaming, multi-turn editing, and error handling for third-party gateways |
OpenAI Responses API — Image Generation
Overview
The OpenAI Responses API uses a tool-based image generation model. The mainline model (e.g. gpt-5.5) orchestrates, while a GPT Image model (e.g. gpt-image-1) generates images behind the scenes. You must include image_generation in tools and use tool_choice to force the call.
Quick Reference
| Field | Value | Required |
|---|
| Endpoint | POST {baseURL}/responses | Yes |
model | Mainline model (gpt-5.5, gpt-5.4, etc.) — NOT gpt-image-* | Yes |
tools | [{ "type": "image_generation" }] | Yes |
tool_choice | { "type": "image_generation" } (object format) | Recommended |
action | "auto" | "generate" | "edit" (inside tool config) | Optional, default "auto" |
quality | "auto" | "low" | "medium" | "high" (inside tool config) | Optional, default "auto" |
size | "1024x1024", "1536x1024", etc. (inside tool config) | Optional |
instructions | System prompt string | Optional |
reasoning | { "effort": "low"|"medium"|"high" } | Optional |
stream | true for SSE streaming | Optional |
Minimal Request
{
"model": "gpt-5.5",
"input": [
{ "role": "user", "content": [{ "type": "input_text", "text": "Draw a red circle" }] }
],
"tools": [{ "type": "image_generation" }],
"tool_choice": { "type": "image_generation" }
}
Full Request (with all options)
{
"model": "gpt-5.5",
"input": [
{ "role": "user", "content": [
{ "type": "input_text", "text": "Edit this image: make the sky purple" },
{ "type": "input_image", "image_url": "data:image/png;base64,..." }
]}
],
"tools": [{ "type": "image_generation", "action": "auto", "quality": "medium", "size": "1536x1024" }],
"tool_choice": { "type": "image_generation" },
"instructions": "You are a creative assistant.",
"reasoning": { "effort": "medium", "generate_summary": "concise" },
"stream": true
}
Response Output Parsing
The response output array contains items of different types:
for (const item of response.output) {
if (item.type === 'image_generation_call') {
const base64 = item.result;
}
if (item.type === 'message') {
for (const part of item.content) {
if (part.type === 'output_text') {
const text = part.text;
}
}
}
if (item.type === 'reasoning') {
for (const part of item.summary) {
if (part.type === 'summary_text') {
const thinking = part.text;
}
}
}
}
SSE Streaming Events
When stream: true, the API returns Server-Sent Events:
| Event | Payload | Description |
|---|
response.output_text.delta | { "delta": "..." } | Text content chunk |
response.reasoning_summary_text.delta | { "delta": "..." } | Thinking/reasoning chunk |
response.output_item.done | { "item": { "type": "image_generation_call", "result": "base64..." } } | Complete image (arrives all at once) |
response.completed | Full response object | Stream finished |
response.failed | { "error": { "message": "..." } } | Generation failed |
SSE Parsing Pattern
event: response.output_text.delta
data: {"delta":"Here is"}
event: response.output_text.delta
data: {"delta":" your image:"}
event: response.output_item.done
data: {"item":{"type":"image_generation_call","result":"iVBORw0KGgo..."}}
event: response.completed
data: {"response":{"output":[...]}}
Key: Split on \n\n, parse event: and data: lines separately. Buffer incomplete chunks. Images arrive as a single output_item.done event, not streamed incrementally.
Stream Finalization
When response.completed fires, parse the final output but fall back to accumulated stream values to avoid losing content:
const result = parseResponseOutput(finalData.output);
return {
text: result.text ?? accumulatedText,
imageBase64: result.imageBase64 ?? accumulatedImage,
thinking: result.thinking ?? accumulatedThinking,
};
Multi-Turn Conversation
Build the input array with conversation history:
{
"input": [
{ "role": "user", "content": [{ "type": "input_text", "text": "Draw a cat" }] },
{ "role": "assistant", "content": [{ "type": "output_text", "text": "Here's your cat!" }] },
{ "role": "user", "content": [{ "type": "input_text", "text": "Now make it wear a hat" }] }
]
}
For image editing, include the reference image in the user message:
{
"role": "user",
"content": [
{ "type": "input_text", "text": "Make the background blue" },
{ "type": "input_image", "image_url": "data:image/png;base64,..." }
]
}
The action: "auto" setting lets the model decide whether to generate or edit based on context.
Common Mistakes
| Mistake | Error | Fix |
|---|
tool_choice: "required" | 400 Tool choice 'required' must be specified with 'tools' | Use object format: { "type": "image_generation" } |
tool_choice: "image_generation" | 400 Invalid tool_choice | Use object format: { "type": "image_generation" } |
model: "gpt-image-1" | 400 Invalid model | Use mainline model (gpt-5.5, gpt-5.4), image model is selected internally |
No tool_choice | Model returns text only, no image | Add tool_choice: { "type": "image_generation" } |
No tools array | 400 tool_choice requires tools | Always include tools: [{ "type": "image_generation" }] |
action: "edit" without image | 400 edit requires image | Use "auto" to let model decide |
| Stream content lost after completion | Empty final message | Use ?? fallback: result.text ?? accumulatedText |
Error Classification
| Status | Meaning | Action |
|---|
| 401 | Invalid API key | Check credentials |
| 403 | Insufficient permissions | Check API key scope |
| 404 | Wrong endpoint | Verify base URL ends with /v1 |
| 429 | Rate limited | Retry with backoff |
| 500-504 | Server error | Retry later |
| CORS | Browser blocked | Use reverse proxy or server-side client |
Size Constraints
| Constraint | Value |
|---|
| Width & Height | Must be divisible by 16 |
| Max edge length | 3840px |
| Aspect ratio | Between 1:3 and 3:1 |
| Total pixels | 655,360 — 8,294,400 (min ~810x810, max ~2880x2880) |
Standard presets: 1024x1024, 1536x1024, 1024x1536, 1792x1024, 1024x1792
Custom sizes supported (e.g. 1536x864, 2560x1440) as long as all constraints are met.
Omit size from tool config to let the model choose automatically.
Implementation Reference
See src/lib/api.ts in this repository for a complete TypeScript implementation including:
- Request building with multi-turn history
- SSE stream parsing with accumulation
- Error classification (CORS detection, HTTP status mapping)
- AbortController integration for cancellation
- System prompt injection (full or minimal metadata)