---
title: "Recast a video"
description: "Put your own person and your own product into a video that you own."
url: https://docs.roasos.com/workflows/recast-a-video
---

# Recast a video

Recast is the identity/product replacement path for **Preserve video**. It edits footage your workspace owns and requests that the original camera, motion, cuts and timing remain. Review the result for fidelity; matching frames or output length is not guaranteed. For a new script and storyboard, choose [Rebuild ad](/workflows/remix-an-ad).

Recast runs on `seedance-2.5-edit` by default. A request that sends `elements` and no `model` runs on `kling-4`, because only Kling accepts identity elements. `model` chooses the model: `seedance-2.5-edit` or `kling-4`. The quote, the idempotency key, the job envelope, the webhook and the refund are the same as for [`POST /v1/video-edit/kling-4`](/models/video-edit/kling-4).

## 1. Choose the source video

Send exactly one source.

| Field                  | Use it for                                                   |
| ---------------------- | ------------------------------------------------------------ |
| `source_video_url`     | A video on `https://cdn.roasos.com` that your workspace owns |
| `source_generation_id` | The id of a completed video job in your workspace            |

`source_generation_id` resolves to the output URL of that job. Use it to recast a clip that roasOS made for you, without copying the URL.

On `kling-4`, the source must be 3 to 30 seconds long, at most 100 MiB, at least 700 px per side, and `video/mp4`. On `seedance-2.5-edit`, the source must be 4 to 30 seconds long and at most 100 MiB.

