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

# Using Viggle-Animate

> Generate a character-animation video from character images — up to four on their own, or up to five with a driving video.

Send `multipart/form-data` — or a plain `application/json` body — with one or more character images (`character_image`/`character_image_url`/`character_image_urls`) and, optionally, a driving video (`driving_video`/`driving_video_url`). Supplying any of these fields selects the Viggle-Animate character-animation mode of `POST /v1/videos`.

<Note>
  A driving video is optional. Without one, the request may contain up to four character images, all references of the same character. With a driving video, it may contain up to five — each image replaces one person in the driving video. This is also the only mode that accepts a plain JSON body as an alternative to `multipart/form-data`.
</Note>

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 Reference Video/Image)](/v1/api-reference/videos/create-from-reference-video) — which mode runs is selected by which fields you send, not by a separate parameter. Character-animation fields cannot be combined with `quality` or any frame/reference field from the other four modes; doing so answers `400 INVALID_REQUEST` with "driving\_video\_url/character\_image\_url cannot be combined with quality".

<Warning>
  Character-animation mode is billed at **1 credit ($0.01) per second of the driving video**, rounded up (5–16 credits), or a fixed **11 credits ($0.11)** without a driving video. There's no caller-supplied `duration_s` for this mode, and no `quality`, `resolution`, `aspect_ratio`, or `seed`. See [Pricing](#pricing).
</Warning>

## Request parameters

Send `multipart/form-data`, or JSON (see below).

| Parameter | Type | Required | Default | Description |
| - | - | :-: | - | - |
| `driving_video` | file | No | — | Optional driving video uploaded directly. Mutually exclusive with `driving_video_url`. When present, up to five character images are allowed, one per person to replace. Must be 5–15 seconds, at least 64x64, with even dimensions. |
| `driving_video_url` | string (URI) | No | — | Optional `https://` driving-video URL. Mutually exclusive with `driving_video`. When present, up to five character images are allowed, one per person to replace. |
| `character_image` | file (repeatable) | One of | — | Uploaded character reference images: one to four without a driving video, one to five with one. Mutually exclusive with URL-form character images. |
| `character_image_url` | string (URI), repeatable | One of | — | Character image URLs: one to four without a driving video, one to five with one. Mutually exclusive with `character_image_urls` and uploaded images. |
| `character_image_urls` | array of string (URI) | One of | — | Same as `character_image_url`, supplied as one JSON-array field. One to four entries without a driving video; one to five with it. |
| `prompt` | string | No | — | Optional style/content guidance. Unlike every other mode, not required and not enforced non-empty. |
| `watermark` | boolean | No | `false` | Whether to burn in a Viggle watermark. Same field and convention as the other four modes. |
| `priority` | integer | No | `1000` | Admission-queue priority tier, shared with H3 modes. Must be one of the server's configured tiers (currently `1000` or `0`); an unsupported value answers `400 INVALID_REQUEST` — "unsupported priority", and a non-integer answers "priority must be an integer". Most callers never need to set this. |

A driving video is optional. At least one character image is always required. Without a driving video, up to four character images are accepted; with one, up to five. Going over answers `400 INVALID_REQUEST` — "at most 4 character images are allowed" (or "at most 5 ..." with a driving video).

### Driving video vs. character image

* **Driving video** (`driving_video` or `driving_video_url`): optional. If supplied, use exactly one of the file or URL form. It must be 5–15 seconds long (inclusive, truncated to whole seconds first), at least 64x64, with even width and height.
* **Character images** (`character_image`, `character_image_url`, or `character_image_urls`): at least one is required. Supply uploads or URLs, not both; the two URL spellings are mutually exclusive. Without a driving video, up to four images are accepted, all references of one character. With a driving video, up to five are accepted, and each image replaces one person in the video.

Every URL — driving video or character image — must be `https://`; anything else (a bare `http://` or a `gs://` path, for example) answers `400 INVALID_REQUEST` with "each character\_image\_url must be an https\:// URL" (or the `driving_video_url` equivalent). An uploaded `character_image` that isn't a valid, decodable image answers `400 INVALID_REQUEST` — "character\_image must be a valid image".

## JSON request body

Character-animation mode alone also accepts a plain JSON body (`Content-Type: application/json`) as an alternative to `multipart/form-data`, native arrays included. JSON requests can only carry URL-form fields — `driving_video`/`character_image` uploads have no bytes to attach to a JSON body, so use `driving_video_url` and `character_image_url`/`character_image_urls` instead:

```json theme={null}
{
  "driving_video_url": "https://example.test/driving.mp4",
  "character_image_url": "https://example.test/char1.png",
  "prompt": "keep the same outfit and background",
  "watermark": false
}
```

