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

# Generate Video (from Reference Video/Image)

> Generate a MiniMax H3 video that carries the subject and/or style of a reference video and up to 4 reference images (omni-reference).

Send `multipart/form-data` with at least one of `reference_video`, `reference_video_url`, `reference_image`, or `reference_image_url`. Supplying any of these four fields is what selects this mode of `POST /v1/videos` — reference-video (omni-reference) generation — and produces a Viggle-optimized MiniMax H3 video with native audio.

<Warning>
  If you supply none of the four reference fields, this is **not** an error — the request silently falls back to [Generate Video (from Text)](/v1/api-reference/videos/create-from-text) instead, the same way omitting `first_frame_image`/`last_frame_image` there falls back to text-only. There is no `source`/`mode` parameter to force reference-video selection independently of these fields, so double-check your request actually includes one of them before relying on this mode.
</Warning>

This uses the same `POST /v1/videos` endpoint as [Generate Video (from Text)](/v1/api-reference/videos/create-from-text), [Generate Video (from First Frame and/or Text)](/v1/api-reference/videos/create-from-first-frame), [Generate Video (from First-Last Frames and/or Text)](/v1/api-reference/videos/create-from-first-last-frame), and [Generate Video (from Character Animation)](/v1/api-reference/videos/create-from-character-animation) — which mode runs is selected by which fields you send, not by a separate parameter. Reference fields cannot be combined with `first_frame_image`/`last_frame_image`.

<Note>
  Every generated video includes **native audio** — there's no separate audio flag or field. **Pricing** (1 credit = $0.01): ⌈`duration_s`⌉ × the per-second rate for `quality` — **$0.01/sec (1 credit/second)\*\* for both `low` and `high`. Reference-conditioned generation costs the same as text-only for the same quality and duration; `resolution` and `aspect_ratio` don't affect price. See [Pricing and retention](/v1/pricing).
</Note>

## Request parameters

Send `multipart/form-data`.

| Parameter             | Type                     |  Required | Default | Description                                                                                                                                                                                                                                          |
| --------------------- | ------------------------ | :-------: | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `prompt`              | string                   |    Yes    | —       | Non-empty text description of the desired video. Still required with reference video/images supplied.                                                                                                                                                |
| `quality`             | string                   |    Yes    | —       | `low` for faster generation and quicker iteration, or `high` for higher-fidelity output. Both tiers are billed at the same **\$0.01/sec** rate.                                                                                                      |
| `reference_video`     | file                     | See below | —       | Reference video uploaded directly. Declared as a repeatable field for forward compatibility, but at most 1 reference video is accepted today, combined with `reference_video_url`. Not required if at least one reference image is supplied instead. |
| `reference_video_url` | string (URI)             | See below | —       | Publicly reachable URL of the reference video; the service fetches and re-hosts it. Declared as a repeatable field for forward compatibility, but at most 1 reference video is accepted today, combined with `reference_video`.                      |
| `reference_image`     | file, repeatable         | See below | —       | Reference image uploaded directly. Can be repeated, and freely mixed with `reference_image_url` — combined total across both is capped at 4.                                                                                                         |
| `reference_image_url` | string (URI), repeatable | See below | —       | Publicly reachable URL of a reference image; the service fetches and re-hosts it. Can be repeated, and freely mixed with `reference_image` — combined total across both is capped at 4.                                                              |
| `duration_s`          | number                   |     No    | `5`     | Target duration in seconds, 3–15. Rounded up to determine the credit charge.                                                                                                                                                                         |
| `resolution`          | string                   |     No    | `768p`  | `480p`, `768p`, or `1080p`. Does not affect price.                                                                                                                                                                                                   |
| `aspect_ratio`        | string                   |     No    | `16:9`  | `16:9`, `9:16`, `1:1`, `4:3`, `3:4`, or `21:9`. Does not affect price.                                                                                                                                                                               |
| `seed`                | integer                  |     No    | —       | ≥ 0. Omit for a random seed.                                                                                                                                                                                                                         |
| `watermark`           | boolean                  |     No    | `false` | Whether to burn in a Viggle watermark.                                                                                                                                                                                                               |
| `priority`            | integer                  |     No    | `1000`  | Admission-queue priority tier. Must be one of the server's configured tiers (currently `1000` or `0`). Most callers never need to set this.                                                                                                          |

