---
title: "Recast an actor"
description: "Put a different person into a clip that you already own, with the consent record that a face needs."
url: https://docs.roasos.com/workflows/recast-an-actor
---

# Recast an actor

Actor recast is **Preserve video** for a replacement person. It edits an owned clip while asking to retain the camera work, motion, cuts, timing, product and existing text. Review the output for identity and visual fidelity. For a new script or storyboard, use [Rebuild ad](/workflows/remix-an-ad).

This page is the person-swap recipe. It runs on the same door as [Recast a video](/workflows/recast-a-video), which is the reference for the whole request. Read that page for the full field list.

## The recipe

roasOS ships the recipe as a pack skill: `roasos:recast-actor`, named **Recast the actor**. The skill does four steps, and you can do the same four yourself.

1. **Identify who is on screen.** Use an existing analysis or, when the target is unclear, analyze the clip with [`POST /v1/workflows/analyze`](/workflows/analyze-an-ad).
2. **Pick the person to replace.** Read `characterSheet.people` and take the one with `primary: true`.
3. **Cast the replacement.** Use a gallery avatar, a workspace avatar, or a real person whose written permission you hold.
4. **Recast the clip.** Send [`POST /v1/workflows/recast`](/workflows/recast-a-video) with the swap written into the prompt.

```bash
curl -X POST https://api.roasos.com/v1/skills/roasos:recast-actor/runs \
  -H "Authorization: Bearer roasos_sk_..." \
  -H "Idempotency-Key: recast-actor-kitchen-001" \
  -H "Content-Type: application/json" \
  -d '{ "generation_spend_cap_credits": 80, "quote_only": true }'
```

The skill run is an agent run. Read it at `GET /v1/agent-runs/{id}`. Refer to [Skills](/agent/skills).

## 1. Read who is on screen

An analysis returns a character sheet. It holds up to six people. Each person has an `observable` description, a `presence` value and, for exactly one of them, `primary: true`.

```json
{
  "characterSheet": {
    "count": 2,
    "people": [
      {
        "id": "p1",
        "observable": "A woman in her thirties, dark curly hair, grey sweatshirt, speaks to camera.",
        "primary": true,
        "presence": "on-camera"
      },
      {
        "id": "p2",
        "observable": "A pair of hands that open the jar.",
        "presence": "hands-only"
      }
    ]
  }
}
```

| `presence`   | Use it as the swap target?                         |
| ------------ | -------------------------------------------------- |
| `on-camera`  | Yes. This is the person the ad is carried by.      |
| `hands-only` | No. Hands are a product moment, not a cast member. |
| `background` | No. A background person is set dressing.           |

Take the person with `primary: true`. Write the swap instruction from that person's `observable` description, so the model knows who leaves the frame.

## 2. Cast the replacement

There are three sources, and they carry different rules.

| Source                      | Send        | Consent record                                |
| --------------------------- | ----------- | --------------------------------------------- |
| A roasOS gallery avatar     | `avatar_id` | None. The gallery avatar is already licensed. |
| An avatar of your workspace | `avatar_id` | None. A person in your workspace made it.     |
| An uploaded face            | `elements`  | Required. Send `media_rights` with it.        |

<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`. The legacy path `POST /v1/avatars` answers `503 avatar_generation_unavailable`.
</Callout>

### An uploaded face needs a consent record

A recast puts a different person into real footage, so an uploaded face carries the same versioned attestation that the avatar lipsync lanes carry.

```json
{
  "media_rights": { "accepted": true, "statement_version": 1 }
}
```

`accepted` must be the literal `true`. `statement_version` must be `1`, which is the current statement. roasOS writes one attestation row for the run, under the purpose `persona_swap`, and it binds that record into the dispatch.

Send `media_rights` whenever the body carries `elements`. roasOS cannot see a face inside a URL, so it treats every element as one and asks for the statement. A recast that casts only `avatar_id` or `product_id` needs no block, and roasOS answers `400 media_rights_required` for an `elements` recast that carries none.

Sending the block where it is not owed is safe. roasOS then records one row anyway, and the row still says something true: your source ad is media that the statement covers.

By sending the block you state that the person shown in your reference images gave you written permission to depict them in an ad.

## 3. Recast the clip

```bash
curl -X POST https://api.roasos.com/v1/workflows/recast \
  -H "Authorization: Bearer roasos_sk_..." \
  -H "Idempotency-Key: recast-actor-kitchen-001" \
  -H "Content-Type: application/json" \
  -d '{
    "source_generation_id": "3d2f7b90-6c11-4a8d-9b02-77e5a1c4d380",
    "prompt": "Replace the woman in the grey sweatshirt with the person in @Image1. Keep the jar, the hands and the kitchen light the same.",
    "avatar_id": "9c17ab42-0b53-4d1e-8f7a-2c9b6e40a115",
    "quote_only": true
  }'
