OpenAI Image Generation skill for Codex

Generate, mask, iterate and store images. Codex chooses between the Image API and the Responses image tool based on whether editing is one-shot or conversational, sets size, quality and format explicitly, and writes decoded bytes to storage instead of passing base64 around. Masks follow the alpha and size rules, blocked prompts get a neutral message, and cost-relevant parameters are logged.

  • AI & LLM Engineering
  • $openai-image-generation
  • 165 lines
  • 2 files
  • ~1,750 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- openai-image-generation

Installs to ~/.agents/skills/openai-image-generation/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $openai-image-generation, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Generates and edits images with OpenAI GPT Image models through the Image API (images.generate, images.edit with masks and reference images) or the Responses image_generation tool for multi-turn editing — size, quality, format, transparent backgrounds, partial-image streaming, moderation errors, storage and cost control. Use when adding text-to-image or image editing to an app, producing product shots or illustrations from code, iterating on an image conversationally, or handling blocked prompts.

Use it when

  • Adding text-to-image or image editing to an application
  • Inpainting with masks or combining reference images
  • Iterative, conversational image refinement
  • Image requests time out, fail moderation or cost too much

Use something else when

How to check the result

One scripted generate and one edit call produce decodable files at the requested dimensions, and a mocked moderation error returns the friendly message path.

The mistake it prevents

Requesting a transparent background with JPEG output, or leaving default HTTP timeouts that cut off high-quality generations.

~/.agents/skills/openai-image-generation/SKILL.md

--- name: openai-image-generation description: Generates and edits images with OpenAI GPT Image models through the Image API (images.generate, images.edit with masks and reference images) or the… license: MIT ---

Done well means: the right API for the interaction (one-shot vs. conversational), parameters set on purpose rather than left to auto in production, images written to your own storage instead of shipped as base64 through every layer, moderation failures surfaced as a clear user message, and per-image cost visible.

Choose the API

NeedUse
One image from a prompt, batch jobs, simple endpointImage API client.images.generate
Edit an existing image, inpaint with a mask, combine reference imagesImage API client.images.edit
“Make it bluer”, iterative edits in a chat, model decides when to drawResponses API with {"type": "image_generation"} tool
Thousands of images offlineBatch API on /v1/images/generations (see openai-batch-api)

Models: GPT Image models (gpt-image-*) replace DALL·E. Read the model from config (IMAGE_MODEL) and check the image generation guide for the current lineup — some variants favor speed, others editing precision. Using GPT Image models requires API organization verification; a 403 on the first call usually means that is missing.

Workflow

  • 1. Read AGENTS.md; find storage (S3/GCS/local), upload limits and existing media code
  • 2. Pick API and model from the table; set explicit size, quality, format
  • 3. Write the prompt from a template (subject, composition, style, text, constraints)
  • 4. Decode b64_json, write to storage, return a URL
  • 5. Handle moderation blocks and timeouts
  • 6. Log cost-relevant params and latency

Generate

import base64, os, uuid
from openai import OpenAI

client = OpenAI(timeout=180)              # complex prompts can take up to ~2 minutes
IMAGE_MODEL = os.environ["IMAGE_MODEL"]

result = client.images.generate(
    model=IMAGE_MODEL,
    prompt=prompt,
    size="1536x1024",          # 1024x1024 | 1536x1024 | 1024x1536 are safe everywhere
    quality="medium",          # low for drafts/thumbnails, high for final assets
    output_format="webp",      # png (default) | jpeg | webp
    output_compression=85,     # jpeg/webp only, 0-100
    background="opaque",       # "transparent" needs png or webp
    n=1,
)
data = base64.b64decode(result.data[0].b64_json)
key = f"images/{uuid.uuid4()}.webp"
storage.put(key, data, content_type="image/webp")

Newer models accept custom WIDTHxHEIGHT sizes (multiples of 16, aspect between 1:3 and 3:1, bounded pixel count) and extra quality tiers; confirm support for the configured model before exposing them in a UI.

Edit with references and a mask

result = client.images.edit(
    model=IMAGE_MODEL,
    image=[open("product.png", "rb"), open("style_ref.png", "rb")],
    mask=open("mask.png", "rb"),   # applies to the FIRST image
    prompt="Place the product from image 1 on a marble countertop lit like image 2. "
           "Keep the label text unchanged.",
    size="1024x1024",
)