The request body must be a single JSON object containing `character_image_url` or `character_image_urls`; `driving_video_url` is optional. An empty or malformed body answers `400 INVALID_REQUEST` — "request body must be a JSON object" (or "JSON animation requests require character images" when no character-image field is present).

Since JSON object keys cannot repeat, use `character_image_urls` when sending two to four character images in JSON.

## Response parameters

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

| Field | Type | Always present | Description |
| - | - | :-: | - |
| `id` | string | Yes | Public video ID, `anim_`-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": "anim_7c1e4b",
  "status": "queued",
  "progress": null,
  "created_at": "2026-09-09T09:12:03Z"
}
```

This response deliberately carries none of the fields `GET /v1/videos/{video_id}` returns (`video_url`, `completed_at`, `error`). See [Get Video](/v1/api-reference/videos/get) for the full shape once the video is processing or done — character-animation's response there carries no `seed` (H3-only) alongside the fields every video shares.

## Pricing

Character-animation mode is billed at **1 credit (\$0.01) per second**, rounded up. What the request reserves depends on whether it has a driving video:

* **With a driving video**, the service measures the driving video's duration at submission and reserves ⌈duration⌉ credits — **5–16 credits ($0.05–$0.16)**. The duration check truncates to whole seconds before comparing against 5–15, so a 15.9-second clip is accepted and reserves 16.
* **Without a driving video** (images only), there's nothing to measure, so every request reserves a fixed **11 credits (\$0.11)**.

Once the video is `ready`, the charge is settled against the finished video's duration at the same rate, capped at the reservation — settlement can only refund, never charge more than was reserved. A `failed` task is refunded in full. `watermark` does not affect price. See [Pricing and retention](/v1/pricing).

## Examples

<CodeGroup>
  ```bash cURL (image-only, multiple characters) theme={null}
  curl https://apis.viggle.ai/v1/videos \
    -H "Authorization: Bearer $VIGGLE_API_KEY" \
    -F character_image_url=https://example.test/char1.png \
    -F character_image_url=https://example.test/char2.png
  ```

  ```bash cURL (multipart, driving video + character image URLs) theme={null}
  curl https://apis.viggle.ai/v1/videos \
    -H "Authorization: Bearer $VIGGLE_API_KEY" \
    -F driving_video_url=https://example.test/driving.mp4 \
    -F character_image_url=https://example.test/char1.png
  ```

  ```bash cURL (multipart, uploaded files) theme={null}
  curl https://apis.viggle.ai/v1/videos \
    -H "Authorization: Bearer $VIGGLE_API_KEY" \
    -F prompt="keep the same outfit and background" \
    -F driving_video=@driving.mp4 \
    -F character_image=@char1.png
  ```

  ```bash cURL (JSON body) theme={null}
  curl https://apis.viggle.ai/v1/videos \
    -H "Authorization: Bearer $VIGGLE_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
          "driving_video_url": "https://example.test/driving.mp4",
          "character_image_url": "https://example.test/char1.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']}", "Content-Type": "application/json"},
      json={
          "driving_video_url": "https://example.test/driving.mp4",
          "character_image_url": "https://example.test/char1.png",
      },
  )
  response.raise_for_status()
  video = response.json()
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://apis.viggle.ai/v1/videos", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.VIGGLE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      driving_video_url: "https://example.test/driving.mp4",
      character_image_url: "https://example.test/char1.png",
    }),
  });
  const video = await response.json();
  ```
</CodeGroup>

## Common errors

| Error | Cause |
| - | - |
| `400 INVALID_REQUEST` — "character images are required" | Neither `character_image_url` nor `character_image_urls` supplied, and no `character_image` uploaded. |
| `400 INVALID_REQUEST` — "supply only one of character\_image\_url or character\_image\_urls" | Both URL fields supplied together. |
| `400 INVALID_REQUEST` — "supply character images as uploads or as character\_image\_url/character\_image\_urls, not both" | A `character_image` upload supplied alongside `character_image_url`/`character_image_urls`. |
| `400 INVALID_REQUEST` — "at most 4 character images are allowed" | More than four character images supplied without a driving video. |
| `400 INVALID_REQUEST` — "at most 5 character images are allowed" | More than five character images supplied together with a driving video. |
| `400 INVALID_REQUEST` — "each character\_image\_url must be an https\:// URL" (or "...character\_image\_urls...", matching whichever field you used) | A character image URL is not an `https://` URL. |
| `400 INVALID_REQUEST` — "character\_image must be a valid image" | An uploaded `character_image` file couldn't be decoded as an image. |
| `400 INVALID_REQUEST` — "supply only one of driving\_video or driving\_video\_url" | Both the uploaded file and URL form of the driving video supplied together. |
| `400 INVALID_REQUEST` — "driving\_video\_url must be an https\:// URL" | `driving_video_url` is not an `https://` URL. |
| `400 INVALID_REQUEST` — "driving\_video\_url must be a single URL" | `driving_video_url` supplied more than once. |
| `400 INVALID_REQUEST` — "failed to fetch driving\_video\_url" | The driving video URL couldn't be fetched. |
| `400 INVALID_REQUEST` — "invalid or oversized driving\_video upload" | An uploaded `driving_video` was empty, unreadable, or exceeded the 100 MiB video limit. |
| `400 INVALID_REQUEST` — "driving\_video\_url duration must be between 5 and 15 seconds (inclusive)" | The driving video (uploaded or fetched from `driving_video_url`) was outside the 5–15 second window — the message names `driving_video_url` even when the video was uploaded via `driving_video`. |
| `400 INVALID_REQUEST` — "driving\_video\_url/character\_image\_url cannot be combined with quality" | `quality` (or another H3-mode field) supplied alongside a character-animation field. |
| `400 INVALID_REQUEST` — "unsupported priority" | `priority` set to a value outside the server's configured tiers. |
| `400 INVALID_REQUEST` — "priority must be an integer" | `priority` isn't parseable as an integer. |