```

Name the change and only the change. The source clip owns the camera and the motion, so do not describe either one.

The request names no `model`, so it runs on the default, `seedance-2.5-edit`. On that model the images bind as `@Image1` to `@Image9`, in this order: your `references`, the product images, then the avatar. On Kling 4.0, name each identity element one time, as `@Element1` to `@Element7`. Refer to [Name each identity in the prompt](/workflows/recast-a-video#name-each-identity-in-the-prompt).

### Choose the model

`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` runs on `kling-4`, because only Kling takes identity elements.

| `model`                             | Reserved credits for a 5-second source | Reserved credits for a 15-second source |
| ----------------------------------- | -------------------------------------- | --------------------------------------- |
| `seedance-2.5-edit` (default)       | 29                                     | 85                                      |
| `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. Refer to [Seedance 2.5 Edit](/models/video/seedance-2.5-edit) and [Kling 4.0 Edit](/models/video-edit/kling-4) for the full tables.

The quote reserves credits from the measured source duration. A longer output cannot exceed the approved quote.

## The source length

<Callout type="warn">
  **A recast source must be 4 to 30 seconds long on `seedance-2.5-edit`, and 3 to 30 seconds on `kling-4`.** roasOS measures the source and refuses a longer one with `400 recast_source_too_long`, **before** it prices anything. No credits move on that refusal.
</Callout>

For an ad that is longer than the model takes, do one of these three things.

1. **Cut the source.** Use [`POST /v1/assembly/trim-video`](/models/assembly/trim-video), then recast the cut.
2. **Recast a clip that is already short enough.** Pick one cut of the ad, not the whole flattened file.
3. **Recast the segments, then stitch them.** An ad that roasOS made for you exists as its own segments. Recast each segment, and then join the results with [`POST /v1/assembly/merge-videos`](/models/assembly/merge-videos).

## The result records what it came from

A completed recast is stamped `recast-of` against the source generation. The lineage says that this clip is a recast of that clip, and not a plain edit of it.

## Rights

<Callout type="warn">
  Recast footage that you have the right to use. A Discover creative is a competitor's ad: recast accepts it, and you are responsible for its rights. Refer to [Media rights](/concepts/media-rights).
</Callout>

The person you swap **in** must be one of three things: a roasOS gallery avatar, an avatar of your workspace, or a real person whose written permission you hold. Nothing else is a legal cast.

The person you swap **out** stays in your own footage, which you own. That is why the source rule and the cast rule are two different rules.

## Errors

| Status | Code                                  | Cause                                                                            |
| ------ | ------------------------------------- | -------------------------------------------------------------------------------- |
| `400`  | `recast_target_required`              | No `avatar_id`, no `product_id` and no `elements`                                |
| `400`  | `recast_source_too_long`              | The measured source is longer than the model takes: 30 seconds                   |
| `400`  | `reference_media_not_workspace_owned` | The source or a reference is not workspace-owned media                           |
| `400`  | `media_rights_required`               | The swap target is an uploaded face and the body carries no `media_rights` block |
| `400`  | `invalid_request`                     | Two sources, no source, or more images than the model takes                      |
| `402`  | `insufficient_credits`                | The balance does not cover the quote                                             |
| `410`  | `quote_expired`                       | The quote is older than 10 minutes; quote again under a new idempotency key      |
| `503`  | `avatar_generation_unavailable`       | You called the legacy `POST /v1/avatars`. Use `POST /v1/avatar/nano-banana-pro`  |

Refer to [Errors](/concepts/errors) and to [Recast a video](/workflows/recast-a-video#errors).

## From an agent

The MCP tool is `roasos_recast_video`, and the pack skill runs through `roasos_run_skill` by its slug. Both obey the same quote rule as every paid tool. Refer to [Connect an AI agent (MCP)](/mcp).

## Next

* The full request: [Recast a video](/workflows/recast-a-video).
* Read the ad first: [Analyze an ad](/workflows/analyze-an-ad).
* The rules for a face: [Media rights](/concepts/media-rights).