Supplying any of `reference_video`/`reference_video_url`/`reference_image`/`reference_image_url` is what selects this mode — there's no separate `source` parameter. Supplying none of the four does **not** error; see the warning above — the request is generated as text-to-video instead.

### Reference video vs. reference images

You can supply a reference video, one or more reference images, or both together in the same request — a reference video is not required if you supply at least one reference image, and vice versa.

* **Reference video** (`reference_video` / `reference_video_url`): both fields are declared as arrays for forward compatibility, but at most 1 reference video is accepted today across the two combined — supplying more (two files, two URLs, or one of each) answers `400 INVALID_REQUEST` — "at most 1 reference video(s) allowed". The reference video itself must be 0.5–600 seconds long and at least 64x64 with even width and height — the same upload constraints enforced elsewhere in this API. This is independent of `duration_s`, which controls the duration of the *generated* output video, not the reference.
* **Reference images** (`reference_image` / `reference_image_url`): each can be repeated and the two forms can be freely mixed, but the combined count across both cannot exceed 4. Going over answers `400 INVALID_REQUEST` — "at most 4 reference images are allowed".

Reference fields cannot be combined with `first_frame_image`/`last_frame_image` (file or URL form). Supplying both answers `400 INVALID_REQUEST` — "reference\_video/reference\_image cannot be combined with first\_frame\_image/last\_frame\_image".

## Response parameters

Returns `200 OK` — an acceptance acknowledgment, not the final result.

| Field        | Type            | Always present | Description                                    |
| ------------ | --------------- | :------------: | ---------------------------------------------- |
| `id`         | string          |       Yes      | Public video ID, `vid_`-prefixed.              |
| `status`     | string          |       Yes      | Always `queued` on acceptance.                 |
| `progress`   | integer or null |       Yes      | Always `null` on acceptance.                   |
| `created_at` | string          |       Yes      | ISO 8601 creation timestamp, second precision. |

```json theme={null}
{
  "id": "vid_3f2a9c",
  "status": "queued",
  "progress": null,
  "created_at": "2026-08-24T09:12:03Z"
}
```

This response deliberately carries none of the fields `GET /v1/videos/{video_id}` returns (`stage`, `video_url`, `alpha_url`, `completed_at`, `error`). See [Get Video](/v1/api-reference/videos/get) for the full shape once the video is processing or done.

## Examples

