roasOS
Workflows

Recast an actor

Put a different person into a clip that you already own, with the consent record that a face needs.

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.

This page is the person-swap recipe. It runs on the same door as 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.
  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 with the swap written into the prompt.
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.

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.

{
  "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"
      }
    ]
  }
}
presenceUse it as the swap target?
on-cameraYes. This is the person the ad is carried by.
hands-onlyNo. Hands are a product moment, not a cast member.
backgroundNo. 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.

SourceSendConsent record
A roasOS gallery avataravatar_idNone. The gallery avatar is already licensed.
An avatar of your workspaceavatar_idNone. A person in your workspace made it.
An uploaded faceelementsRequired. Send media_rights with it.

Make an avatar over the API with POST /v1/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.

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

{
  "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

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.

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.

modelReserved credits for a 5-second sourceReserved credits for a 15-second source
seedance-2.5-edit (default)2985
kling-4 (default with elements)1751

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 and Kling 4.0 Edit for the full tables.

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

The source length

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.

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

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

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.

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

StatusCodeCause
400recast_target_requiredNo avatar_id, no product_id and no elements
400recast_source_too_longThe measured source is longer than the model takes: 30 seconds
400reference_media_not_workspace_ownedThe source or a reference is not workspace-owned media
400media_rights_requiredThe swap target is an uploaded face and the body carries no media_rights block
400invalid_requestTwo sources, no source, or more images than the model takes
402insufficient_creditsThe balance does not cover the quote
410quote_expiredThe quote is older than 10 minutes; quote again under a new idempotency key
503avatar_generation_unavailableYou called the legacy POST /v1/avatars. Use POST /v1/avatar/nano-banana-pro

Refer to Errors and to Recast a video.

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

Next

On this page