<Callout type="warn">
  roasOS measures the source and refuses a longer clip with `400 recast_source_too_long`, **before** it prices anything. No credits move on that refusal. Refer to [The source length](/workflows/recast-an-actor#the-source-length) for what to do with a longer ad.
</Callout>

## 2. Choose the target

Send at least one of these three fields. Without one of them the call is a plain video edit, so roasOS refuses it with `400 recast_target_required`.

| Field        | What it does                                                                                                                  |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------- |
| `avatar_id`  | Uses a saved avatar as one identity element                                                                                   |
| `product_id` | Adds the images of a saved product as style references                                                                        |
| `elements`   | `kling-4` only. Locks an identity yourself: 1 to 7 items of `{ frontal_image_url, reference_image_urls }` (1 to 3 views each) |

Add optional `references` for a look, such as a season or a location. On `seedance-2.5-edit`, your references, the product images and the avatar share 9 image slots. On the Kling tiers, explicit `elements`, a saved avatar and all reference images share four slots. The avatar counts as one identity element, and each image resolved from `product_id` counts as one appearance reference.

<Callout type="info">
  **Make an avatar over the API with [`POST /v1/avatar/nano-banana-pro`](/models/avatar/nano-banana-pro).** The MCP tool is `roasos_avatar_nano_banana_pro`. Send the new id in `avatar_id`, or send a roasOS gallery avatar. The legacy path `POST /v1/avatars` answers `503 avatar_generation_unavailable`. Its list, get and delete paths work.
</Callout>

A body that carries `elements` needs a consent record. Send `media_rights` with it, or roasOS answers `400 media_rights_required`. A recast that casts only `avatar_id` or `product_id` needs none. Refer to [Recast an actor](/workflows/recast-an-actor#an-uploaded-face-needs-a-consent-record).

### Name each identity in the prompt

The provider needs every identity element named one time in the prompt.

On `seedance-2.5-edit` (the default):

* `@Image1` to `@Image9` are the images, in this order: your `references`, the product images, then the avatar.
* Write the tags yourself.

On `kling-4`:

* `@Video1` is the source clip.
* `@Element1` to `@Element7` are the `elements`, in order.
* `@Image1` to `@Image9` are the `references`, in order.
* roasOS appends the tag for `avatar_id` when your prompt does not carry it. Write the other tags yourself.

Name the requested change, then ask to preserve the remaining motion, camera, cuts, timing and existing captions. Keep generation-only no-text suffixes off an edit.

## 3. Quote

This example names `kling-4`, so the avatar binds as `@Element1`.

```bash
curl -X POST https://api.roasos.com/v1/workflows/recast \
  -H "Authorization: Bearer roasos_sk_..." \
  -H "Idempotency-Key: recast-kitchen-cut-001" \
  -H "Content-Type: application/json" \
  -d '{
    "source_generation_id": "3d2f7b90-6c11-4a8d-9b02-77e5a1c4d380",
    "prompt": "Replace the woman with @Element1. Keep the product, the hands and the lighting the same.",
    "avatar_id": "9c17ab42-0b53-4d1e-8f7a-2c9b6e40a115",
    "product_id": "5e6b3311-7a2c-4f19-b3d8-0c41e7f92a63",
    "model": "kling-4",
    "quote_only": true
  }'
```

```json
{
  "data": {
    "kind": "quote",
    "quote_id": "c4a0e918-...",
    "credits": 17,
    "expires_at": "2026-08-21T11:12:19.000Z",
    "pricing_version": "customer-pricing@2026-10-30.v14",
    "confirmation": {
      "model": "kling-4",
      "mode": "edit",
      "duration_seconds": 5,
      "reference_count": 2
    }
  }
}
```

Read `confirmation`. It states what roasOS understood. Credits are reserved from the measured source duration. Completion refunds credits proportionally for shorter output and caps longer output at the approved quote; the source is not billed again as an input-video offset.

## 4. Confirm

Send the same body with the `quote_id`. Use the same idempotency key. Add `webhook_url` when you do not want to poll.

```bash
curl -X POST https://api.roasos.com/v1/workflows/recast \
  -H "Authorization: Bearer roasos_sk_..." \
  -H "Idempotency-Key: recast-kitchen-cut-001" \
  -H "Content-Type: application/json" \
  -d '{
    "source_generation_id": "3d2f7b90-6c11-4a8d-9b02-77e5a1c4d380",
    "prompt": "Replace the woman with @Element1. Keep the product, the hands and the lighting the same.",
    "avatar_id": "9c17ab42-0b53-4d1e-8f7a-2c9b6e40a115",
    "product_id": "5e6b3311-7a2c-4f19-b3d8-0c41e7f92a63",
    "model": "kling-4",
    "quote_id": "c4a0e918-..."
  }'
```

The answer is `201` with the standard job envelope.

```json
{
  "data": {
    "id": "7b52d0c4-...",
    "kind": "video",
    "model": "kling-4",
    "status": "queued",
    "credits_reserved": 17,
    "created_at": "2026-08-21T11:02:31Z",
    "links": {
      "self": "/v1/jobs/7b52d0c4-...",
      "status": "/v1/jobs/7b52d0c4-.../status",
      "result": "/v1/jobs/7b52d0c4-.../result",
      "events": "/v1/jobs/7b52d0c4-.../events"
    }
  }
}
```

## 5. Poll the job

```bash
curl https://api.roasos.com/v1/jobs/7b52d0c4-.../status \
  -H "Authorization: Bearer roasos_sk_..."
```

A recast is an ordinary job, so it carries the five job statuses. Poll every few seconds until the status is `completed`, `failed` or `canceled`. Refer to [Jobs](/concepts/jobs) and to [Webhooks](/concepts/webhooks).

## 6. Read the result

```bash
curl https://api.roasos.com/v1/jobs/7b52d0c4-.../result \
  -H "Authorization: Bearer roasos_sk_..."
```

```json
{
  "data": { "url": "https://cdn.roasos.com/workspaces/.../recast.mp4" }
}
```

The URL is immutable. Send it as the media input of your next call.

## Rights

You may recast only a video that your workspace owns.

roasOS checks the source against the workspace media rules before it prices anything. An immutable object under `workspaces/…` or `uploads/…` on `https://cdn.roasos.com` passes. Every other URL fails with `400 reference_media_not_workspace_owned`.

<Callout type="warn">
  A Discover creative is a competitor's ad. Recast accepts it as the source video. You are responsible for the rights to the footage you recast. Refer to [Media rights](/concepts/media-rights).
</Callout>

## Price

The quote reserves credits from the measured source length.

### Choose the tier

`model` chooses the model. The default is `seedance-2.5-edit`, the preserve-video role in `roasos_get_catalog`. A request with `elements` and no `model` uses `kling-4`.

| `model`                             | Reserved credits for 5 s | Reserved credits for 15 s |
| ----------------------------------- | ------------------------ | ------------------------- |
| `seedance-2.5-edit` (default)       | 18                       | 54                        |
| `kling-4` (default with `elements`) | 17                       | 51                        |

Both models bill the source seconds and the output seconds. The Kling 4.0 values are at 1080p, its default tier. A longer output cannot exceed the approved quote. The full tables are on the model pages: [Kling 4.0 Edit](/models/video-edit/kling-4).

`seedance-2.5-edit` takes a source up to 30 seconds and up to 9 reference images. On it, `references` alone is a valid target, and `elements` is refused. The model page states its price: [Seedance 2.5 Edit](/models/video/seedance-2.5-edit).

## Limits

* The source must be 3 to 30 seconds on `kling-4` and 4 to 30 seconds on `seedance-2.5-edit`. A longer clip is `400 recast_source_too_long`.
* Keep the prompt at 2,500 characters or fewer. Every recast model holds the same limit.
* On `kling-4`, the edit has no audio control. Do not promise the original sound.
* The source length sets the reservation. Review output duration: shorter output receives a proportional refund, and longer output cannot exceed the approved quote.

## Errors

| Code                                  | Cause                                                                       |
| ------------------------------------- | --------------------------------------------------------------------------- |
| `recast_target_required`              | No `avatar_id`, no `product_id` and no `elements`                           |
| `recast_source_too_long`              | The measured source is longer than the model takes: 30 seconds              |
| `media_rights_required`               | The body carries `elements` and no `media_rights` block                     |
| `reference_media_not_workspace_owned` | The source video or a reference is not workspace-owned media                |
| `invalid_request`                     | Two sources, no source, or more images than the model takes                 |
| `insufficient_credits`                | The balance does not cover the quote                                        |
| `quote_expired`                       | The quote is older than 10 minutes; quote again under a new idempotency key |

Refer to [Errors](/concepts/errors).

## From an agent

The MCP tool is `roasos_recast_video`. The quote rule is the same. Refer to [Connect an AI agent (MCP)](/mcp).
