> ## Documentation Index
> Fetch the complete documentation index at: https://dripart-matt-sdk-0-3-0-run-kind.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Use HappyHorse 1.1 R2V with Comfy Router

> Python, TypeScript and cURL snippets for calling wan/happyhorse-1.1-r2v over HTTP through Comfy Router, plus the request fields and the result shape

API Reference for HappyHorse 1.1 R2V. HappyHorse 1.1 reference-to-video keeps the subjects of up to nine reference images consistent across a new scene, addressed in the prompt as character1, character2 and so on.

## Quick start

Create a key in [your Comfy workspace](https://platform.comfy.org/profile/api-keys) and export it as `COMFY_API_KEY`. The Python and TypeScript snippets use the Comfy SDKs (`pip install comfy-sdk`, `npm install @comfyorg/sdk`); the cURL snippet is the same call over raw HTTP.

**Model ID:** `wan/happyhorse-1.1-r2v`

**Endpoint:** `POST https://api.comfy.org/v2/models/wan/happyhorse-1.1-r2v`

<Tabs>
  <Tab title="Wait for the result">
    <CodeGroup>
      ```python Python theme={null}
      from comfy_sdk import Comfy

      # Reads COMFY_API_KEY from the environment.
      # The SDK automatically creates an idempotency key and reuses it for automatic retries.
      with Comfy() as client:
          result = client.models.run(
              "wan/happyhorse-1.1-r2v",
              {
                  "input": {
                      "prompt": "character1 walks in from the left, turns to the camera and waves, plain orange backdrop",
                      "media": [
                          {
                              "type": "reference_image",
                              "url": "https://raw.githubusercontent.com/Comfy-Org/workflow_templates/main/input/pink_robot_front.png",
                          },
                      ],
                  },
                  "parameters": {
                      "resolution": "720P",
                      "duration": 5,
                  },
              },
          )

      print("video:", result["output"]["video_url"])
      ```

      ```typescript TypeScript theme={null}
      import { comfy } from "@comfyorg/sdk";

      // Reads COMFY_API_KEY from the environment.
      // The SDK automatically creates an idempotency key and reuses it for automatic retries.
      type Result = { output: { video_url: string } };
      const result = await comfy.models.run<Result>("wan/happyhorse-1.1-r2v", {
        input: {
          prompt: "character1 walks in from the left, turns to the camera and waves, plain orange backdrop",
          media: [
            {
              type: "reference_image",
              url: "https://raw.githubusercontent.com/Comfy-Org/workflow_templates/main/input/pink_robot_front.png",
            },
          ],
        },
        parameters: {
          resolution: "720P",
          duration: 5,
        },
      });
      if (result.kind !== "json") throw new Error("expected a JSON result");

      console.log("video:", result.data.output.video_url);
      ```

      ```bash cURL theme={null}
      curl https://api.comfy.org/v2/models/wan/happyhorse-1.1-r2v \
        -H "X-API-Key: $COMFY_API_KEY" \
        -H "Idempotency-Key: $(uuidgen)" \
        -H "Content-Type: application/json" \
        -d "{\"input\": {\"prompt\":\"character1 walks in from the left, turns to the camera and waves, plain orange backdrop\",\"media\":[{\"type\":\"reference_image\",\"url\":\"https://raw.githubusercontent.com/Comfy-Org/workflow_templates/main/input/pink_robot_front.png\"}]}, \"parameters\": {\"resolution\":\"720P\",\"duration\":5}}"
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Queue and collect later">
    <Note>
      Queued delivery is rolling out per workspace. Until yours is enabled, the submit route answers `403` with `X-Comfy-Error-Type: not_enabled`. Nothing about the request is wrong, and the same body works through the synchronous route in the meantime.
    </Note>

    The same body, sent to `POST https://api.comfy.org/v2/models/wan/happyhorse-1.1-r2v/requests`. Router answers `201` with a `request_id` as soon as the run is admitted, and the result is collected once it is ready, from this process or another one. [Queued delivery](/development/comfy-router/queue) walks through status, cancellation and collection.

    <CodeGroup>
      ```python Python theme={null}
      from comfy_sdk import Comfy

      # Reads COMFY_API_KEY from the environment.
      # Each submit() call mints its own Idempotency-Key and reuses it for automatic retries.
      with Comfy() as client:
          handle = client.models.submit(
              "wan/happyhorse-1.1-r2v",
              {
                  "input": {
                      "prompt": "character1 walks in from the left, turns to the camera and waves, plain orange backdrop",
                      "media": [
                          {
                              "type": "reference_image",
                              "url": "https://raw.githubusercontent.com/Comfy-Org/workflow_templates/main/input/pink_robot_front.png",
                          },
                      ],
                  },
                  "parameters": {
                      "resolution": "720P",
                      "duration": 5,
                  },
              },
          )
          print("request_id:", handle.request_id)  # with the model ID, all another process needs

          # Poll until the request completes, waiting the Retry-After the server names.
          for update in handle.iter_events():
              print(update.status, update.queue_position)

          # The provider's own payload, the same value models.run() returns.
          # A request that failed or was cancelled raises the typed Router error here.
          result = handle.get()

      print("video:", result["output"]["video_url"])
      ```

      ```typescript TypeScript theme={null}
      import { comfy } from "@comfyorg/sdk";

      // Reads COMFY_API_KEY from the environment.
      // Each submit() call mints its own Idempotency-Key and reuses it for automatic retries.
      type Result = { output: { video_url: string } };
      const handle = await comfy.models.submit<Result>("wan/happyhorse-1.1-r2v", {
        input: {
          prompt: "character1 walks in from the left, turns to the camera and waves, plain orange backdrop",
          media: [
            {
              type: "reference_image",
              url: "https://raw.githubusercontent.com/Comfy-Org/workflow_templates/main/input/pink_robot_front.png",
            },
          ],
        },
        parameters: {
          resolution: "720P",
          duration: 5,
        },
      });
      console.log("requestId:", handle.requestId); // with the model ID, all another process needs

      // Poll until the request completes, waiting the Retry-After the server names.
      for await (const update of handle.events()) {
        console.log(update.status, update.queuePosition);
      }

      // The same result models.run() returns. A request that failed or was cancelled rejects here.
      const result = await handle.get();
      if (result.kind !== "json") throw new Error("expected a JSON result");

      console.log("video:", result.data.output.video_url);
      ```

      ```bash cURL theme={null}
      # 1. Submit. Router answers 201 with request_id, status_url, response_url and cancel_url.
      curl https://api.comfy.org/v2/models/wan/happyhorse-1.1-r2v/requests \
        -H "X-API-Key: $COMFY_API_KEY" \
        -H "Idempotency-Key: $(uuidgen)" \
        -H "Content-Type: application/json" \
        -d "{\"input\": {\"prompt\":\"character1 walks in from the left, turns to the camera and waves, plain orange backdrop\",\"media\":[{\"type\":\"reference_image\",\"url\":\"https://raw.githubusercontent.com/Comfy-Org/workflow_templates/main/input/pink_robot_front.png\"}]}, \"parameters\": {\"resolution\":\"720P\",\"duration\":5}}"

      # 2. Poll until status is COMPLETED, waiting the Retry-After seconds each response names.
      REQUEST_ID="<request_id from the 201 body>"
      curl -i https://api.comfy.org/v2/models/wan/happyhorse-1.1-r2v/requests/$REQUEST_ID/status \
        -H "X-API-Key: $COMFY_API_KEY"

      # 3. Collect. 200 with the model's native output, 202 with the status body while it is still running.
      curl https://api.comfy.org/v2/models/wan/happyhorse-1.1-r2v/requests/$REQUEST_ID \
        -H "X-API-Key: $COMFY_API_KEY"
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## Schema

### Input

<ParamField body="input" type="object" required>
  Enter basic information, such as prompt words, etc.
</ParamField>

<ParamField body="input.audio_url" type="string">
  Audio file download URL. Supported formats: mp3 and wav. Cannot be used with reference\_video\_urls.
</ParamField>

<ParamField body="input.img_url" type="string">
  First frame image URL or Base64 encoded data.
  Required for the wan2.5-i2v-preview and wan2.6-i2v models only. The
  happyhorse-1.x i2v spellings do NOT take their first frame here: they
  take it as a `media` element of type `first_frame` (see `media` below),
  and an `img_url` body that succeeds on wan2.6-i2v is refused by the
  provider on happyhorse-1.0-i2v and happyhorse-1.1-i2v.
  Image formats: JPEG, JPG, PNG, BMP, WEBP. Resolution: 360-2000 pixels.
  File size: max 10MB.
</ParamField>

<ParamField body="input.media" type="object[]">
  Media asset list for the wan2.7, wan3.0 and happyhorse-1.x models. Specifies reference
  materials (image, audio, video) for video generation. Each element contains a type and
  url field.
  Supported type values vary by model:

  * wan2.7-i2v: first\_frame, last\_frame, driving\_audio, first\_clip
  * wan2.7-r2v: reference\_image, reference\_video
  * wan2.7-videoedit: video, reference\_image
  * wan3.0-video: first\_frame (max 1), last\_frame (max 1), reference\_image (max 10),
    reference\_video (max 5 clips, total duration \<= 15s), reference\_audio (max 5 clips,
    total duration \<= 15s), file (max 1, cannot be used with link), link (max 1, cannot
    be used with file). The reference\_\*/file/link types and first\_frame/last\_frame types
    are mutually exclusive within the same request. The array order defines the reference
    order of assets in the prompt (Image 1, Video 1, Audio 1, ...).
  * happyhorse-1.x-i2v: first\_frame only, exactly 1. At least 300x300 pixels,
    JPEG/JPG/PNG/WEBP, max 20MB, a public URL or a data:\{MIME\_type};base64,... URL.
    These spellings take NO img\_url; the first frame goes here.
  * happyhorse-1.x-r2v: reference\_image only, 1 to 9 of them. Shortest side at least
    400 pixels, max 20MB, a public URL or a data: URL. reference\_video is NOT an input
    type for this operation.
  * happyhorse-1.x-video-edit: video
    The per-asset "max 20MB" figures above are the PARTNER's ceiling on the image it
    ends up with, and they apply as written when the asset is a public URL, because
    those bytes never travel through Comfy. An INLINE data: URL does travel through
    Comfy and meets a lower transport ceiling first: a Comfy Router POST body is
    capped at 10 MiB in total and answered 413 past it, and base64 inflates a payload
    by about 4/3, so a single inline asset above roughly 7.5 MiB is refused before
    any of the partner rules here are reached — sooner still when the body carries
    several of them, since the 10 MiB bounds the WHOLE request rather than each
    element. Send anything near these ceilings as a public URL.
</ParamField>

<ParamField body="input.media[].type" type="string" required>
  Media asset type

  Possible values: `first_frame`, `last_frame`, `driving_audio`, `first_clip`, `reference_image`, `reference_video`, `reference_audio`, `video`, `file`, `link`
</ParamField>

<ParamField body="input.media[].url" type="string" required>
  URL of the media file: a public HTTP/HTTPS URL, an OSS temporary URL, or — where the model's entry in the `media` description above says so, as the happyhorse-1.x i2v and r2v spellings do — an inline `data:{MIME_type};base64,...` URL. See that description for the per-model size and pixel floors, and for the 10 MiB Router request-body cap that bounds an inline payload before any partner rule applies.
</ParamField>

<ParamField body="input.negative_prompt" type="string">
  Reverse prompt words are used to describe content that you do not want to see in the video screen
</ParamField>

<ParamField body="input.prompt" type="string">
  Text prompt words. Support Chinese and English, length not exceeding 800 characters
  (up to 20,000 characters for wan3.0-video; content exceeding the limit is truncated).
  For wan2.6-r2v with multiple reference videos, use 'character1', 'character2', etc. to refer to subjects
  in the order of reference videos. Example: "Character1 sings on the roadside, Character2 dances beside it"
  For wan3.0-video reference mode, use 'Image 1', 'Video 1', 'Audio 1', etc. to refer to media assets
  in the corresponding order within the media array.
</ParamField>

<ParamField body="input.reference_video_urls" type="string[]">
  Reference video URLs for wan2.6-r2v model only. Array of 1-3 video URLs.
  Input restrictions:

  * Format: mp4, mov
  * Quantity: 1-3 videos
  * Single video length: 2-30 seconds
  * Single file size: max 30MB
  * Cannot be used with audio\_url
    Reference duration: Single video max 5s, two videos max 2.5s each, three videos proportionally less.
    Billing: Based on actual reference duration used.
</ParamField>

<ParamField body="input.template" type="string">
  Video effect template name. Optional. Currently supported: squish, flying, carousel. When used, prompt parameter is ignored.
</ParamField>

<ParamField body="model" type="string">
  The ID of the model to call. NOT constrained on this component: Comfy Router fills it from the `{model}` path segment of `POST /v2/models/wan/{model}`, so a Router caller omits it. A direct v1 call to `POST /proxy/wan/api/v1/services/aigc/video-generation/video-synthesis` MUST supply it, and the enum of accepted spellings lives on that operation's own component, `WanVideoGenerationRequest`.
</ParamField>

<ParamField body="parameters" type="object">
  Video processing parameters
</ParamField>

<ParamField body="parameters.audio" type="boolean" default="true">
  Whether to add audio to the video
</ParamField>

<ParamField body="parameters.audio_setting" type="string" default="&#x22;auto&#x22;">
  Video audio setting for wan2.7-videoedit model.

  * auto (default): Model intelligently judges based on prompt content
  * origin: Forcefully preserve the original audio from the input video

    Possible values: `auto`, `origin`
</ParamField>

<ParamField body="parameters.duration" type="integer" default="5">
  The duration of the video generated, in seconds:

  * wan2.5 models: 5 or 10 seconds
  * wan2.6-t2v, wan2.6-i2v: 5, 10, or 15 seconds
  * wan2.6-r2v: 5 or 10 seconds only (no 15s support)
  * wan2.7-i2v, wan2.7-t2v: integer in \[2, 15]
  * wan2.7-r2v, wan2.7-videoedit: integer in \[2, 10]
  * wan3.0-video: integer in \[2, 30] without video input; with video input the total
    input video duration + output video duration must not exceed 30 seconds; -1 enables
    smart duration mode where the model picks a suitable duration

    Range: `-1` to `30`
</ParamField>

<ParamField body="parameters.prompt_extend" type="boolean" default="true">
  Is it enabled prompt intelligent rewriting. Default is true
</ParamField>

<ParamField body="parameters.ratio" type="string">
  Aspect ratio of the generated video. For wan2.7 and wan3.0 models only.
  For wan2.7 models, defaults based on the resolution tier if not provided.
  For wan3.0-video, adaptive (the default) automatically recommends a suitable
  aspect ratio based on the input media proportions and intent.

  Possible values: `adaptive`, `16:9`, `9:16`, `1:1`, `4:3`, `3:4`
</ParamField>

<ParamField body="parameters.resolution" type="string">
  Resolution level. Supported values vary by model:

  * wan2.5-i2v-preview: 480P, 720P, 1080P
  * wan2.6-i2v: 720P, 1080P only (no 480P support)
  * wan2.7 models (i2v, t2v, r2v, videoedit): 720P, 1080P (default 1080P)
  * wan3.0-video, wan3.0-video-prime: 480P, 720P, 1080P (upstream default 1080P)
    This proxy rejects video generation requests that provide neither resolution
    nor size, because the resolution tier selects the billing rate.

    Possible values: `480P`, `720P`, `1080P`
</ParamField>

<ParamField body="parameters.seed" type="integer">
  Random number seed, used to control the randomness of the model generated content

  Range: `0` to `2147483647`
</ParamField>

<ParamField body="parameters.shot_type" type="string" default="&#x22;single&#x22;">
  Intelligent multi-lens control. Only active when prompt\_extend is enabled.
  For wan2.6 and wan2.7-r2v models.

  * single: Single-shot video (default)
  * multi: Multi-shot video

    Possible values: `multi`, `single`
</ParamField>

<ParamField body="parameters.size" type="string">
  Video resolution in format width*height. Supported resolutions vary by model:
  For wan2.5 T2V: 480P (480*832, 832*480, 624*624), 720P, 1080P sizes
  For wan2.6 T2V/R2V (no 480P):
  720P: 1280*720, 720*1280, 960*960, 1088*832, 832*1088
  1080P: 1920*1080, 1080*1920, 1440*1440, 1632*1248, 1248*1632
</ParamField>

<ParamField body="parameters.watermark" type="boolean" default="false">
  Whether to add a watermark logo, the watermark is located in the lower right corner
</ParamField>

Generated from the schema Router serves at `GET /v2/models/wan/happyhorse-1.1-r2v/openapi.json`, the same document it validates a call against before the request reaches the provider.

### Output

<ResponseField name="output" type="object" required />

<ResponseField name="output.actual_prompt" type="string">
  Actual prompt after intelligent rewriting (for video tasks)
</ResponseField>

<ResponseField name="output.check_audio" type="string">
  Audio URL for I2V tasks with audio generation
</ResponseField>

<ResponseField name="output.code" type="string">
  The error code for the failed request (not returned if request is successful)
</ResponseField>

<ResponseField name="output.end_time" type="string">
  Task completion time
</ResponseField>

<ResponseField name="output.message" type="string">
  Detailed information about the failed request (not returned if request is successful)
</ResponseField>

<ResponseField name="output.orig_prompt" type="string">
  Original input prompt (for video tasks)
</ResponseField>

<ResponseField name="output.results" type="object[]">
  List of task results for image generation tasks
</ResponseField>

<ResponseField name="output.results[].actual_prompt" type="string">
  Actual prompt after intelligent rewriting (if enabled)
</ResponseField>

<ResponseField name="output.results[].code" type="string">
  Image error code (returned when some tasks fail)
</ResponseField>

<ResponseField name="output.results[].message" type="string">
  Image error information (returned when some tasks fail)
</ResponseField>

<ResponseField name="output.results[].orig_prompt" type="string">
  Original input prompt
</ResponseField>

<ResponseField name="output.results[].url" type="string">
  Generated image URL address
</ResponseField>

<ResponseField name="output.scheduled_time" type="string">
  Task execution time
</ResponseField>

<ResponseField name="output.submit_time" type="string">
  Task submission time
</ResponseField>

<ResponseField name="output.task_id" type="string" required>
  Task ID
</ResponseField>

<ResponseField name="output.task_metrics" type="object">
  Task result statistics for image generation tasks
</ResponseField>

<ResponseField name="output.task_metrics.FAILED" type="integer">
  Number of failed tasks
</ResponseField>

<ResponseField name="output.task_metrics.SUCCEEDED" type="integer">
  Number of successful tasks
</ResponseField>

<ResponseField name="output.task_metrics.TOTAL" type="integer">
  Total number of tasks
</ResponseField>

<ResponseField name="output.task_status" type="string" required>
  Task status

  Possible values: `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN`
</ResponseField>

<ResponseField name="output.video_url" type="string">
  Video URL for completed video generation tasks. Link validity period 24 hours
</ResponseField>

<ResponseField name="request_id" type="string" required>
  Unique request identifier
</ResponseField>

<ResponseField name="usage" type="object">
  Output information statistics. Only successful results are counted
</ResponseField>

<ResponseField name="usage.SR" type="integer">
  Video resolution level (I2V and wan3.0-video tasks)
</ResponseField>

<ResponseField name="usage.duration" type="number">
  Duration of generated video in seconds (I2V and wan3.0-video tasks)
</ResponseField>

<ResponseField name="usage.fps" type="integer">
  Frame rate of the generated video (wan3.0-video tasks)
</ResponseField>

<ResponseField name="usage.image_count" type="integer">
  Number of generated images (T2I and I2I tasks)
</ResponseField>

<ResponseField name="usage.input_video_duration" type="number">
  Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)
</ResponseField>

<ResponseField name="usage.output_video_duration" type="number">
  Duration of the output video in seconds (wan3.0-video tasks)
</ResponseField>

<ResponseField name="usage.ratio" type="string">
  Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)
</ResponseField>

<ResponseField name="usage.size" type="string">
  Image resolution (T2I and I2I tasks)
</ResponseField>

<ResponseField name="usage.video_count" type="integer">
  Number of generated videos (T2V tasks)
</ResponseField>

<ResponseField name="usage.video_duration" type="number">
  Duration of generated video in seconds (T2V tasks)
</ResponseField>

<ResponseField name="usage.video_ratio" type="string">
  Video resolution ratio (T2V tasks)
</ResponseField>

<ResponseField name="code" type="string">
  Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).
</ResponseField>

<ResponseField name="message" type="string">
  Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.
</ResponseField>

## Examples

### Input

```json theme={null}
{
  "input": {
    "prompt": "character1 walks in from the left, turns to the camera and waves, plain orange backdrop",
    "media": [
      {
        "type": "reference_image",
        "url": "https://raw.githubusercontent.com/Comfy-Org/workflow_templates/main/input/pink_robot_front.png"
      }
    ]
  },
  "parameters": {
    "resolution": "720P",
    "duration": 5
  }
}
```

### Output

```json theme={null}
{
  "output": {
    "task_id": "0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60",
    "task_status": "SUCCEEDED",
    "submit_time": "2027-01-01T00:00:00.000Z",
    "scheduled_time": "2027-01-01T00:00:01.000Z",
    "end_time": "2027-01-01T00:01:04.000Z",
    "orig_prompt": "character1 walks in from the left, turns to the camera and waves, plain orange backdrop",
    "actual_prompt": "character1 walks in from the left, turns to the camera and waves, plain orange backdrop, even studio lighting, steady medium shot",
    "video_url": "https://.../generated.mp4"
  },
  "request_id": "7574ee8f-38a3-4b1e-9280-11c33ab46e51",
  "usage": {
    "SR": 720,
    "duration": 5
  }
}
```

The video URL is valid for 24 hours. Download the video promptly if you need to keep it.

## Before you ship

The SDKs create an `Idempotency-Key` and reuse it for automatic retries. For manual retries, reuse the original key. Router can hold the connection for up to 10 minutes.

When a request fails, Router sends an `X-Comfy-Error-Type` response header explaining why. A `422` means Router rejected the input before calling the provider. Download generated assets promptly because [result URLs can expire](/development/comfy-router/reference#result-assets).

<CardGroup cols={3}>
  <Card title="Headers" icon="list" href="/development/comfy-router/headers">
    Authentication, idempotency, request IDs, error buckets, retry pacing, spend limits.
  </Card>

  <Card title="Using the Router API" icon="code" href="/development/comfy-router/api">
    Model discovery, validation errors, retries, and billing.
  </Card>

  <Card title="Limitations" icon="triangle-exclamation" href="/development/comfy-router/limitations">
    What Router does not do today, and what to use instead.
  </Card>
</CardGroup>