Mask rules: same format and dimensions as the first image, has an alpha channel (fully transparent pixels = area to change), under 50 MB. Masking is prompt-guided — the model may change pixels slightly outside the mask, so composite the original back over unmasked areas if exact preservation matters.

Conversational editing (Responses)

resp = client.responses.create(
    model=os.environ["OPENAI_MODEL"],     # mainline model drives the conversation
    input="Draw a minimalist poster of a lighthouse at dusk.",
    tools=[{"type": "image_generation", "model": IMAGE_MODEL,
            "size": "1024x1536", "quality": "high"}],
)
imgs = [o for o in resp.output if o.type == "image_generation_call"]
png = base64.b64decode(imgs[0].result)
revised = imgs[0].revised_prompt          # log it: shows how the prompt was rewritten

follow = client.responses.create(
    model=os.environ["OPENAI_MODEL"],
    previous_response_id=resp.id,
    input="Same poster, but make the sky deep teal and add the title 'NORTH POINT'.",
    tools=[{"type": "image_generation", "model": IMAGE_MODEL}],
)

Tool action can be auto, generate or edit; forcing edit with no image in context is an error. Inputs can be URLs, data URLs or file ids.

Streaming previews

Set partial_images (0–3) with streaming to show progressive previews. You may receive fewer partials than requested if the final image finishes first; always wait for the final image event before saving.

Prompt template

Subject: <what, with concrete attributes — materials, colors, count>
Composition: <framing, angle, placement, negative space for text overlay>
Style: <photo / flat vector / watercolor; lighting; lens if photographic>
Text in image: "<exact text>" — <font feel, placement>   (keep short; spell it out)
Constraints: <must keep / must avoid; brand colors as hex>

Short, exact strings render best; long paragraphs of in-image text and precise layouts remain unreliable — overlay text in HTML/Canvas when it must be pixel-perfect.

Safety and policy

  • Blocked requests fail with an image_generation_user_error style error and coarse moderation details (stage input or output). Show a neutral “couldn’t create that image” message; do not echo the policy category to end users verbatim, and never auto-rephrase prompts to get around a block.
  • moderation="low" relaxes filtering slightly; keep auto for user-generated prompts.
  • Send safety_identifier (a hashed user id) on end-user requests.
  • Do not generate images of real private individuals from user uploads without consent; check product policy for likeness and trademark use.
  • Generated images carry provenance metadata; do not strip it in post-processing unless the user asks and policy allows.

Gotchas

  • Returning base64 through APIs and databases bloats payloads 33% and stalls clients. Store the bytes, return a URL.
  • background="transparent" with output_format="jpeg" fails — JPEG has no alpha.
  • Default HTTP timeouts (often 30–60 s) cut off high-quality generations; raise them.
  • Every edit round-trips full images; resize inputs to the target size first.
  • Cost scales with size, quality and n. Default to low/medium for previews and generate high only for the chosen variant.

Verification

  • Script one generate and one edit call (needs network and spend; confirm first), then assert the file decodes (python3 -c "from PIL import Image; Image.open('out.webp').verify()" if Pillow is installed, or check magic bytes) and has the requested dimensions.
  • Unit-test the moderation-error path with a mocked API error.
  • Check logs record model, size, quality, latency and the revised prompt.

Output contract

**API:** Image API generate + edit; Responses tool for the chat editor
**Model:** IMAGE_MODEL (<value>); size 1536x1024; quality medium (previews low)
**Storage:** s3://media/images/<uuid>.webp, URL returned to client
**Safety:** moderation auto, safety_identifier set, block → friendly message
**Measured:** p50 <x> s, p95 <y> s; est. cost per image <z>

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "OpenAI Image Generation"
  short_description: "Generate, mask, iterate and store images"
  default_prompt: "$openai-image-generation Add an endpoint that generates product hero images from a text prompt and saves them to S3"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
New

Introducing Codex Skills Pro

79 professional skills that take Codex through complete engagements, each with its own scripts, references and a report that verifies the result.

All-AccessOne-time payment, no subscription
$49
Get All-AccessOr browse individual skills from $2.99 →

Secure checkout by Creem