<CodeGroup>
  ```bash cURL (video file + one reference image URL) theme={null}
  curl https://apis.viggle.ai/v1/videos \
    -H "Authorization: Bearer $VIGGLE_API_KEY" \
    -F prompt="keep the same outfit and walk forward" \
    -F quality=high \
    -F reference_video=@./reference.mp4 \
    -F reference_image_url=https://example.test/ref1.png \
    -F duration_s=8
  ```

  ```bash cURL (URL only, multiple reference images) theme={null}
  curl https://apis.viggle.ai/v1/videos \
    -H "Authorization: Bearer $VIGGLE_API_KEY" \
    -F prompt="a dancer in the same style" \
    -F quality=low \
    -F reference_video_url=https://example.test/ref.mp4 \
    -F reference_image_url=https://example.test/img1.png \
    -F reference_image_url=https://example.test/img2.png
  ```

  ```bash cURL (reference images only, no video) theme={null}
  curl https://apis.viggle.ai/v1/videos \
    -H "Authorization: Bearer $VIGGLE_API_KEY" \
    -F prompt="combine these two references" \
    -F quality=low \
    -F reference_image=@./img1.png \
    -F reference_image=@./img2.png
  ```

  ```python Python theme={null}
  import os, requests
  response = requests.post(
      "https://apis.viggle.ai/v1/videos",
      headers={"Authorization": f"Bearer {os.environ['VIGGLE_API_KEY']}"},
      data={
          "prompt": "keep the same outfit and walk forward",
          "quality": "high",
          "reference_video_url": "https://example.test/ref.mp4",
          "duration_s": 8,
      },
  )
  response.raise_for_status()
  video = response.json()
  ```

  ```javascript JavaScript theme={null}
  const form = new FormData();
  form.append("prompt", "a dancer in the same style");
  form.append("quality", "low");
  form.append("reference_video_url", "https://example.test/ref.mp4");
  form.append("reference_image_url", "https://example.test/img1.png");
  form.append("reference_image_url", "https://example.test/img2.png");
  const response = await fetch("https://apis.viggle.ai/v1/videos", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.VIGGLE_API_KEY}` },
    body: form,
  });
  const video = await response.json();
  ```
</CodeGroup>

### `@viggle/sdk`

```javascript theme={null}
const video = await client.videos.create({
  prompt: "keep the same outfit and walk forward",
  quality: "high",
  referenceVideoUrl: "https://example.com/ref.mp4",
  referenceImageUrl: ["https://example.com/img1.png", "https://example.com/img2.png"],
  durationS: 8,
});
// video.id, video.status ("queued", ...); poll with client.videos.get(video.id)
```

## Common errors

| Error                                                                                                                      | Cause                                                                                           |
| -------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `400 INVALID_REQUEST` — "at most 1 reference video(s) allowed"                                                             | `reference_video` and/or `reference_video_url` together supply more than 1 reference video.     |
| `400 INVALID_REQUEST` — "at most 4 reference images are allowed"                                                           | `reference_image` and `reference_image_url` together exceed 4 images.                           |
| `400 INVALID_REQUEST` — "reference\_video/reference\_image cannot be combined with first\_frame\_image/last\_frame\_image" | A reference field supplied alongside `first_frame_image`/`last_frame_image` (file or URL form). |

## Next step

Use [Get Video](/v1/api-reference/videos/get) with the returned `id` to poll for the result.

<CardGroup cols={3}>
  <Card title="Generate Video (from Text)" icon="text" href="/v1/api-reference/videos/create-from-text">
    Generate from a prompt alone, with no frame image or reference.
  </Card>

  <Card title="Generate Video (from First Frame and/or Text)" icon="image" href="/v1/api-reference/videos/create-from-first-frame">
    Anchor only the start of the video, with no last frame.
  </Card>

  <Card title="Generate Video (from First-Last Frames and/or Text)" icon="images" href="/v1/api-reference/videos/create-from-first-last-frame">
    Also anchor the end of the video on a last frame.
  </Card>

  <Card title="Generate Video (from Character Animation)" icon="person-running" href="/v1/api-reference/videos/create-from-character-animation">
    Apply a driving video's motion and/or character images on a separate backend.
  </Card>
</CardGroup>


## OpenAPI

````yaml openapi.yaml POST /v1/videos
openapi: 3.0.3
info:
  title: Viggle API
  description: Generate AI-powered character animation videos
  version: 2.0.0
  contact:
    name: Viggle Support
    url: https://viggle.ai
servers:
  - url: https://apis.viggle.ai
    description: Production server
security:
  - bearerAuth: []
tags:
  - name: Renders
    description: Prepare inputs, create renders, observe progress, and retrieve results.
  - name: Credits
    description: Read the credit balance available to the calling principal.
  - name: Characters
    description: |
      Create, list, inspect, and delete reusable character assets. `type`
      selects whether creation also extracts a 3D vsplat; export its
      download URL separately once ready.
  - name: Motions
    description: |
      Create, list, inspect, and delete reusable motion assets, and import one
      from an official motion template. `type` selects whether creation also
      extracts or generates a 3D animation; export its download URL
      separately once ready.
  - name: Videos
    description: |
      Generate a MiniMax H3 text-to-video (`vid_` prefix) or a
      character-animation video from a character image and/or driving video
      (`anim_` prefix), and read the unified view over every video the
      caller owns — H3 generations, character-animation generations, and
      character+motion Renders (`render_` prefix) — merged into one
      resource.
paths:
  /v1/videos:
    post:
      tags:
        - Videos
      summary: >-
        Generate a video from text, from a first and optional last frame, from a
        reference video/image, or from a character image and/or driving video
      description: >
        Generates a Viggle-optimized MiniMax H3 video with native audio, or

        (for character-animation mode) a character-animation video on a

        separate backend. `multipart/form-data` for every mode;

        character-animation mode also accepts a plain `application/json`

        body. The fields supplied select one of five generation modes:


        - Neither frame field nor any reference or character-animation
          field supplied: text-to-video from `prompt` alone.
        - `first_frame_image`/`first_frame_image_url` supplied, no last
          frame: first-frame-to-video.
        - Both a first frame and a last frame supplied:
        first+last-frame-to-video.

        - Any of `reference_video`/`reference_video_url`/`reference_image`/
          `reference_image_url` supplied: reference-video (omni-reference)
          generation, carrying the subject/style of a reference video and/or
          up to 4 reference images into the new video. Mutually exclusive
          with the frame fields.
        - `driving_video_url` and/or `character_image_url`/
          `character_image_urls` supplied: character-animation generation —
          applies a driving video's motion and/or up to 4 character images
          to produce a new video. Mutually exclusive with every field
          above, including `quality`. See `CreateVideoForm` and `Generate
          Video (from Character Animation)`.

        `quality=low` generates faster for quicker iteration; `quality=high`

        produces higher-fidelity output. Both tiers are billed at the same

        $0.01/sec rate — see `CreateVideoForm` below. Character-animation

        mode has no `quality` tier and is billed at a flat rate instead.


        All five modes answer `200` with the same minimal acceptance shape;

        poll `GET /v1/videos/{video_id}` for the final result.
      operationId: createVideo
      parameters:
        - $ref: '#/components/parameters/RequestId'
        - $ref: '#/components/parameters/SourceChannel'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/CreateVideoForm'
      responses:
        '200':
          description: Video generation accepted and queued.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VideoCreateResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/V1Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
components:
  parameters:
    RequestId:
      name: X-Request-Id
      in: header
      required: false
      description: >-
        Optional caller-supplied correlation ID, up to 128 characters. The
        service returns the effective value in the response header for tracing
        and support.
      schema:
        type: string
        minLength: 1
        maxLength: 128
    SourceChannel:
      name: X-Viggle-Source
      in: header
      required: false
      description: >-
        Optional source-channel label, up to 128 characters, used to attribute
        traffic to an SDK, integration, product surface, or internal workflow.
      schema:
        type: string
        minLength: 1
        maxLength: 128
  schemas:
    CreateVideoForm:
      description: |
        Body of `POST /v1/videos`, `multipart/form-data` only (character-
        animation mode below also accepts a plain `application/json` body —
        see `Generate Video (from Character Animation)`). Which fields are
        supplied selects the generation mode:

        - Neither `first_frame_image`/`first_frame_image_url` nor
          `last_frame_image`/`last_frame_image_url` nor any reference or
          character-animation field supplied: text-to-video.
        - A first frame supplied, no last frame: first-frame-to-video.
        - Both a first frame and a last frame supplied:
          first+last-frame-to-video.
        - Any of `reference_video`/`reference_video_url`/
          `reference_image`/`reference_image_url` supplied: reference-video
          (omni-reference) generation.
        - `driving_video_url` and/or `character_image_url`/
          `character_image_urls` supplied: character-animation generation,
          dispatched to a separate backend from the four modes above. It
          has no `quality` tier, no `duration_s`/`resolution`/
          `aspect_ratio`/`seed`, and `prompt` is optional rather than
          required; see below and `Generate Video (from Character
          Animation)` for its own fields, pricing, and constraints.

        Within a frame slot, supply at most one of the file and the URL
        form. Supplying both `first_frame_image` and `first_frame_image_url`
        (or both `last_frame_image` and `last_frame_image_url`) answers
        `400 INVALID_REQUEST` with "supply only one of first_frame_image or
        first_frame_image_url" (or the `last_frame_*` equivalent). A last
        frame requires a first frame; supplying a last frame with no first
        frame answers `400 INVALID_REQUEST` with "a last frame requires a
        first frame".

        Reference-video mode is selected purely by whether any of the four
        reference fields is present — there is no separate `source`/`mode`
        parameter, and no error if you meant to but forgot: supplying none
        of `reference_video`/`reference_video_url`/`reference_image`/
        `reference_image_url` simply generates text-to-video instead (the
        first bullet above), the same way omitting `first_frame_image`/
        `last_frame_image` falls back to text-to-video rather than
        erroring. `reference_video` and `reference_video_url` are declared
        as arrays for forward compatibility, but at most 1 reference video
        is accepted today; supplying any combination of the two fields that
        adds up to more than 1 (two files, two URLs, or one of each) answers
        `400 INVALID_REQUEST` with "at most 1 reference video(s) allowed".
        `reference_image` and `reference_image_url` may be mixed freely, but
        their combined count cannot exceed 4; going over answers
        `400 INVALID_REQUEST` with "at most 4 reference images are allowed".
        Reference fields cannot be combined with
        `first_frame_image`/`last_frame_image` (file or URL form); doing so
        answers `400 INVALID_REQUEST` with
        "reference_video/reference_image cannot be combined with
        first_frame_image/last_frame_image". The reference video itself
        (independent of `duration_s`, the target duration of the *generated*
        video) must be 0.5–600 seconds long and at least 64x64 with even
        width and height, the same constraints enforced on uploaded video
        elsewhere in this API.

        Character-animation mode cannot be combined with `quality` (or any
        frame/reference field above) — doing so answers
        `400 INVALID_REQUEST` with "driving_video_url/character_image_url
        cannot be combined with quality". At least one character image is
        required (`character_image_url` and `character_image_urls` are
        mutually exclusive ways to supply them — "supply only one of
        character_image_url or character_image_urls" otherwise); up to 4
        are accepted when `driving_video_url` is absent, but only 1 when
        it's present ("only one character_image_url is allowed when
        driving_video_url is provided"). `driving_video_url` itself is
        optional, single-valued, and — unlike every other video input in
        this API — checked against a narrower 5–10 second (inclusive)
        duration window rather than 0.5–600 seconds, on top of the same
        at-least-64x64/even-dimensions checks.

        Billed at ⌈`duration_s`⌉ × the per-second credit rate for `quality`
        (currently 1 credit/second — $0.01/sec — for both `low` and `high`).
        `resolution` and `aspect_ratio` do not affect price, and
        image/video-conditioned generation (first-frame, first-last-frame,
        or reference-video) costs the same as text-only for the same
        quality and duration. Character-animation mode is billed
        differently — a flat rate regardless of actual output duration; see
        `Generate Video (from Character Animation)`.

        Every generated video includes native audio; there is no separate
        audio flag or field.
      type: object
      properties:
        prompt:
          type: string
          minLength: 1
          description: >-
            Text description of the desired video. Required and non-empty for
            text, first-frame, first+last-frame, and reference-video modes.
            Optional (and merely a style/content hint, not enforced non-empty)
            for character-animation mode.
          example: A paper airplane gliding through a sunlit office
        quality:
          type: string
          enum:
            - low
            - high
          description: >-
            Generation quality tier — also selects the per-second credit rate
            charged for this request. `low` generates faster, for quicker
            iteration; `high` generates slower but produces higher-fidelity
            output. Both tiers are billed at the same $0.01/sec rate. Required
            for every mode except character-animation, which has no quality
            tiers and rejects this field outright if supplied.
          example: low
        first_frame_image:
          type: string
          format: binary
          description: >-
            First-frame image uploaded directly. Mutually exclusive with
            `first_frame_image_url`. Supplying either switches on
            first-frame-to-video mode. Cannot be combined with any
            `reference_video`/`reference_image` field.
        first_frame_image_url:
          type: string
          format: uri
          description: >-
            Publicly reachable URL of the first-frame image; the service fetches
            and re-hosts it. Mutually exclusive with `first_frame_image`. Cannot
            be combined with any `reference_video`/`reference_image` field.
        last_frame_image:
          type: string
          format: binary
          description: >-
            Last-frame image uploaded directly. Mutually exclusive with
            `last_frame_image_url`. Valid only alongside a first frame. Cannot
            be combined with any `reference_video`/`reference_image` field.
        last_frame_image_url:
          type: string
          format: uri
          description: >-
            Publicly reachable URL of the last-frame image; the service fetches
            and re-hosts it. Mutually exclusive with `last_frame_image`. Valid
            only alongside a first frame. Cannot be combined with any
            `reference_video`/`reference_image` field.
        reference_video:
          type: array
          maxItems: 1
          items:
            type: string
            format: binary
          description: >-
            Reference video uploaded directly, for reference-video
            (omni-reference) generation. Declared as an array for forward
            compatibility, but at most 1 is accepted today — combined with
            `reference_video_url`. Not required if at least one reference image
            is supplied instead. Must be 0.5–600 seconds long and at least 64x64
            with even width/height. Cannot be combined with
            `first_frame_image`/`last_frame_image`.
        reference_video_url:
          type: array
          maxItems: 1
          items:
            type: string
            format: uri
          description: >-
            Publicly reachable URL of the reference video; the service fetches
            and re-hosts it. Declared as an array for forward compatibility, but
            at most 1 is accepted today — combined with `reference_video`. Same
            duration/resolution constraints as `reference_video`.
        reference_image:
          type: array
          maxItems: 4
          items:
            type: string
            format: binary
          description: >-
            Reference images uploaded directly, for reference-video
            (omni-reference) generation. Up to 4 total, combined with
            `reference_image_url`. Can be freely mixed with
            `reference_image_url`.
        reference_image_url:
          type: array
          maxItems: 4
          items:
            type: string
            format: uri
          description: >-
            Publicly reachable URLs of reference images; the service fetches and
            re-hosts each one. Up to 4 total, combined with `reference_image`.
            Can be freely mixed with `reference_image`.
        driving_video_url:
          type: string
          format: uri
          description: >-
            Publicly reachable URL (`https://`) or internal `gs://` object of a
            driving video, for character-animation generation — its motion is
            applied to the character image(s). Optional and single-valued; a
            character-animation request can run on character images alone. Must
            be 5–10 seconds long (inclusive), at least 64x64, and even
            width/height. Selects character-animation mode together with any
            `character_image_url`/`character_image_urls`; cannot be combined
            with `quality` or any frame/reference field above.
        character_image_url:
          type: array
          items:
            type: string
            format: uri
          description: >-
            Character reference image URL(s) (`https://` or `gs://`),
            repeatable, for character-animation generation. Mutually exclusive
            with `character_image_urls` — supply one field or the other, not
            both. At least one character image is required across the two
            fields; up to 4 total when `driving_video_url` is absent, but only 1
            when it's present.
        character_image_urls:
          type: array
          items:
            type: string
            format: uri
          description: >-
            Same as `character_image_url`, supplied as one field instead of
            repeated ones — as a JSON array of strings on a multipart request,
            or natively on a JSON request body. Mutually exclusive with
            `character_image_url`.
        priority:
          type: integer
          description: >-
            Admission-queue priority tier, shared by H3 and character-animation
            modes. Defaults to 1000 (top tier) — most callers never need to set
            this. Must be one of the server's configured tiers (currently 1000
            or 0); an unsupported value answers `400 INVALID_REQUEST` with
            "unsupported priority".
          default: 1000
        duration_s:
          type: number
          minimum: 3
          maximum: 15
          default: 5
          description: Target video duration in seconds.
        resolution:
          type: string
          enum:
            - 480p
            - 768p
            - 1080p
          default: 768p
          description: Output resolution tier.
        aspect_ratio:
          type: string
          enum:
            - '16:9'
            - '9:16'
            - '1:1'
            - '4:3'
            - '3:4'
            - '21:9'
          default: '16:9'
          description: Output aspect ratio.
        seed:
          type: integer
          minimum: 0
          description: >-
            Deterministic generation seed. Omit for a random seed. H3 modes only
            — not applicable to character-animation mode.
        watermark:
          type: boolean
          default: false
          description: Whether to burn in a Viggle watermark.
    VideoCreateResponse:
      description: |
        Acceptance acknowledgment for `POST /v1/videos`, shared by all five
        generation modes. This is a queued confirmation, not the final
        result — poll `GET /v1/videos/{video_id}` (or watch it via
        `GET /v1/videos`) until `status` reaches `ready`, then read
        `video_url` from that response.
      type: object
      additionalProperties: false
      required:
        - id
        - status
        - progress
        - created_at
      properties:
        id:
          type: string
          description: >-
            Public video ID — `vid_`-prefixed for the four H3 modes,
            `anim_`-prefixed for character-animation mode.
          minLength: 1
        status:
          description: Lifecycle state at acceptance time; always `queued`.
          allOf:
            - $ref: '#/components/schemas/ResourceStatus'
        progress:
          type: integer
          description: Always null on acceptance.
          nullable: true
          minimum: 0
          maximum: 100
        created_at:
          type: string
          description: ISO 8601 creation timestamp, second precision.
          nullable: true
          format: date-time
    ResourceStatus:
      description: |
        The public lifecycle shared by every asynchronous resource. It carries
        the same values as `RenderStatus`.
      type: string
      enum:
        - queued
        - processing
        - ready
        - failed
        - cancelled
    ErrorResponse:
      type: object
      additionalProperties: false
      required:
        - error
      properties:
        error:
          description: >-
            Top-level draft-flow error payload containing the stable code,
            message, retry guidance, correlation ID, and error-specific details.
          allOf:
            - $ref: '#/components/schemas/ErrorBody'
    ErrorBody:
      type: object
      additionalProperties: false
      required:
        - code
        - message
        - retryable
        - request_id
        - details
        - remediation
      properties:
        code:
          description: Stable error category from the draft-based render error vocabulary.
          allOf:
            - $ref: '#/components/schemas/ErrorCode'
        message:
          type: string
          description: >-
            Human-readable explanation suitable for logs or presentation to a
            developer.
        retryable:
          type: boolean
          description: >-
            Whether retrying the operation without changing its semantic input
            may succeed.
        request_id:
          type: string
          description: >-
            Correlation identifier for tracing and support. Include it when
            reporting the failure.
        details:
          type: object
          description: Error-specific structured context; available keys depend on `code`.
          additionalProperties: true
        remediation:
          description: Suggested recovery action and optional retry delay.
          allOf:
            - $ref: '#/components/schemas/ErrorRemediation'
    ErrorCode:
      description: |
        The failure vocabulary of the draft-based render flow. The resource
        operations project the same vocabulary onto the lower_snake_case codes
        described by `ResourceError`.
      type: string
      enum:
        - UNAUTHENTICATED
        - INVALID_CREDENTIAL
        - FORBIDDEN
        - INVALID_REQUEST
        - INVALID_CHARACTER
        - INVALID_MOTION
        - UNSUPPORTED_MEDIA
        - CONTENT_POLICY_VIOLATION
        - UPLOAD_REQUIRED
        - UPLOAD_MISMATCH
        - UPLOAD_EXPIRED
        - DRAFT_NOT_FOUND
        - DRAFT_ALREADY_CONSUMED
        - RENDER_NOT_FOUND
        - RENDER_NOT_CANCELLABLE
        - CHARACTER_NOT_FOUND
        - MOTION_NOT_FOUND
        - MOTION_NOT_READY
        - MOTION_TEMPLATE_NOT_FOUND
        - AVATAR_NOT_FOUND
        - ANIMATION_NOT_FOUND
        - EXTRACTION_UNAVAILABLE
        - EXTRACTION_FAILED
        - IDEMPOTENCY_KEY_REUSED
        - INSUFFICIENT_CREDITS
        - RATE_LIMITED
        - SERVICE_BUSY
        - INTERNAL_ERROR
    ErrorRemediation:
      type: object
      additionalProperties: false
      required:
        - action
        - retry_after_ms
      properties:
        action:
          type: string
          description: >-
            Suggested machine-readable or human-readable next action, such as
            retrying later, uploading media again, or correcting input.
        retry_after_ms:
          type: integer
          description: >-
            Recommended delay in milliseconds before retrying; null when no
            timed retry is advised.
          nullable: true
          format: int64
          minimum: 0
  headers:
    RequestId:
      description: Stable request identifier for support and tracing.
      schema:
        type: string
  responses:
    BadRequest:
      description: Invalid request.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    V1Unauthorized:
      description: Missing, invalid, or expired credential.
      headers:
        WWW-Authenticate:
          schema:
            type: string
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    PaymentRequired:
      description: Insufficient credits.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: Credential does not grant access to this resource.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    RateLimited:
      description: Rate limit exceeded.
      headers:
        Retry-After:
          schema:
            type: integer
            minimum: 0
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    InternalServerError:
      description: Unexpected server error.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    ServiceUnavailable:
      description: Service temporarily unavailable.
      headers:
        Retry-After:
          schema:
            type: integer
            minimum: 0
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key or OAuth access token
      description: |
        Server-side SDK clients use a project API key. Remote MCP clients use
        an OAuth access token. Never expose a project API key in browser code.

````