subjects[] is how you tell POST /v1/images which people or reference images to compose into the output — one ordered array, each entry either a character (a saved identity) or a reference (ad-hoc images you supply inline). It supersedes the older character_id and reference_image_urls fields, which only ever let you attach a single identity input. This guide covers what the field does, how to migrate off the older fields, and what’s different about a generation that uses it. (For loose, ungrouped references — raw material your prompt draws on, with no identity guarantee — see Context images instead.)
What subjects[] is
Each entry has a type and, depending on the type, either a character_id or an image_urls array:
charactersubjects reference a saved character (fromPOST /v1/characters) by itschar_<ulid>— a legacy UUID is also accepted, same as the top-levelcharacter_idfield. The character must bestatus: ready; otherwise you get400 character_not_ready.referencesubjects carry 1-10image_urls— each entry either an opaquefile_<ulid>fromPOST /v1/filesor anhttps://URL. This is the same input shapereference_image_urlsalready accepts.
character subject always occupies exactly one slot; a reference subject occupies one slot per URL. So a character subject followed by a 3-image reference subject places the character at Image 1 and the reference subject across Images 2-4. Reordering subjects[] changes which images map to which slot — put your intended focal subject first.
Omit subjects entirely, or send [], for a plain text-to-image or style (lora_id) generation — both are treated as “no subjects.”
Migrating from the legacy fields
Both legacy identity fields fold ontosubjects[] as a single-entry array.
character_id → one character subject
reference_image_urls → one reference subject
subjects[] array with the matching type. The generation behaves identically for a single identity; subjects[] only starts to matter once you want more than one.
Existing requests keep working
character_id and reference_image_urls are not being removed and there is no scheduled removal date. If you keep sending them, your integration keeps working exactly as it does today — nothing to fix urgently.
What changes: a successful (2xx) response to a request that used either legacy field now carries two extra headers, nudging you toward subjects[]:
Deprecation: true— advisory only. There’s noSunsetheader and no countdown; it simply signals “this request used a superseded field shape.”Link— points back to this page.
"character_id": null) is still counted as legacy-shaped. If you’ve fully migrated and still see the header, check for a stray character_id/reference_image_urls key your client is serializing. The header is only ever set on success; a request that 400s/404s/402s never carries it, since there’s no successful call to advertise a migration path for.
Quickstart: two characters in one image
The clearest reason to move tosubjects[] — compositing more than one identity into a single output — needs nothing beyond two saved characters and an array:
GET /v1/images/{id} (or register a webhook endpoint) until status reaches a terminal value, same as any other generation. Need a char_<ulid> to try this with? See Create a character. Mixing a character subject with a reference subject in the same array works the same way — swap the second entry for { "type": "reference", "image_urls": [...] }.
Rules
The 10-image budget
Every subject contributes at least one input image to the composition, and the total across the whole array is capped at 10:- A
charactersubject always contributes exactly 1 image (its saved cover reference). - A
referencesubject contributes one image per URL inimage_urls.
character subjects plus a reference subject with two URLs (11 images) — and the request returns 400 too_many_reference_images. This endpoint does not trim on your behalf: fix the request client-side (drop a subject, or drop a URL from a reference subject) and resubmit. See too_many_reference_images in the error reference.
The response
EveryGET/POST response for a generation — and every webhook delivery, which carries the same shape — now includes a subjects field:
- Entries are positional — entry N echoes entry N of the
subjects[]you sent. characterentries echo the opaquecharacter_idyou sent (never anything else about the character).referenceentries echocharacter_id: nullandimage_count— the number of URLs you sent, never the URLs themselves.subjectsisnullon every generation that didn’t compose subjects — plain prompt-only generations, style/LoRA generations, and all video generations. It’s a new, always-present, nullable key: existing integrations that don’t read it are unaffected.
What’s different about a subject generation
A generation that uses the explicitsubjects[] field always runs on a fixed, optimized composition pipeline rather than the general-purpose generation path. (Requests using the legacy single-identity fields may or may not be served by this pipeline depending on the rest of the request — the reliable signal is the response itself: subjects is non-null exactly when the composition pipeline handled the generation.) For subjects[] requests, a few things follow:
- Exactly one output image.
countis accepted but ignored — you always get exactly one image back, and pricing reflects that: it’s the standard single-image rate, with no per-subject or per-reference-image surcharge, regardless of how many subjects you attach (up to the 10-image budget). - Tuning knobs are inert.
steps,guidance_scale,cfg_rescale,denoise_strength,negative_prompt, andseedare all still accepted on the request — none of them will cause an error — but subject generations use optimized settings and don’t apply them. On the response,cfg_rescaleanddenoise_strengthare omitted, andseedandnegative_promptcome backnull, regardless of what you sent. output_format: "png"for transparency. The default isjpeg. Setoutput_format: "png"if you want a transparent background where the composition supports it.
Estimates
POST /v1/images/estimate mirrors all of the above for a subjects[] request: send the same body and get back a quote for exactly one image, at the standard single-image rate. One difference from the real create call: the estimate never fetches your https:// reference URLs — it validates their shape and, for character subjects, that the character exists and is ready, but any problem specific to an inline URL (unreachable host, non-image content, and so on) only surfaces when you actually call POST /v1/images.
Where to next?
- Create an image — full request/response shape
- Create a character — mint a
char_<ulid>to use as a subject too_many_reference_images— the budget error in the error reference- Cost transparency — how the credit receipt works
- Idempotency — safe retries for
POST /v1/images

