> ## 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.

# Create an image

> Submit an image generation. Optionally anchor identity with a character.

`POST /v1/images` submits an image generation request. Credits are deducted immediately from your team balance; the generation is processed asynchronously. Poll [`GET /v1/images/{id}`](/api-reference/images/retrieve-image) for status, or register a [webhook endpoint](/webhooks) for a push callback when the generation completes or fails.

For a step-by-step walkthrough, see the [Quickstart](/quickstart). The full request shape, including all generation parameters, is in the playground below.

## Using a style

Styles come from `GET /v1/loras`; pass a style's `id` (opaque `lora_*` or slug) as `lora_id`. The field is **tri-state**:

| You send             | What happens                                                                                                                                                                                        |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| *(omitted)*          | Style matching runs automatically: when your prompt clearly names a look (e.g. "golden-hour film photo"), a matching style is applied. Matching is conservative — most prompts resolve to no style. |
| `"lora_…"` or a slug | That style is pinned and applied.                                                                                                                                                                   |
| `null`               | Style matching is disabled for this request — the image generates without a style.                                                                                                                  |

Whatever happens, the outcome is observable: every generation response echoes `style: { id, name }` (or `null`), and `style.id` round-trips — pass it back as `lora_id` to reuse the style.

Styles **compose** with [composition acts](/guides/actions) and `subjects` — pin a style and an `action_id` together and both apply. One special case: some catalog entries are composition acts. Sending an act's id as `lora_id` pins the act itself, so combining it with a *different* `action_id` returns `400 parameter_invalid_combination`. The same applies to `subjects`: the few styles that pick their own model still return `400 parameter_invalid_combination` when combined with `subjects`. `lora_id` remains incompatible with `context_images`.

### Retired styles

Retired style ids keep working — how depends on the style:

