> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aurous-labs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Subjects

> Compose ordered character and reference subjects into a single image, and migrate off the legacy character_id / reference_image_urls fields.

`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](/guides/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:

```json theme={null}
{
  "prompt": "Two friends at a rooftop party, golden hour",
  "subjects": [
    { "type": "character", "character_id": "char_01HXMQ7Z3K8Y2VNABCDEFGHJKM" },
    { "type": "reference", "image_urls": ["https://example.com/friend.jpg"] }
  ]
}
```

* **`character` subjects** reference a saved character (from `POST /v1/characters`) by its `char_<ulid>` — a legacy UUID is also accepted, same as the top-level `character_id` field. The character must be `status: ready`; otherwise you get `400 character_not_ready`.
* **`reference` subjects** carry 1-10 `image_urls` — each entry either an opaque `file_<ulid>` from `POST /v1/files` or an `https://` URL. This is the same input shape `reference_image_urls` already accepts.

The array is **ordered** and **positional**: the platform composes every input image into numbered slots (Image 1, Image 2, …), assigned in the order the subjects appear. A `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 onto `subjects[]` as a single-entry array.

### `character_id` → one character subject

<CodeGroup>
  ```json Before theme={null}
  {
    "prompt": "A portrait against a neon skyline",
    "character_id": "char_01HXMQ7Z3K8Y2VNABCDEFGHJKM"
  }
  ```

  ```json After theme={null}
  {
    "prompt": "A portrait against a neon skyline",
    "subjects": [
      { "type": "character", "character_id": "char_01HXMQ7Z3K8Y2VNABCDEFGHJKM" }
    ]
  }
  ```
</CodeGroup>

### `reference_image_urls` → one reference subject

<CodeGroup>
  ```json Before theme={null}
  {
    "prompt": "The person from these photos, hiking in the mountains",
    "reference_image_urls": [
      "https://example.com/ref1.jpg",
      "https://example.com/ref2.jpg"
    ]
  }
  ```

  ```json After theme={null}
  {
    "prompt": "The person from these photos, hiking in the mountains",
    "subjects": [
      {
        "type": "reference",
        "image_urls": [
          "https://example.com/ref1.jpg",
          "https://example.com/ref2.jpg"
        ]
      }
    ]
  }
  ```
</CodeGroup>

Both migrations are purely mechanical — wrap the same value in a one-entry `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[]`:

```http theme={null}
HTTP/1.1 201 Created
Deprecation: true
Link: <https://docs.aurous-labs.com/guides/subjects>; rel="deprecation"; type="text/html"
```

* `Deprecation: true` — advisory only. There's no `Sunset` header and no countdown; it simply signals "this request used a superseded field shape."
* `Link` — points back to this page.

The check is on the *field name*, not its value — a request body that includes the key at all (even `"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 to `subjects[]` — compositing more than one identity into a single output — needs nothing beyond two saved characters and an array:

```bash theme={null}
curl -X POST https://api.aurous-labs.com/v1/images \
  -H "X-Api-Key: $AUROUS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "prompt": "Two old friends sharing a laugh over coffee, warm afternoon light",
    "subjects": [
      { "type": "character", "character_id": "char_01HXMQ7Z3K8Y2VNABCDEFGHJKM" },
      { "type": "character", "character_id": "char_01HXMQ7Z3K8Y2WNABCDEFGHJKM" }
    ],
    "output_format": "png"
  }'
```

```json theme={null}
{
  "object": "inference",
  "id": "img_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
  "status": "pending",
  "prompt": "Two old friends sharing a laugh over coffee, warm afternoon light",
  "subjects": [
    { "type": "character", "character_id": "char_01HXMQ7Z3K8Y2VNABCDEFGHJKM", "image_count": 1 },
    { "type": "character", "character_id": "char_01HXMQ7Z3K8Y2WNABCDEFGHJKM", "image_count": 1 }
  ],
  "image_count": 1,
  "created_at": "2026-07-15T10:00:00Z"
}
```

Poll `GET /v1/images/{id}` (or register a [webhook endpoint](/webhooks)) until `status` reaches a terminal value, same as any other generation. Need a `char_<ulid>` to try this with? See [Create a character](/api-reference/characters/create-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

| Combination                                                   | Result                                                                                                                                                   |
| ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `subjects[]` **and** `character_id` or `reference_image_urls` | 400 `mutually_exclusive_input` — pick one input style per request.                                                                                       |
| `subjects[]` **and** `lora_id`                                | 400 `parameter_invalid_combination` — a multi-subject composition picks its own settings and can't be paired with a style.                               |
| `character_id`/`reference_image_urls` **and** `lora_id`       | **Allowed, unchanged.** A style can still be paired with the legacy single-identity fields — this restriction is specific to the new `subjects[]` field. |

### 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 `character` subject always contributes exactly 1 image (its saved cover reference).
* A `reference` subject contributes one image per URL in `image_urls`.

Go over the cap — for example nine `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`](/errors#too_many_reference_images) in the error reference.

## The response

Every `GET`/`POST` response for a generation — and every webhook delivery, which carries the same shape — now includes a `subjects` field:

```json theme={null}
{
  "subjects": [
    { "type": "character", "character_id": "char_01HXMQ7Z3K8Y2VNABCDEFGHJKM", "image_count": 1 },
    { "type": "reference", "character_id": null, "image_count": 2 }
  ]
}
```

* Entries are positional — entry N echoes entry N of the `subjects[]` you sent.
* `character` entries echo the opaque `character_id` you sent (never anything else about the character).
* `reference` entries echo `character_id: null` and `image_count` — the number of URLs you sent, never the URLs themselves.
* `subjects` is `null` on 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 explicit `subjects[]` 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.** `count` is 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`, and `seed` are 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_rescale` and `denoise_strength` are omitted, and `seed` and `negative_prompt` come back `null`, regardless of what you sent.
* **`output_format: "png"` for transparency.** The default is `jpeg`. Set `output_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](/api-reference/images/create-image) — full request/response shape
* [Create a character](/api-reference/characters/create-character) — mint a `char_<ulid>` to use as a subject
* [`too_many_reference_images`](/errors#too_many_reference_images) — the budget error in the error reference
* [Cost transparency](/guides/cost-transparency) — how the credit receipt works
* [Idempotency](/idempotency) — safe retries for `POST /v1/images`
