GPT Image 2.5 mask_url on Sume: build the PNG mask with Pillow
mask_url works only on the GPT Image 2.5 rows and must be a public HTTPS link. OpenAI wants an alpha channel and a matching size. A Pillow script builds one.

On Sume, mask_url is accepted only by the GPT Image 2.5 rows, openai/gpt-image-2.5 and openai/gpt-image-2.5-sunburst, and it has to be a public HTTPS URL. Other image rows return a 400 for it. The mask file itself follows OpenAI's rules: its guide (read 2026-10-06) says the mask needs an alpha channel, the same format and size as the image, and a size under 50MB.
So the work is in building the file correctly and hosting it where Sume can fetch it.
Build the mask
This script makes an opaque black PNG the same size as your source and cuts a transparent rectangle where the edit should happen. Which area the model edits is defined in OpenAI's guide, so read that page and confirm on a low-quality test call before you trust the direction.
import sys
from PIL import Image, ImageDraw
def make_mask(src: str, dst: str, box: tuple) -> None:
size = Image.open(src).size
mask = Image.new("RGBA", size, (0, 0, 0, 255))
ImageDraw.Draw(mask).rectangle(box, fill=(0, 0, 0, 0))
mask.save(dst, format="PNG")
print("mask", size, "alpha at box start:", mask.getpixel(box[:2])[3])
if __name__ == "__main__":
if len(sys.argv) != 7:
raise SystemExit("usage: mask.py SRC DST X0 Y0 X1 Y1")
x0, y0, x1, y1 = map(int, sys.argv[3:])
make_mask(sys.argv[1], sys.argv[2], (x0, y0, x1, y1))Three masks, three conventions
| Where | Mask rule | How you pass it |
|---|---|---|
| Sume GPT Image 2.5 rows | Public HTTPS URL only | mask_url field |
| OpenAI image guide | Alpha channel, same format and size, under 50MB | Its own API fields |
| Ideogram precise edit | Black marks the area to edit, white the area to keep; same size as the image; needs both colors | mask upload |
| Sume Ideogram 4.5 row | No mask field | Edit by prompt and reference only |
Send it
- Upload the source and the mask to storage that serves public HTTPS, with no login and no localhost.
- Send the source as the first
input_referencesentry and the mask asmask_url. - Use
quality: "low"while you check the direction and the edge. - If the call fails with a message about an unfetchable image, the usual cause is a link that is not public.
Do not reuse an Ideogram mask
Ideogram's convention is the opposite style: a black and white image, not an alpha cutout. A mask that works for one will not work for the other, so keep them in separate folders and name them by target model.
Sources
Related posts
More in Developers
- GPT Image 2.5 on Sume: no quality means high, auto reserves max
Skipping quality on GPT Image 2.5 means high ($0.0355 at 1280x720). Sending auto reserves max ($0.1420). What that does to a budget.
- Sume MCP consent: granting write always includes read
Sume's MCP consent offers mcp:read (required) and mcp:write (opt-in). A write grant always includes read, and there is no paid scope.
- Haystack MCPTool 30-second timeout vs Sume jobs_wait holds
Haystack's MCPTool times out tool calls at 30 seconds; Sume's jobs_wait holds 50 to 55. Raise invocation_timeout or wait in shorter slices.
- Heroku H12 at 30 seconds: call Sume async, not sync
Heroku's router ends a request at 30 s (H12). Sume sync mode waits up to 30 s too. Submit async, return 202 to the browser, then poll or take a webhook.
Written by Sume