* **Aliased** — the id applies its designated successor style; the response `style` echoes the successor. Update your stored id at your convenience.
* **Plain** — the request succeeds but generates **without** a style, and the response carries a [`warnings[]`](#warnings) entry with code `style_retired_plain`.
* **Discontinued** — a small set of styles no longer generate at all: `400` with code [`style_retired`](/errors#style_retired). Pick a current style from `GET /v1/loras`.

## Batching with `count`

`count` (1–4, whole number) generates that many images in parallel and bills per image. If some images in the batch fail, you receive the ones that succeeded and the difference is refunded automatically — the response `image_count` reflects the number actually delivered, and `output_urls` contains one URL per delivered image.

## Warnings

The 201 body (and the [estimate](/api-reference/openapi) response) may carry `warnings[]` — non-fatal adjustments the platform made to your request:

```json theme={null}
{
  "warnings": [
    {
      "code": "style_retired_plain",
      "param": "lora_id",
      "message": "Style lora_06AAAAAAAAAAAAAAAAAAAAAAAA is retired and no longer applies a style — this request generates without one."
    }
  ]
}
```

Current codes are `style_retired_plain` (see above) and `parameter_ignored` (a parameter you sent has no effect on the generation path your request selected — for example `seed` on a styled generation). The key is omitted when there is nothing to report, appears **only** on the create and estimate responses (never on GETs, lists, or webhooks), and the code set is open — ignore codes you don't recognize. Idempotent replays return the original warnings verbatim.

Response fields echo the request parameters as sent, not as used: an ignored parameter (flagged in `warnings[]`) is echoed back with exactly the value you sent, not the value that was actually applied.

## Using a character

When `character_id` is set, the platform attaches the character's saved reference images to the generation as visual anchors for identity consistency. The dispatch path is image-to-image, so `denoise_strength` becomes effective and influences how closely the output follows the refs vs the prompt.

The character must be in `status: ready`. Use a `synthesizing` / `reviewing` / `failed` character, or a soft-deleted one, and the request returns `400 character_not_ready`.

<Note>
  `character_id` and `reference_image_urls` are **mutually exclusive**. Sending
  both returns `400 mutually_exclusive_input`. Pick one path per generation.
</Note>

```bash cURL 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": "Aurora at golden hour on a windswept cliff, cinematic",
    "character_id": "char_01HXMQ7Z3K8Y2VNABCDEFGHJKM",
    "size": "2k_2_3"
  }'
```

If the character has multiple ref poses, the dispatcher consumes all of them as anchors. There is no current way to limit attachment to a subset of poses — the whole ref set goes in.

## Size

Specify image dimensions one of two ways — never both:

**Named preset** via `size`:

| Tier | Available aspect ratios                                   |
| ---- | --------------------------------------------------------- |
| `2k` | `1:1`, `3:2`, `2:3`, `4:3`, `3:4`, `16:9`, `9:16`, `21:9` |
| `4k` | `1:1`, `3:2`, `2:3`, `4:3`, `3:4`, `16:9`, `9:16`, `21:9` |

Combine into a preset string in `<tier>_<ratio>` form, e.g. `2k_16_9`, `4k_1_1`, `2k_2_3`.

**Custom dimensions** via `width` and `height`:

* Both required when used.
* Range `[1024, 4096]` per side.
* Snapped server-side to the nearest multiple of 32 — the response `width`/`height` reflect the post-snap value.

Sending both `size` and `width`/`height` returns `400 parameter_invalid_combination`. Sending only one of `width`/`height` returns `400 missing_field`.

## Idempotency

Pass `Idempotency-Key` (any opaque value, 1–256 chars; UUID v4 recommended). Same key + same body within 24h replays the cached response with `Aurous-Idempotent-Replayed: true`. Same key + different body returns `409 idempotency_key_in_use`. The 24h window and 1–256 char bound are documented in [Idempotency](/idempotency).

## Webhooks

Register a [webhook endpoint](/webhooks) subscribed to `image.completed` / `image.failed` (`POST /v1/webhook_endpoints`) to receive a POST callback when the generation reaches a terminal state. The event payload is `{ event: "image.completed" | "image.failed", data: {...} }` where `data` matches the `GET /v1/images/{id}` response. See [Webhooks](/webhooks) for signature verification.


## OpenAPI

````yaml POST /v1/images
openapi: 3.0.0
info:
  title: Aurous Labs API
  description: >-
    Generate AI images with custom LoRA styles.


    ## Authentication

    All requests require an API key passed in the `X-Api-Key` header.

    Create API keys in your
    [dashboard](https://app.aurous-labs.com/dashboard/api-keys).


    ## Closed-beta access gate

    API keys are scoped to a user. If that user's account is not approved for
    the closed beta, every request returns `403` with one of these `error.code`
    values:


    - `account_pending` — awaiting review

    - `account_rejected` — declined post-signup

    - `account_suspended` — was approved, then suspended


    There is no retry — contact support to be approved. The same codes are
    emitted by the WebSocket gateway via 4001 close.


    ## Common headers

    Every response carries `Aurous-Request-Id` (a server-minted `req_<ULID>` for
    support tracing) and `Aurous-Version` (the API version applied to the
    response). Optionally pin a version on the request with `Aurous-Version:
    YYYY-MM-DD` — defaults to your team's pinned version.


    ## Quick Start

    ```bash

    curl -X POST https://api.aurous-labs.com/v1/images \
      -H "X-Api-Key: al_live_your_key" \
      -H "Content-Type: application/json" \
      -d '{"prompt": "A golden sunset over mountains", "lora_id": "your-lora-id", "size": "2k_1_1"}'
    ```
  version: 1.0.0
  contact: {}
servers:
  - url: https://api.aurous-labs.com
    description: Production
  - url: https://api.preprod.aurous-labs.com
    description: Preprod (staging)
security: []
tags:
  - name: Seedance (raw)
    description: >-
      Drop-in raw passthrough for Seedance video generation. Point the official
      Seedance provider SDK at this API's base URL and authenticate with your
      Aurous API key in the `X-Api-Key` header — request bodies are forwarded to
      the provider verbatim and responses come back shape-identical, so you keep
      the provider's exact request/response shapes. Task ids are Aurous-native
      `vid_…` ids. Billing rides response headers, not the body:
      `Aurous-Credits-Held` on the create response and `Aurous-Credits-Charged`
      on a settled, succeeded task read — the body itself stays provider-shaped.
paths:
  /v1/images:
    post:
      tags:
        - Public API (v1)
      summary: Create a new image generation
      description: >-
        Submits an image generation request. Credits are deducted immediately
        from your team balance. The generation is processed asynchronously —
        poll GET /v1/images/:id to check status, or register a webhook endpoint
        (POST /v1/webhook_endpoints) subscribed to `image.completed` /
        `image.failed` / `image.cancelled` to receive event callbacks; the event
        `data` payload has the same shape as the GET /v1/images/:id response. 


        Styles: `lora_id` (from GET /v1/loras — opaque `lora_*` or slug) is
        tri-state. Omit it and style matching runs automatically when your
        prompt names a look (conservative — many prompts resolve to no style).
        Send `null` to disable style matching for this request. Send an id to
        pin that style. The applied style is echoed on every response as `style:
        { id, name }` (or `null`), so the outcome is always observable. Retired
        style ids keep working: ids with a designated successor apply the
        successor style (echoed in `style`); other retired ids generate without
        a style and add a `warnings[]` entry (`style_retired_plain`); a small
        set of discontinued styles return 400 `style_retired`. Styles compose
        with `action_id` and `subjects` — and a composition-act id sent in
        `lora_id` acts as the act pin itself, so it cannot be combined with a
        *different* `action_id` (400 `parameter_invalid_combination`). 


        `count` generates up to 4 images in parallel, billed per image; if some
        images fail, you receive the ones that succeeded and the difference is
        refunded automatically (`image_count` reflects what was delivered). 


        Use size presets (e.g. "2k_1_1", "4k_16_9") or specify custom
        width/height (1024-4096 per side, snapped to multiples of 32, total
        pixels in [921,600 ; 16,777,216]). Pick one mode per request — sending
        both returns 400 `parameter_invalid_combination`. 


        When `enhance_prompt: true`, your prompt is run through an AI rewriting
        step before generation and the enhanced-generation rate applies. Styled
        generations always shape the prompt around the style — that built-in
        pass is not the enhancer and never bills the enhancer rate;
        `enhance_prompt` stays your explicit, separately-priced opt-in. 


        When you attach a `character_id`, the output follows your prompt. 


        Alternatively, supply up to 10 loose reference images via
        `context_images` for multi-image composition — positions follow array
        order and are addressed in the prompt as "Image N". Context-image
        requests cannot be combined with `subjects`, `character_id`,
        `reference_image_urls`, or `lora_id`. The response echoes only a count
        (`context_images: { image_count }`), never the image URLs. 


        Optionally pin a composition act with `action_id` (from GET
        /v1/actions). With subjects, the act must support your subject count
        (see `supported_character_counts`) — an unsupported count returns 400
        `action_not_available`; with zero subjects it renders a new person your
        prompt describes. An unknown or inaccessible act returns 404
        `resource_not_found`. `action_id` composes with `lora_id` when that id
        names a style, and is mutually exclusive with `context_images` (400
        `mutually_exclusive_input`). Omit it to let act detection run
        automatically, or send `null` to disable detection for the request.
        Every response echoes the applied act as `action: { id, name }` (or
        `null` when none was applied). 


        The 201 body may carry `warnings[]` — non-fatal adjustments such as a
        retired style pin or a parameter with no effect on the selected
        generation path (`parameter_ignored`). Warnings appear only on this 201
        body and the estimate response, never on GETs or webhooks; unknown codes
        should be ignored. 


        Output URLs are valid for approximately 24 hours after generation.
        Download what you want to keep.
      operationId: V1ImagesController_createGeneration
      parameters:
        - name: Idempotency-Key
          in: header
          description: >-
            Stripe-style idempotency key (1-256 chars). Same key + same
            canonical-JSON body returns the cached response with
            `Aurous-Idempotent-Replayed: true`. Same key + different body
            returns `409 invalid_request / idempotency_key_in_use`. UUID v4
            recommended. Replay window is 24 hours. Absent header is treated as
            non-idempotent (each call processes anew).
          required: false
          schema:
            type: string
        - name: Aurous-Version
          in: header
          required: false
          description: >-
            Optional API version pin (YYYY-MM-DD). Defaults to your team's
            pinned version, or the system default `2026-07-16` for
            unauthenticated requests.
          schema:
            type: string
            example: '2026-07-16'
            pattern: ^\d{4}-\d{2}-\d{2}$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateGenerationDto'
      responses:
        '201':
          description: Generation created and pending processing
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenerationResponse'
          headers:
            Aurous-Request-Id:
              description: Server-minted request id. Quote this in support tickets.
              schema:
                type: string
                example: req_01HXMQ7Z3K8Y2NABCDEFGHJKMP
            Aurous-Version:
              description: API version pin applied to this response (YYYY-MM-DD).
              schema:
                type: string
                example: '2026-07-16'
            X-RateLimit-Limit:
              description: Bucket capacity (max tokens) for this endpoint class.
              schema:
                type: integer
                example: 120
            X-RateLimit-Remaining:
              description: Tokens remaining after this request.
              schema:
                type: integer
                example: 119
            X-RateLimit-Reset:
              description: >-
                Epoch seconds when the bucket would be full again, assuming no
                further requests.
              schema:
                type: integer
                example: 1700000060
            Deprecation:
              description: >-
                Present (always the literal string `true`) on a 2xx response to
                POST /v1/images, /v1/images/estimate, /v1/videos or
                /v1/videos/estimate when the request body used the legacy
                `character_id` or `reference_image_urls` field instead of
                `subjects[]`. Absent otherwise. No removal date is set — see the
                linked guide.
              schema:
                type: string
                enum:
                  - 'true'
                example: 'true'
            Link:
              description: >-
                Paired with `Deprecation` — an RFC 8288 link to the subjects[]
                migration guide (`rel="deprecation"`). Same presence rule as
                `Deprecation`.
              schema:
                type: string
                example: >-
                  <https://docs.aurous-labs.com/guides/subjects>;
                  rel="deprecation"; type="text/html"
            Aurous-Idempotent-Replayed:
              description: >-
                Present (literal `true`) when this response was served from a
                stored idempotent replay — the same `Idempotency-Key` +
                canonical body was seen within the 24h window and the original
                response is returned WITHOUT re-executing (no second charge, no
                second task). Absent on the first (fresh) execution and on any
                request sent without an `Idempotency-Key`. Only on the
                idempotency-aware create routes.
              schema:
                type: string
                enum:
                  - 'true'
                example: 'true'
        '400':
          description: >-
            Validation failed, the prompt was rejected by content policy (code:
            prompt_blocked), a retired style was pinned (code: `style_retired`),
            an invalid parameter combination (e.g. `context_images` with
            `lora_id`, or a composition-act `lora_id` with a different
            `action_id`), or a pinned composition act that does not support the
            subject count (code: `action_not_available`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          headers:
            Aurous-Request-Id:
              description: Server-minted request id. Quote this in support tickets.
              schema:
                type: string
                example: req_01HXMQ7Z3K8Y2NABCDEFGHJKMP
            Aurous-Version:
              description: API version pin applied to this response (YYYY-MM-DD).
              schema:
                type: string
                example: '2026-07-16'
            X-RateLimit-Limit:
              description: Bucket capacity (max tokens) for this endpoint class.
              schema:
                type: integer
                example: 120
            X-RateLimit-Remaining:
              description: Tokens remaining after this request.
              schema:
                type: integer
                example: 119
            X-RateLimit-Reset:
              description: >-
                Epoch seconds when the bucket would be full again, assuming no
                further requests.
              schema:
                type: integer
                example: 1700000060
        '401':
          description: Unauthorized — missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          headers:
            Aurous-Request-Id:
              description: Server-minted request id. Quote this in support tickets.
              schema:
                type: string
                example: req_01HXMQ7Z3K8Y2NABCDEFGHJKMP
            Aurous-Version:
              description: API version pin applied to this response (YYYY-MM-DD).
              schema:
                type: string
                example: '2026-07-16'
            X-RateLimit-Limit:
              description: Bucket capacity (max tokens) for this endpoint class.
              schema:
                type: integer
                example: 120
            X-RateLimit-Remaining:
              description: Tokens remaining after this request.
              schema:
                type: integer
                example: 119
            X-RateLimit-Reset:
              description: >-
                Epoch seconds when the bucket would be full again, assuming no
                further requests.
              schema:
                type: integer
                example: 1700000060
        '402':
          description: Insufficient credits — top up via /dashboard/billing
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          headers:
            Aurous-Request-Id:
              description: Server-minted request id. Quote this in support tickets.
              schema:
                type: string
                example: req_01HXMQ7Z3K8Y2NABCDEFGHJKMP
            Aurous-Version:
              description: API version pin applied to this response (YYYY-MM-DD).
              schema:
                type: string
                example: '2026-07-16'
            X-RateLimit-Limit:
              description: Bucket capacity (max tokens) for this endpoint class.
              schema:
                type: integer
                example: 120
            X-RateLimit-Remaining:
              description: Tokens remaining after this request.
              schema:
                type: integer
                example: 119
            X-RateLimit-Reset:
              description: >-
                Epoch seconds when the bucket would be full again, assuming no
                further requests.
              schema:
                type: integer
                example: 1700000060
        '403':
          description: >-
            Account not approved for closed beta. error.code is one of
            `account_pending`, `account_rejected`, `account_suspended`. There is
            no retry — contact support to be approved.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          headers:
            Aurous-Request-Id:
              description: Server-minted request id. Quote this in support tickets.
              schema:
                type: string
                example: req_01HXMQ7Z3K8Y2NABCDEFGHJKMP
            Aurous-Version:
              description: API version pin applied to this response (YYYY-MM-DD).
              schema:
                type: string
                example: '2026-07-16'
            X-RateLimit-Limit:
              description: Bucket capacity (max tokens) for this endpoint class.
              schema:
                type: integer
                example: 120
            X-RateLimit-Remaining:
              description: Tokens remaining after this request.
              schema:
                type: integer
                example: 119
            X-RateLimit-Reset:
              description: >-
                Epoch seconds when the bucket would be full again, assuming no
                further requests.
              schema:
                type: integer
                example: 1700000060
        '404':
          description: >-
            A referenced resource (a style `lora_id`, a character, or a pinned
            composition `action_id`) was not found or is inaccessible
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          headers:
            Aurous-Request-Id:
              description: Server-minted request id. Quote this in support tickets.
              schema:
                type: string
                example: req_01HXMQ7Z3K8Y2NABCDEFGHJKMP
            Aurous-Version:
              description: API version pin applied to this response (YYYY-MM-DD).
              schema:
                type: string
                example: '2026-07-16'
            X-RateLimit-Limit:
              description: Bucket capacity (max tokens) for this endpoint class.
              schema:
                type: integer
                example: 120
            X-RateLimit-Remaining:
              description: Tokens remaining after this request.
              schema:
                type: integer
                example: 119
            X-RateLimit-Reset:
              description: >-
                Epoch seconds when the bucket would be full again, assuming no
                further requests.
              schema:
                type: integer
                example: 1700000060
        '409':
          description: Idempotency-Key was reused with a different request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          headers:
            Aurous-Request-Id:
              description: Server-minted request id. Quote this in support tickets.
              schema:
                type: string
                example: req_01HXMQ7Z3K8Y2NABCDEFGHJKMP
            Aurous-Version:
              description: API version pin applied to this response (YYYY-MM-DD).
              schema:
                type: string
                example: '2026-07-16'
            X-RateLimit-Limit:
              description: Bucket capacity (max tokens) for this endpoint class.
              schema:
                type: integer
                example: 120
            X-RateLimit-Remaining:
              description: Tokens remaining after this request.
              schema:
                type: integer
                example: 119
            X-RateLimit-Reset:
              description: >-
                Epoch seconds when the bucket would be full again, assuming no
                further requests.
              schema:
                type: integer
                example: 1700000060
        '429':
          description: >-
            Rate limit exceeded for this endpoint class. error.code is
            `too_many_requests` (request-count limit; LLM endpoints may instead
            return `tpm_rate_limit_exceeded` or `concurrency_limit_exceeded`).
            Retry after the number of seconds in the `Retry-After` response
            header.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          headers:
            Aurous-Request-Id:
              description: Server-minted request id. Quote this in support tickets.
              schema:
                type: string
                example: req_01HXMQ7Z3K8Y2NABCDEFGHJKMP
            Aurous-Version:
              description: API version pin applied to this response (YYYY-MM-DD).
              schema:
                type: string
                example: '2026-07-16'
            X-RateLimit-Limit:
              description: Bucket capacity (max tokens) for this endpoint class.
              schema:
                type: integer
                example: 120
            X-RateLimit-Remaining:
              description: Tokens remaining after this request.
              schema:
                type: integer
                example: 119
            X-RateLimit-Reset:
              description: >-
                Epoch seconds when the bucket would be full again, assuming no
                further requests.
              schema:
                type: integer
                example: 1700000060
            Retry-After:
              description: >-
                Seconds to wait before retrying. Present on 429 (rate limit) and
                on 503 provider_unavailable. Prefer this over computing
                X-RateLimit-Reset − now.
              schema:
                type: integer
                example: 12
        '503':
          description: >-
            Character generation is temporarily unavailable, or the platform
            could not verify your account/billing state or prepare the request
            (for example, reference images) before dispatch — error.code is
            `provider_unavailable` in every case. Retry after the number of
            seconds in the `Retry-After` header.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          headers:
            Aurous-Request-Id:
              description: Server-minted request id. Quote this in support tickets.
              schema:
                type: string
                example: req_01HXMQ7Z3K8Y2NABCDEFGHJKMP
            Aurous-Version:
              description: API version pin applied to this response (YYYY-MM-DD).
              schema:
                type: string
                example: '2026-07-16'
            X-RateLimit-Limit:
              description: Bucket capacity (max tokens) for this endpoint class.
              schema:
                type: integer
                example: 120
            X-RateLimit-Remaining:
              description: Tokens remaining after this request.
              schema:
                type: integer
                example: 119
            X-RateLimit-Reset:
              description: >-
                Epoch seconds when the bucket would be full again, assuming no
                further requests.
              schema:
                type: integer
                example: 1700000060
            Retry-After:
              description: >-
                Seconds to wait before retrying. Present on 429 (rate limit) and
                on 503 provider_unavailable. Prefer this over computing
                X-RateLimit-Reset − now.
              schema:
                type: integer
                example: 12
      security:
        - api-key: []
components:
  schemas:
    CreateGenerationDto:
      type: object
      properties:
        prompt:
          type: string
          description: >-
            The text prompt describing the image to generate. 1-4000 characters;
            whitespace-only is rejected.
          example: A golden sunset over mountains, cinematic lighting, 8k resolution
          minLength: 1
          maxLength: 4000
        lora_id:
          type: string
          nullable: true
          example: lora_01HXMQ7Z3K8Y2VNABCDEFGHJKM
          description: >-
            Optional. Style identifier (lora_*) or slug, from GET /v1/loras.
            Tri-state: omit and matching runs automatically when your prompt
            names a look; send null to disable style matching for this request;
            send an id to pin that style. Styles now compose with `action_id`
            and `subjects` — a composition-act id in this field cannot be
            combined with a different `action_id`. Retired style ids keep
            working: aliased ids apply their successor style (echoed in
            `response.style`); other retired ids generate without a style and
            add a `warnings[]` entry.
        character_id:
          type: string
          description: >-
            Optional character ID (`char_<ulid>` from POST /v1/characters; UUID
            also accepted for legacy back-compat). When set, the character's
            reference images are sent to the model as visual anchors for
            identity consistency. The character must be in `status: ready` —
            referencing a `synthesizing` / `reviewing` / `failed` character
            returns 400 `character_not_ready`. Cross-team character_ids return
            404 (existence is never leaked). Mutually exclusive with
            `reference_image_urls`: sending both returns 400
            `mutually_exclusive_input`. The output follows your prompt.
            Superseded by `subjects[]`; fully supported — successful responses
            carry an advisory `Deprecation: true` header when this field is
            used.
          example: char_01HXMQ7Z3K8Y2VNABCDEFGHJKM
          deprecated: true
        width:
          type: number
          description: >-
            Custom output image width in pixels. Use with `height` OR use `size`
            (preset), not both. Range [1024, 4096]; snapped server-side to the
            nearest multiple of 32. Sending both `size` and custom dimensions
            returns 400 with code `parameter_invalid_combination`. Sending only
            one of `width`/`height` returns 400 with code `missing_field`.
          example: 2048
          minimum: 1024
          maximum: 4096
        height:
          type: number
          description: >-
            Custom output image height in pixels. Use with `width` OR use `size`
            (preset), not both. Range [1024, 4096]; snapped server-side to the
            nearest multiple of 32. Sending both `size` and custom dimensions
            returns 400 with code `parameter_invalid_combination`. Sending only
            one of `width`/`height` returns 400 with code `missing_field`.
          example: 2048
          minimum: 1024
          maximum: 4096
        size:
          type: string
          description: >-
            Image size as a named preset. Use this OR custom `width`/`height`,
            not both. Format is `<tier>_<ratio>` where tier is `2k` or `4k` and
            ratio matches the supported aspect-ratio set. Sending both `size`
            and custom dimensions returns 400 with code
            `parameter_invalid_combination`.
          example: 2k_1_1
          enum:
            - 2k_1_1
            - 2k_3_2
            - 2k_2_3
            - 2k_4_3
            - 2k_3_4
            - 2k_16_9
            - 2k_9_16
            - 2k_21_9
            - 4k_1_1
            - 4k_3_2
            - 4k_2_3
            - 4k_4_3
            - 4k_3_4
            - 4k_16_9
            - 4k_9_16
            - 4k_21_9
        count:
          type: number
          description: >-
            Number of images to generate in this request (1-4, whole number).
            Images generate in parallel and you are billed per image; if some
            images in the batch fail, you receive the ones that succeeded and
            the difference is refunded automatically — the response
            `image_count` reflects the number actually delivered.
          example: 1
          minimum: 1
          maximum: 4
        enhance_prompt:
          type: boolean
          description: >-
            When true, an LLM rewrites your prompt before generation to a more
            detailed, model-friendly form; the rewritten prompt is what reaches
            the model. This is the only customer-facing prompt-shaping toggle in
            the public API, and the only one that changes the price: enhanced
            generations cost a configurable multiplier of the base rate. Styled
            generations (a pinned or auto-matched style) always shape the prompt
            around the style — that built-in pass is not the enhancer, never
            fails a request, and never bills the enhancer multiplier unless you
            set this flag yourself.
          example: false
        reference_image_urls:
          description: >-
            Up to 6 reference images. Each entry can be either:
             - an opaque file ID `file_<ulid>` returned by `POST /v1/files`, or
             - an `https://` URL pointing at a public host (max 2048 chars).
            URLs are server-side fetched through an SSRF-pinned client (rejects
            private IPs / loopback / link-local / cloud metadata) and
            materialized as a 24h-TTL file under your team. Image files only — a
            `file_<ulid>` uploaded with purpose
            `reference_video`/`reference_audio` is rejected (400
            `invalid_format`). Pricing matches the reference-image rate (see
            Pricing). Empty array or omitted is treated as "no references".
            Mutually exclusive with `character_id` — sending both returns 400
            `mutually_exclusive_input`. Superseded by `subjects[]`; fully
            supported — successful responses carry an advisory `Deprecation:
            true` header when this field is used.
          example:
            - file_01HXMQ7Z3K8Y2NABCDEFGHJKMN
            - https://example.com/ref2.jpg
          minItems: 0
          maxItems: 6
          deprecated: true
          type: array
          items:
            type: string
        subjects:
          maxItems: 10
          description: >-
            Ordered subjects composed into one image (Image 1, Image 2, …). Max
            10 subjects and 10 input images total. Mutually exclusive with
            character_id/reference_image_urls (both → 400
            mutually_exclusive_input). Composes with `lora_id` when it names a
            style; the few styles that pick their own model still return 400
            parameter_invalid_combination. Omit or [] for text-to-image/style.
          type: array
          items:
            $ref: '#/components/schemas/SubjectDto'
        context_images:
          minItems: 0
          maxItems: 10
          example:
            - file_01HXMQ7Z3K8Y2NABCDEFGHJKMN
            - https://example.com/outfit.jpg
          description: >-
            Up to 10 loose reference images for multi-image composition,
            interpreted from your prompt — no identity grouping or per-subject
            framing is applied, and no identity consistency is guaranteed.
            Positions follow array order and can be addressed in the prompt as
            "Image 1" … "Image 10" (e.g. "the outfit in Image 3"). Each entry is
            a `file_<ulid>` ID from POST /v1/files or an https URL to a public
            host (max 2048 chars). Empty array or omitted is treated as "no
            context images". Mutually exclusive with `subjects`, `character_id`,
            and `reference_image_urls` (400 `mutually_exclusive_input`) and with
            `lora_id` (400 `parameter_invalid_combination` — multi-image
            composition picks its own model; styles cannot be stacked). The
            response echoes only a count (`context_images: { image_count }`),
            never the image URLs.
          type: array
          items:
            type: string
        action_id:
          type: string
          nullable: true
          example: 3f2b6c1e-8a4d-4e2b-9c7a-1d5e8f0a2b3c
          description: >-
            Pin a composition act from `GET /v1/actions`. Treat the id as opaque
            — it comes from the catalog and nowhere else. With subjects, the act
            must support your subject count (see `supported_character_counts`) —
            an unsupported count returns 400 `action_not_available`. Without
            subjects, the act renders with a new person described by your
            prompt. Omit to let act detection run automatically; send `null` to
            disable detection for this request. Combinable with `lora_id` when
            that id names a prompt style (the act and the style compose); a
            composition-act id sent in `lora_id` already acts as the pin, so it
            cannot be combined with a DIFFERENT `action_id` (400
            `parameter_invalid_combination`). Mutually exclusive with
            `context_images` (400 `mutually_exclusive_input`). An id that is
            unknown or not visible to your team returns 404 `resource_not_found`
            — the same uniform 404 as `GET /v1/actions/{id}`.
        output_format:
          type: string
          enum:
            - jpeg
            - png
          example: jpeg
          description: >-
            Output format. `png` yields a transparent background where the
            composition supports it. Default `jpeg`.
      required:
        - prompt
    GenerationResponse:
      type: object
      properties:
        object:
          type: string
          description: >-
            Discriminator — always `inference`. Mirrors OpenAI's `object`-field
            convention so SDK clients can branch on the resource type without
            inspecting the ID prefix. A single canonical value (`inference`)
            covers both image and video generations; use `media_type` to
            distinguish the rendering kind.
          example: inference
          enum:
            - inference
          default: inference
        id:
          type: string
          description: Opaque generation ID
          example: img_01HXMQ7Z3K8Y2VNABCDEFGHJKM
        status:
          type: string
          description: >-
            Current generation status. Lifecycle: `pending` (created, awaiting
            dispatch) → `processing` (running) → one of the terminal values
            `succeeded` / `failed` / `cancelled`. Additional terminal values may
            be introduced in future API versions and will be announced via the
            changelog before they appear on the wire.
          example: succeeded
          enum:
            - pending
            - processing
            - succeeded
            - failed
            - cancelled
        media_type:
          type: string
          description: >-
            Distinguishes image vs video generation. May be null for older rows
            minted before this column existed.
          example: image
          enum:
            - image
            - video
          nullable: true
        prompt:
          type: string
          description: The text prompt used for generation
          example: A golden sunset over mountains, cinematic lighting
        output_urls:
          description: >-
            Generated image proxy URLs. Each URL is anonymous-read (no auth
            header required) and edge-cached for 24 hours. Available for ~24
            hours after generation. Save what you want to keep — long-term
            storage is intentionally not part of the platform. URLs return 410
            Gone after expiry.
          example:
            - >-
              https://api.aurous-labs.com/v1/images/img_01HXMQ7Z3K8Y2VNABCDEFGHJKM/output/0
          nullable: true
          type: array
          items:
            type: string
        video_url:
          type: string
          description: >-
            Generated video proxy URL (only present on `media_type: video`).
            Same 24h TTL as image output_urls.
          example: >-
            https://api.aurous-labs.com/v1/videos/vid_01HXMQ7Z3K8Y2VNABCDEFGHJKM/output?token=...
          nullable: true
        reference_image_urls:
          description: >-
            The reference image URLs you supplied as visual anchors for this
            generation, echoed back (snapshotted at inference time). Present
            only when the generation was driven by your own reference images
            (`reference_image_urls` or `reference` subjects). Omitted entirely
            for character-driven generations — a character's reference images
            are managed platform assets and are never echoed.
          example:
            - https://example.com/ref1.jpg
          nullable: true
          type: array
          items:
            type: string
        error_message:
          type: string
          description: >-
            Human-readable error message if the generation failed.
            Non-contractual prose — do not parse or match on this value. Switch
            on error_code instead.
          nullable: true
          example: Content policy violation
        error_code:
          type: string
          description: >-
            Machine-readable failure reason when `status` is `failed`, for the
            cases where you need to branch in code. `null` on every successful
            generation, on failures recorded before this field existed, and on
            failure paths that have no stable code — `error_message` is
            human-facing copy that is re-tuned over time, so never pattern-match
            it. Currently emitted: `generation_interrupted`,
            `reference_preparation_failed`, `content_filtered`,
            `generation_failed`, and `first_frame_too_small` (an `https://`
            `first_frame_url` attached to a video model whose dimensions were
            below the minimum — a `file_<ulid>` frame is rejected with the same
            code as a `400` before any credits are held; see [Errors](/errors)).
            The set is additive: new codes may appear, so treat an unrecognized
            value as a generic failure, keep a default branch in your switch
            statement, and fall back to `error_message`.
          nullable: true
          example: generation_interrupted
        duration_ms:
          type: integer
          description: Processing duration in milliseconds (set on terminal status)
          example: 14820
          nullable: true
        cost:
          description: >-
            Per-generation cost breakdown — same shape as the `estimated_cost`
            returned by `POST /v1/{images,videos}/estimate`. May be `null` for
            older rows from before this field existed; populated for all new
            generations. The `amount` reflects the committed charge for
            terminal-status rows — `0` (with `refunded: true`) on a `failed`
            generation, since the reserved hold was released, never charged.
          nullable: true
          example:
            amount: 2
            currency: credit
            breakdown:
              base: 1
              enhance: 1
          allOf:
            - $ref: '#/components/schemas/GenerationCost'
        width:
          type: number
          description: >-
            Resolved output image width in pixels (image generations only).
            Reflects the post-snap dimension actually generated; may differ from
            a custom-requested `width` by up to 31 px due to multiple-of-32
            snapping.
          example: 2048
        height:
          type: number
          description: >-
            Resolved output image height in pixels (image generations only).
            Reflects the post-snap dimension actually generated; may differ from
            a custom-requested `height` by up to 31 px due to multiple-of-32
            snapping.
          example: 2048
        image_count:
          type: number
          description: >-
            Number of images in the batch. Reflects the requested `count` while
            the generation is running; on a terminal status it reflects the
            number actually DELIVERED — when part of a batch fails you receive
            the successful images, this count re-stamps to match, and the
            difference is refunded automatically.
          example: 1
        size_preset:
          type: string
          description: >-
            Named size preset applied to this generation. `null` when the
            request used custom `width`/`height` instead of a preset.
          example: 2k_1_1
          enum:
            - 2k_1_1
            - 2k_3_2
            - 2k_2_3
            - 2k_4_3
            - 2k_3_4
            - 2k_16_9
            - 2k_9_16
            - 2k_21_9
            - 4k_1_1
            - 4k_3_2
            - 4k_2_3
            - 4k_4_3
            - 4k_3_4
            - 4k_16_9
            - 4k_9_16
            - 4k_21_9
          nullable: true
        inference_type:
          type: string
          description: >-
            Inference mode dispatched. Images: `t2i` (text-to-image) — reference
            images and characters are supplementary inputs to the t2i flow, not
            a separate mode. Videos (this list also returns `vid_*` rows): `t2v`
            (text-to-video), `i2v` (image-to-video — frame-driven,
            subject-driven, or a pinned video model on its own), or `r2v`
            (reference mode — the generation was driven by a motion reference).
            `r2v` does NOT imply you supplied that reference: it covers BOTH
            your own clip sent as `reference_video_url` AND a platform-built
            reference, produced when a first frame rides with a video model
            (pinned via `video_lora_id`, or auto-matched from the image). On the
            platform-built variant `reference_video_url` is `null` — branch on
            that field, not on `inference_type`, to tell the two apart. `i2i` is
            reserved for a future image-edit endpoint and is not currently
            emitted.
          example: t2i
          enum:
            - t2i
            - t2v
            - i2v
            - r2v
            - i2i
          nullable: true
        cfg_rescale:
          type: number
          description: >-
            CFG rescale factor the customer supplied on the request body, echoed
            back here. Range 0.0-1.0. Omitted when the customer did not supply a
            per-request value (the platform applied a precedence-chain default —
            LoRA, character override, or the global 0.7 — which is not exposed
            on the response).
          example: 0.7
          minimum: 0
          maximum: 1
          nullable: true
        denoise_strength:
          type: number
          description: >-
            Denoising strength the customer supplied on the request body, echoed
            back here. Range 0.0-1.0. Omitted when the customer did not supply a
            value or when the generation was a bare text-to-image request
            (denoise is only applied when reference images or a character are
            attached).
          example: 0.6
          minimum: 0
          maximum: 1
          nullable: true
        seed:
          type: integer
          description: >-
            The random seed the model actually used for this image generation.
            Populated even when you omit `seed` on the request — the platform
            requests a random seed and records the concrete value the provider
            rolled, so you can reproduce the result by passing it back as
            `seed`. Available once `status` is `succeeded`; `null` before then
            and for `failed`/`cancelled` generations. For multi-image batches
            (`image_count` > 1) this is the seed of the first image
            (`output_urls[0]`); per-image seeds are not yet exposed. Image
            generations only.
          example: 819572108
          nullable: true
        video_duration:
          type: integer
          description: Video duration in seconds (video generations only)
          example: 5
          nullable: true
        video_resolution:
          type: string
          description: Video resolution (video generations only)
          example: 480p
          enum:
            - 480p
            - 720p
            - 1080p
          nullable: true
        video_ratio:
          type: string
          description: Video aspect ratio (video generations only)
          example: '16:9'
          enum:
            - '16:9'
            - '4:3'
            - '1:1'
            - '3:4'
            - '9:16'
            - '21:9'
            - adaptive
          nullable: true
        video_generate_audio:
          type: boolean
          description: >-
            Whether the generated video includes synchronized audio (video
            generations only). Echoes the request `generate_audio` (default
            true).
          example: true
          nullable: true
        video_task:
          type: string
          description: >-
            Resolved reference-video task (video generations only). `reference`:
            the generation borrows the clip's motion for a new scene. `extend`:
            the generation continues the clip itself. `null` on every generation
            that did not supply `reference_video_url` (including all
            pre-existing rows). Additional task types may be introduced in
            future API versions — treat an unrecognized value as opaque.
          example: reference
          enum:
            - reference
            - extend
          nullable: true
        extend_direction:
          type: string
          description: >-
            Resolved extend direction (video generations only). Non-null only
            when `video_task` is `extend`; `null` otherwise (including every
            reference-mode and non-reference generation).
          example: forward
          enum:
            - forward
            - backward
          nullable: true
        reference_video_url:
          type: string
          description: >-
            The reference video URL you submitted, echoed back VERBATIM as you
            sent it — never re-signed, never a storage path. `null` for
            generations that did not supply `reference_video_url` (including
            every dashboard-originated row, and including an `inference_type:
            "r2v"` generation whose motion reference the platform built from a
            first frame plus a pinned or matched video model — this field, not
            `inference_type`, is what distinguishes your own clip from a
            platform-built reference).
          example: https://cdn.example.com/clips/dance-loop.mp4
          nullable: true
        reference_audio_url:
          type: string
          description: >-
            The reference audio URL you submitted, echoed back VERBATIM as you
            sent it. `null` for generations that did not supply
            `reference_audio_url`.
          example: https://cdn.example.com/audio/voiceover.mp3
          nullable: true
        video_lora_id:
          type: string
          description: >-
            The video model (an id or slug from GET /v1/video_loras) behind this
            generation (video generations only) — either the `video_lora_id` you
            pinned on the request, or the model the platform auto-matched to
            your prompt when none was pinned. `null` for a plain (no video
            model) generation, and always `null` on image generations.
          example: lora_01HXMQ7Z3K8Y2VNABCDEFGHJKM
          nullable: true
        video_lora_name:
          type: string
          description: >-
            Display name of the video model in `video_lora_id`, if any. Omitted
            for plain generations (no pinned or matched video model).
          example: Cinematic Pan
          nullable: true
        character_id:
          type: string
          description: >-
            Character ID supplied on the request (`char_<ulid>` or legacy UUID),
            echoed back. `null` when no character was attached to this
            generation.
          example: char_01HXMQ7Z3K8Y2VNABCDEFGHJKM
          nullable: true
        subjects:
          description: >-
            Ordered subjects composed into this generation (positional — entry N
            echoes entry N of the identity inputs sent on create; legacy
            `character_id` / `reference_image_urls` inputs are normalized into
            the same projection). Character entries carry the opaque
            `char_<ulid>`; reference entries echo only their image count, never
            URLs. Video generations echo their cast the same way (up to 2
            entries), matching the `subjects[]` array accepted by POST
            /v1/videos. Character entries on generations created before cast
            snapshotting may carry `character_id: null`; the id is never
            substituted with an internal identifier. `null` for generations that
            did not compose subjects (plain prompt-only or style generations,
            and context-image generations). The engine is not exposed.
          nullable: true
          example:
            - type: character
              character_id: char_01HXMQ7Z3K8Y2VNABCDEFGHJKM
              image_count: 1
            - type: reference
              character_id: null
              image_count: 2
          type: array
          items:
            $ref: '#/components/schemas/SubjectEcho'
        context_images:
          description: >-
            Count-only echo of the `context_images[]` sent on create — the
            number of loose reference images supplied, never the image URLs
            themselves. `null` for every generation that did not use
            `context_images` (plain prompt-only, style, subject, and video
            generations).
          nullable: true
          example:
            image_count: 3
          allOf:
            - $ref: '#/components/schemas/ContextImagesEcho'
        action:
          description: >-
            The composition act applied to this generation, echoed as `{ id,
            name }`. An act is applied either because you pinned it with
            `action_id` or because act detection matched one automatically. `id`
            is the act identifier from `GET /v1/actions`; `name` is its display
            name (or `null` when the name was not recorded). `null` for every
            generation that did not apply an act — plain prompt-only, style,
            context-image, and video generations. A new, always-present,
            nullable key: existing integrations that do not read it are
            unaffected.
          nullable: true
          example:
            id: 3f2b6c1e-8a4d-4e2b-9c7a-1d5e8f0a2b3c
            name: Over-the-shoulder
          allOf:
            - $ref: '#/components/schemas/ActionEcho'
        style:
          description: >-
            The prompt style applied to this generation, echoed as `{ id, name
            }`. A style is applied either because you pinned it with `lora_id`
            or because style matching resolved one from your prompt — the echo
            always reflects the style that actually ran (a retired id that
            aliases to a successor echoes the successor). `id` is the `lora_*`
            identifier from `GET /v1/loras` and round-trips into `lora_id`.
            `null` for every generation without a style — including `lora_id:
            null` requests, retired styles that generate plain, video
            generations, and all rows minted before styles shipped. A new,
            always-present, nullable key: existing integrations that do not read
            it are unaffected.
          nullable: true
          example:
            id: lora_06AAAAAAAAAAAAAAAAAAAAAAAA
            name: Ring-Light Creator
          allOf:
            - $ref: '#/components/schemas/StyleEcho'
        warnings:
          description: >-
            Non-fatal request adjustments, present ONLY on the `POST /v1/images`
            201 body (and the estimate response) — never on GET reads, list
            rows, or webhook payloads. Omitted entirely when empty. Current
            codes: `parameter_ignored`, `style_retired_plain`; new codes may be
            added without a version bump — ignore unknown codes. Idempotent
            replays return the original warnings verbatim.
          example:
            - code: style_retired_plain
              param: lora_id
              message: >-
                Style lora_06AAAAAAAAAAAAAAAAAAAAAAAA is retired and no longer
                applies a style — this request generates without one.
          type: array
          items:
            $ref: '#/components/schemas/WarningEntry'
        enhancer_outcome:
          type: string
          description: >-
            Outcome of the prompt-optimization step, echoed for observability.
            Current values: `no_request` (the request did not invoke it),
            `succeeded`, `identical`, `refused`, `transport_error`,
            `length_overflow`; `null` on rows minted before this field existed.
            INFORMATIONAL and deliberately an OPEN set (no schema enum — codegen
            clients must not mint a closed union): do not branch control flow on
            it; new values may be added without a version bump.
          example: succeeded
          nullable: true
        loras:
          description: >-
            LoRAs applied to this generation. `null` for prompt-only and
            pure-reference generations.
          nullable: true
          type: array
          items:
            $ref: '#/components/schemas/GenerationLoraEntry'
        aurous_version:
          type: string
          description: >-
            API contract version applied at the time this row was minted (D25 —
            frozen for replay across future version bumps).
          example: '2026-07-16'
        creation_request_id:
          type: string
          description: >-
            Aurous-Request-Id of the POST that created this row. Quote in
            support tickets to trace the original create request.
          example: req_01HXMQ7Z3K8Y2VNABCDEFGHJKM
        created_at:
          type: string
          description: Creation timestamp (ISO 8601)
          example: '2026-05-04T10:00:00Z'
        completed_at:
          type: string
          description: >-
            Terminal-status timestamp (ISO 8601). NULL until the generation
            reaches a terminal state.
          example: '2026-05-04T10:00:14Z'
          nullable: true
      required:
        - object
        - id
        - status
        - prompt
        - created_at
    ErrorResponse:
      type: object
      properties:
        error:
          description: Error payload
          allOf:
            - $ref: '#/components/schemas/ErrorPayload'
      required:
        - error
    SubjectDto:
      type: object
      properties:
        type:
          type: string
          enum:
            - character
            - reference
          example: character
          description: Subject kind.
        character_id:
          type: string
          example: char_01HXMQ7Z3K8Y2VNABCDEFGHJKM
          description: >-
            Required for `character` subjects. `char_<ulid>` or legacy UUID;
            must be a ready character your team owns.
        image_urls:
          minItems: 1
          maxItems: 10
          example:
            - file_01HXMQ7Z3K8Y2NABCDEFGHJKMN
          description: Required for `reference` subjects. 1–10 `file_<ulid>` or https URLs.
          type: array
          items:
            type: string
      required:
        - type
    GenerationCost:
      type: object
      properties:
        amount:
          type: number
          description: >-
            Total credit cost charged for this generation. For images this is
            the sum of `breakdown` values. For videos this is the token-based
            charge (not a sum of breakdown values); trust `amount` as the
            authoritative figure.
          example: 2
        currency:
          type: string
          description: Currency unit. Always `credit` at v1.0.
          example: credit
          enum:
            - credit
        breakdown:
          type: object
          description: >-
            Cost breakdown components, keyed by source. Image keys: `base`
            (always) and `enhance` (only when `enhance_prompt: true`); refs are
            free and do not add line items. When the team has a negotiated
            discount, an additional `discount_factor` line is present
            (multiplier, not a credit amount). For VIDEO the breakdown keys
            shift across the lifecycle. At HOLD time (the create 201, and while
            `pending`/`processing`) it carries `pricing_model` (always
            `video_tokens_v1`), `tokens_max` (the CEILING token count the hold
            was sized at), `credits_held` (the reserved credits), `video_input`
            (boolean), `resolution`, plus optional
            `enhance_flat_credits`/`discount_factor`. Once the video SETTLES
            (`status: succeeded`) the breakdown carries the ACTUAL figures
            instead: `tokens` (the realized token count — a DISTINCT key from
            the hold-time `tokens_max`, not a rename of it), `credits` (the
            settled charge), `credits_held` (the original ceiling), and
            `settled: true`. Video breakdown is NOT additive — `amount` is the
            authoritative charge, computed from the token count and per-model
            rate table. On a `refunded` generation, `breakdown` still reflects
            the original projection (what the attempt was priced at) even though
            `amount` is `0` — compare the two to see what you were NOT charged
            for. Values are typed `number | string | boolean`: video metadata
            like `pricing_model`/`resolution` are strings and
            `video_input`/`settled` are booleans, so read by name and ignore
            unknown keys (the field set is OPEN).
          example:
            pricing_model: video_tokens_v1
            tokens: 108000
            credits: 42
            credits_held: 51.75
            video_input: false
            resolution: 720p
            settled: true
        refunded:
          type: boolean
          description: >-
            True when the reserved credit hold for this generation was released
            without any charge — `amount` is `0` in that case, not the amount
            originally held. Set only on `failed` generations. Omitted (absent)
            on `succeeded`, `pending`, and `processing` rows.
          example: true
      required:
        - amount
        - currency
        - breakdown
    SubjectEcho:
      type: object
      properties:
        type:
          type: string
          description: >-
            Subject kind. `character` entries reference a saved character;
            `reference` entries were supplied as ad-hoc reference images.
          enum:
            - character
            - reference
          example: character
        character_id:
          type: string
          description: >-
            Opaque character ID (`char_<ulid>`) for `character` subjects — the
            same ID accepted by `subjects[].character_id` on create. `null` for
            `reference` subjects.
          example: char_01HXMQ7Z3K8Y2VNABCDEFGHJKM
          nullable: true
        image_count:
          type: integer
          description: Number of input images this subject contributed to the composition.
          example: 1
      required:
        - type
    ContextImagesEcho:
      type: object
      properties:
        image_count:
          type: integer
          description: Number of context images supplied on the request.
          example: 3
      required:
        - image_count
    ActionEcho:
      type: object
      properties:
        id:
          type: string
          description: >-
            Composition-act identifier — the same id returned by `GET
            /v1/actions` and accepted by `action_id` on `POST /v1/images`.
          example: 3f2b6c1e-8a4d-4e2b-9c7a-1d5e8f0a2b3c
          format: uuid
        name:
          type: string
          description: >-
            Display name of the applied act. `null` when the name was not
            recorded on the row.
          example: Over-the-shoulder
          nullable: true
      required:
        - id
    StyleEcho:
      type: object
      properties:
        id:
          type: string
          description: >-
            Style identifier — the same `lora_*` id returned by `GET /v1/loras`
            and accepted by `lora_id` on `POST /v1/images`. Pass it back as
            `lora_id` to reuse the style. `null` only on degenerate legacy rows.
          example: lora_06AAAAAAAAAAAAAAAAAAAAAAAA
          nullable: true
        name:
          type: string
          description: >-
            Display name of the applied style. `null` when the name was not
            recorded on the row.
          example: Ring-Light Creator
          nullable: true
      required:
        - id
    WarningEntry:
      type: object
      properties:
        code:
          type: string
          description: >-
            Machine-readable warning code. Current values: `parameter_ignored`
            (a parameter you sent has no effect on this generation path and was
            ignored) and `style_retired_plain` (the pinned `lora_id` is retired
            — the generation proceeded without a style). This is an OPEN set:
            new codes may be added without a version bump, so ignore unknown
            codes rather than failing on them.
          example: style_retired_plain
        param:
          type: string
          description: >-
            Machine-readable name of the request field the warning is about
            (e.g. `lora_id`, `seed`).
          example: lora_id
        message:
          type: string
          description: >-
            Human-readable explanation. Wording may change; branch on
            `code`/`param`, not on this text.
          example: >-
            Style lora_06AAAAAAAAAAAAAAAAAAAAAAAA is retired and no longer
            applies a style — this request generates without one.
      required:
        - code
        - param
        - message
    GenerationLoraEntry:
      type: object
      properties:
        id:
          type: string
          description: Opaque LoRA ID resolved at dispatch time
          example: lora_01HXMQ7Z3K8Y2VNABCDEFGHJKM
        name:
          type: string
          description: LoRA display name
          example: Sunset Style
    ErrorPayload:
      type: object
      properties:
        type:
          type: string
          description: Broad error category
          example: invalid_request
          enum:
            - invalid_request
            - authentication
            - not_found
            - rate_limit
            - server_error
        code:
          type: string
          description: >-
            Stable error code (programmatic discriminator). Closed-beta gate
            emits one of `account_pending`, `account_rejected`,
            `account_suspended` on 403.
          example: balance_too_low
          enum:
            - invalid_request
            - missing_field
            - invalid_format
            - value_out_of_range
            - unsupported_lora_for_mode
            - generation_not_cancellable
            - prompt_blocked
            - reference_blocked
            - output_moderation_rejected
            - unknown_version
            - mutually_exclusive_input
            - character_not_ready
            - parameter_invalid_combination
            - style_retired
            - parameter_invalid
            - too_many_reference_images
            - action_not_available
            - upload_invalid
            - balance_too_low
            - idempotency_key_in_use
            - api_key_not_found
            - payload_too_large
            - missing_api_key
            - invalid_api_key
            - revoked_api_key
            - resource_not_found
            - forbidden_resource
            - account_pending
            - account_rejected
            - account_suspended
            - already_approved
            - already_rejected
            - already_suspended
            - invalid_reinstate_target
            - invalid_suspend_target
            - cannot_moderate_admin
            - too_many_requests
            - concurrency_limit_exceeded
            - tpm_rate_limit_exceeded
            - internal_error
            - provider_unavailable
            - provider_timeout
            - provider_not_configured
            - invalid_time_range
            - invalid_bucket_width
            - too_many_buckets
            - too_many_group_by
            - invalid_filter
            - invalid_page_token
            - export_too_large
            - user_already_exists
            - self_invite_forbidden
            - invite_link_failed
            - invite_rate_limited
            - model_not_found
            - model_disabled
            - model_wrong_kind
            - max_tokens_exceeds_hard_cap
            - chat_model_misconfigured
            - embeddings_input_too_large
            - embeddings_unsupported_dimensions
            - pricing_frozen
            - provider_rate_limited
            - chat_provider_request_invalid
            - chat_provider_auth_failed
            - chat_provider_unavailable
            - max_input_tokens_exceeded
            - chat_provider_unknown_error
            - embeddings_provider_unknown_error
            - embeddings_batch_not_supported
            - embeddings_input_too_many_items
            - embeddings_video_unsupported
            - encoding_format_unsupported
            - missing_max_tokens_no_model_default
            - chat_cancel_target_not_found
            - chat_completion_not_found
            - chat_cancel_target_already_terminal
            - chat_cancel_target_not_cancellable
            - tool_choice_required_unsupported
            - response_format_too_large
            - response_format_too_deep
            - invalid_cursor
            - invalid_cursor_for_endpoint
            - model_slug_exists
            - output_expired
            - output_not_available
            - content_filtered
            - image_generation_failed
            - reference_media_invalid
            - reference_media_cap_reached
            - reference_fetch_failed
            - unsupported_auth_method
            - insufficient_scope
            - uploads_expired
            - provider_unknown_error
            - first_frame_too_small
        message:
          type: string
          description: Human-readable message
          example: Team available balance is 1.5 credits, generation requires 2.0.
        param:
          type: object
          description: Field name when the error is parameter-scoped
          example: prompt
          nullable: true
        doc_url:
          type: string
          description: Documentation link for this error code
          example: https://docs.aurous-labs.com/errors#balance_too_low
        request_id:
          type: string
          description: Echoes Aurous-Request-Id — quote in support tickets
          example: req_01HXMQ7Z3K8Y2VNABCDEFGHJKM
      required:
        - type
        - code
        - message
        - doc_url
        - request_id
  securitySchemes:
    api-key:
      type: apiKey
      in: header
      name: X-Api-Key
      description: Your team API key (starts with `al_live_`).

````