## Next step

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

<CardGroup cols={2}>
  <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 Reference Video/Image)" icon="clapperboard" href="/v1/api-reference/videos/create-from-reference-video">
    Carry a subject or style from a reference video and/or up to 4 images, on the H3 backend instead.
  </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 video from text, frames, or references (`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.
  - name: Images
    description: |
      Viggle Image — generate 1–6 PNG images from a text prompt with the
      `qwen` model, optionally guided by up to 8 reference images
      (`img_` prefix), and retrieve them by ID.
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 driving video and a character image
      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`/`driving_video_url` and/or `character_image`/
          `character_image_url`/`character_image_urls` supplied:
          character-animation generation — animates up to 4 character
          images, or with an optional driving video, replaces up to 5
          people in it with the supplied characters.
          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

        per-second rate for a given `resolution`, and `enhance` does not

        affect price either — see `CreateVideoForm` below.

        Character-animation mode has no `quality` tier and is billed at

        $0.01/sec (1 credit/second) of the driving video's duration,

        rounded up, instead — or a flat 11 credits without a driving

        video.


        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`/`driving_video_url` and/or `character_image`/
          `character_image_url`/`character_image_urls` supplied:
          character-animation generation, dispatched to a separate backend
          from the four modes above. A driving video is optional. Without
          one, up to 4 character images are accepted, all references of one
          character; with one, up to 5 are accepted, each replacing one
          person in the driving video. 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, and at most 100 MiB whether uploaded or
        fetched from `reference_video_url`.

        Frame and reference images (`first_frame_image`, `last_frame_image`,
        `reference_image`) may be up to 20 MiB each when uploaded, and up to
        30 MiB when fetched from the matching `*_url` field. The format is
        detected from the image bytes — JPEG, PNG, WebP, GIF, BMP, TIFF, or
        HEIC/HEIF/AVIF — and there is no minimum or maximum dimension; an
        image at or above 4K (long edge >= 3840 px or short edge >= 2160 px)
        is downscaled to fit 1920x1080, keeping its aspect ratio. An
        oversized upload answers `400 INVALID_REQUEST` with "invalid or
        oversized first_frame_image upload" (naming the field), an
        undecodable one with "first_frame_image must be a valid image"; a
        URL that can't be decoded answers "first_frame_image_url is not a
        valid image", and an unreadable reference video URL answers
        "reference_video_url is not a valid video".

        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". A driving video is optional; when
        supplied, use `driving_video` (an uploaded file) or
        `driving_video_url`, not both. It is checked against a 5–15 second
        (inclusive) duration window (truncated to whole seconds first, so
        15.9s is accepted as 15s), on top of the same at-least-64x64 and
        even-dimension checks. At least one character image is required, as
        `character_image` uploads or values supplied through
        `character_image_url`/`character_image_urls`
        — uploads and URL fields cannot be combined ("supply character
        images as uploads or as character_image_url/character_image_urls,
        not both"), `character_image_url`/`character_image_urls` remain
        mutually exclusive with each other ("supply only one of
        character_image_url or character_image_urls"). Up to 4 images are
        accepted without a driving video, and up to 5 with one; going over
        answers `400 INVALID_REQUEST` with "at most 4 character images are
        allowed" (or "at most 5 ..." with a driving video).

        Billed at ⌈`duration_s`⌉ × the per-second credit rate for
        `resolution` (currently 1 credit/second — $0.01/sec — at `480p`,
        `768p`, `1080p`, and `2k`, rising to 5 credits/second — $0.05/sec —
        at `4k`; the same rate applies to both `quality` tiers). `quality`,
        `aspect_ratio`, and `enhance` 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, resolution, and duration. Character-animation mode is billed
        at 1 credit/second instead: with a driving video it reserves the
        driving video's measured duration rounded up (5–16 credits); without
        one it reserves a flat 11 credits. On completion the charge is
        settled against the finished video's duration, never above the
        reservation. It is not tied to `duration_s`; 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. `low` generates faster, for quicker
            iteration; `high` generates slower but produces higher-fidelity
            output. Both tiers are billed at the same per-second rate for a
            given `resolution` — see `resolution` below. 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. Up to 20 MiB; JPEG, PNG,
            WebP, GIF, BMP, TIFF, or HEIC/HEIF/AVIF, detected from the file
            contents. An image with a long edge of 3840 px or more, or a short
            edge of 2160 px or more, is downscaled to fit 1920x1080, keeping its
            aspect ratio.
        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. The
            fetched image may be up to 30 MiB, in the same formats and with the
            same downscaling as the upload form.
        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. Up
            to 20 MiB; JPEG, PNG, WebP, GIF, BMP, TIFF, or HEIC/HEIF/AVIF,
            detected from the file contents. An image with a long edge of 3840
            px or more, or a short edge of 2160 px or more, is downscaled to fit
            1920x1080, keeping its aspect ratio.
        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. The fetched image may be
            up to 30 MiB, in the same formats and with the same downscaling as
            the upload form.
        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. Up to 100 MiB, in any container with a readable
            video stream (MP4, MOV, WebM, etc.). 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
            size, duration, and 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`. Up to 20 MiB; JPEG, PNG, WebP, GIF, BMP,
            TIFF, or HEIC/HEIF/AVIF, detected from the file contents. An image
            with a long edge of 3840 px or more, or a short edge of 2160 px or
            more, is downscaled to fit 1920x1080, keeping its aspect ratio.
        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`. Each fetched image may
            be up to 30 MiB, in the same formats and with the same downscaling
            as `reference_image`.
        driving_video:
          type: string
          format: binary
          description: >-
            Optional driving video uploaded directly for character-animation
            generation. Mutually exclusive with `driving_video_url`. When
            present, the request may carry up to 5 character images, each
            replacing one person in the video; it is validated for 5–15 second
            duration, at least 64x64, and even dimensions, then staged to object
            storage.
        driving_video_url:
          type: string
          format: uri
          description: >-
            Optional `https://` driving-video URL for character-animation
            generation. Mutually exclusive with `driving_video`. When present,
            the request may carry up to 5 character images, each replacing one
            person in the video; it must be 5–15 seconds long, at least 64x64,
            with even dimensions.
        character_image:
          type: array
          maxItems: 5
          items:
            type: string
            format: binary
          description: >-
            Character reference images uploaded directly for character-animation
            generation. Up to 4 are accepted without a driving video, or 5 when
            a driving video is present. Mutually exclusive with URL-form
            character images.
        character_image_url:
          type: array
          maxItems: 5
          items:
            type: string
            format: uri
          description: >-
            Repeatable character reference image `https://` URL. Up to 4 are
            accepted without a driving video, or 5 when a driving video is
            present. Mutually exclusive with `character_image_urls` and uploaded
            character images.
        character_image_urls:
          type: array
          maxItems: 5
          items:
            type: string
            format: uri
          description: >-
            Same as `character_image_url`, supplied as one JSON-array field. Up
            to 4 are accepted without a driving video, or 5 when a driving video
            is present. Mutually exclusive with `character_image_url` and
            uploaded character images.
        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 integer answers `400 INVALID_REQUEST` with
            "invalid render request: unsupported priority 500" (ending with the
            value sent) in H3 modes, or "unsupported priority" in
            character-animation mode; a non-integer answers "priority must be an
            integer".
          default: 1000
        duration_s:
          type: number
          minimum: 5
          maximum: 15
          default: 5
          description: >-
            Requested video duration in seconds, between 5 and 15 inclusive.
            Defaults to 5.
        resolution:
          type: string
          enum:
            - 480p
            - 768p
            - 1080p
            - 2k
            - 4k
          default: 768p
          description: >-
            Output resolution tier — also selects the per-second credit rate
            charged for this request. `480p`, `768p`, `1080p`, and `2k` are
            billed at $0.01/sec (1 credit/second); `4k` is billed at $0.05/sec
            (5 credits/second).
        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
          format: int64
          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.
        enhance:
          type: boolean
          default: true
          description: >-
            Whether to run a low-effort AI Planner pass that rewrites only
            `prompt` before generation; any supplied frame or reference
            video/image is left unchanged. H3 modes only — not applicable to
            character-animation mode. Defaults to `true`; pass `false` to use
            `prompt` exactly as given. Does not affect price.
    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
        - NOT_FOUND
        - TASK_FAILED
    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.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.