---
title: Dub existing footage
description: Dub, translate or re-voice footage you already have: new speech timed to the video, the music kept, and the lips re-synced where faces show.
---
Dub existing footage: translate it, re-voice it, or replace a speaker's voice in a video you already shot. Your agent makes new speech timed to the picture, keeps the music and effects, and re-syncs the lips where a face is on screen. You get the dubbed video on an editable timeline, with the original sound still there to switch back.

Use invideo for this when you want speech timing, lip sync and the final mix in one project, with the original kept and a branch per language.

## Quickest path

Set up your agent first with [invideo for AI agents](/agents). Then give it this prompt.

```text
Dub interview.mp4 into Spanish (Spain) in invideo. Show me the Spanish
lines first. Keep the music. Re-sync the speaker's lips. Mute the original
voice on a branch, don't delete it, and show me frames before you export.
```

In a chat app, you drag the video into the project. In a terminal, the agent uploads the file you name.

## What you need

These are the inputs for a good dub.

- The footage, with its sound.
- The target language, or the new lines if you are re-voicing in the same language.
- Who approves the new lines.
- The voice: a stock voice, or the original speaker's voice cloned with their consent from short clean clips.
- Whether faces are on screen. Lip sync costs more and is needed only where a mouth shows.

## Steps

There are two ways to make the speech. The agent picks one with you. Both make one take per line, placed at the line's original time.

- `seed_audio_1_0.ta2a` speaks your lines in the original speaker's voice, from one to three reference clips tagged `@Audio1` to `@Audio3`.
- `elevenlabs_text_to_speech.elevenlabs_tts_v4` with a stock voice, as on [Translate a training video and retime the cut to fit the new voice](/how-to/training/translate-and-retime).

invideo has no model that dubs a whole video in one call. For that, use a dubbing service, named at the end of this page.

<Steps>
<Step title="Bring the footage in and transcribe it">
The agent adds the file with `add_media`. It prices `audio.transcript` with `dry_run` on the file's sequence URL and tells you the price. For a price of 1 credit or more it waits for your yes. Then it starts the transcript with `max_credits` set to that price, and calls again after `retry_after_s` for the words. It sees every word with its time and speaker.
</Step>
<Step title="Write and approve the new lines">
The agent translates or rewrites the lines, keeps their times, and shows you the script. It sees your yes or your changes.
</Step>
<Step title="Keep the music">
The agent runs `media` `audio.separate` on the footage's sequence URL with `dry_run`, and tells you the price. For a price of 1 credit or more it waits for your yes. Then it runs it with `max_credits` set to that price and an `idempotency_key`. It sees new sequences for the voice and the rest of the sound.
</Step>
<Step title="Make the new speech">
1. The agent runs `find_models` with the job in words, such as "seed audio speech in a reference voice", picks a model, and reads its inputs with `get_model`. A good pick to keep the speaker's voice is `seed_audio_1_0.ta2a`, with a clean clip of the speaker as `reference_audio_1`, tagged `@Audio1` in the `prompt`. For a stock voice, a good pick is `elevenlabs_text_to_speech.elevenlabs_tts_v4` with a `voice_id` from `elevenlabs_text_to_speech_voices`. Your agent can search for others with `find_models`.
2. It calls `generate` per line with `dry_run: true`, and tells you the price.
3. For a price of 1 credit or more it waits for your yes. Then it calls `generate` per line with `max_credits` set to that price and an `idempotency_key`, like `line-01-es`. It gets a thread URL and, when the result lands, one audio sequence URL per line. It reads each URL with `read` and never calls `generate` again to check.
</Step>
<Step title="Re-sync the lips">
Where a face speaks on screen, the agent re-syncs the lips. The lip sync models take one audio file.

1. The agent joins the line takes into one track with `media` `audio.concat`.
2. It runs `find_models` with "lip sync", and reads its pick with `get_model`. A good pick is `sync_lipsync_v3.video`. Your agent can search for others with `find_models`.
3. It calls `generate` with the footage as `video`, the joined speech as `audio` and `dry_run: true`, and tells you the price.
4. For a price of 1 credit or more it waits for your yes. Then it calls `generate` with `max_credits` set to that price and an `idempotency_key`. It reads the new video's sequence URL with `read`, and looks at it with `media` `video.frames` before it uses it.
</Step>
<Step title="Mix on a branch">
The agent creates a branch with `timeline_history`. On the branch it mutes the original sound with `set_audio_muted` and places the music stem and each new line at its original start with `insert_media`. It sees a new `state_token`.
</Step>
<Step title="Check, then export">
The agent calls `capture` on faces mid-sentence, `media` `audio.loudness` on the mix, and `check_timeline`. Then it calls `export`. It sees an export card with status `completed`.
</Step>
</Steps>

The terminal version:

```bash
invideo edit new <project-url> --name "Interview ES"
invideo asset upload <edit-url> ./interview.mp4
invideo media audio.transcript <footage-url> --dry-run
invideo media audio.transcript <footage-url> --max-credits <price>
invideo media audio.separate <footage-url> --dry-run
invideo media audio.separate <footage-url> --max-credits <price> \
  --idempotency-key stems-1
invideo gen models <project-url> --query "seed audio reference voice"
invideo gen model <project-url> --model seed_audio_1_0.ta2a
invideo gen run <page-url> --model seed_audio_1_0.ta2a --dry-run \
  --input line-01-es.json
invideo gen run <page-url> --model seed_audio_1_0.ta2a \
  --max-credits <price> --idempotency-key line-01-es --input line-01-es.json
invideo gen models <project-url> --query "lip sync"
invideo gen model <project-url> --model sync_lipsync_v3.video
invideo gen run <page-url> --model sync_lipsync_v3.video --dry-run \
  -i video=<footage-url> -i audio=<speech-url>
invideo gen run <page-url> --model sync_lipsync_v3.video \
  --max-credits <price> --idempotency-key lipsync-1 \
  -i video=<footage-url> -i audio=<speech-url>
invideo status <thread-url> --wait
invideo notebook show <thread-url>
invideo edit show <edit-url> --json
invideo branch new <edit-url> --name dub-es --token <state_token>
invideo edit show <branch-url> --json
invideo edit change <branch-url> --timeline T1 --token <state_token> \
  --operations @mix.json
invideo edit check <branch-url>
invideo edit export <branch-url> --format video --quality 1080 --json
invideo status <card-url> --wait
invideo notebook show <card-url> --out ./out
```

The file `line-01-es.json` holds the model's inputs alone, as `get_model` lists them, with no `inputs` wrapper. The dry run and the paid run read the same file, so the run that spends is the run that was priced.

```json
{
  "prompt": "@Audio1, calm and clear: \"Bienvenidos al curso.\"",
  "reference_audio_1": "<speaker-clip-sequence-url>"
}
```

Where each value comes from:

- `<project-url>` comes from `invideo project new`, and `<edit-url>` from `invideo edit new`.
- `<footage-url>` is the `url` that `invideo asset upload <edit-url>` returns, a `…/notebook/seq-N.M` URL.
- `<speaker-clip-sequence-url>` is the sequence URL of a clean clip of the speaker, such as the voice stem `audio.separate` makes.
- `<price>` is each dry run's `.estimated_credits`.
- `<page-url>` is a notebook page of the project, listed by `invideo notebook show <project-url>`.
- `<speech-url>` is the joined track from `audio.concat`. `<thread-url>` is the `url` the paid `gen run` returns, and `notebook show` on it lists the new video.
- `<branch-url>` is the `branch_url` that `invideo branch new` returns.
- `<state_token>` comes from `invideo edit show` on the URL the next command runs on: `<edit-url>` for `branch new`, then `<branch-url>`, read again before each change.
- `@mix.json` reads a bare JSON list of ops: `set_audio_muted` on the original sound and `insert_media` for each new line.
- `<card-url>` is `.sequences[0]` of the export answer. `invideo status` waits but saves nothing. `invideo notebook show` saves the video as `./out/export.mp4`, and the agent renames it.

The MCP `generate` call for one line, in the speaker's own voice, as a dry run. Over MCP the inputs go inside `inputs`. The paid call is the same with `max_credits` set to the price and an `idempotency_key`, without `dry_run`.

```json
{
  "model": "seed_audio_1_0.ta2a",
  "dry_run": true,
  "inputs": {
    "prompt": "@Audio1, calm and clear: \"Bienvenidos al curso.\"",
    "reference_audio_1": "<speaker-clip-sequence-url>"
  }
}
```

For a file without a timeline, `media` `video.replaceAudio` on the file's sequence URL, with `audio_input`, makes a new video with the new sound. The timeline path keeps the original, so prefer it.

## Cost and time

Edits and exports are free. Generation and transcripts cost credits. Paid stock costs a small credit charge, or one of your workspace's iStock licenses. Your agent runs a dry run first and tells you the price before it spends anything.

- Costs credits: the transcript, stem separation, the new speech, and lip sync, which is the largest part.
- Free: every edit, branch and export.
- The tool ops in `media`, like `audio.concat`: the dry run shows the price before anything starts.

A short clip takes a few minutes of agent time plus generation. Lip sync takes longer. See [Credits](/account/credits) for prices.

## If something goes wrong

Most problems are length, voice or lips.

| Symptom | Fix |
| --- | --- |
| `get_model` or `generate` refuses the model as not found | Models retire. Run `find_models` with the job in words, use the best match, and tell the person which model you used. |
| CLI exit 2 "inputs is not an input of this model" | The `--input` file has an `inputs` wrapper. Put the model's inputs at the top level of the file. |
| `audio.transcript` or `audio.separate` refused with `code: "not_found"` | The call named a media bin asset. Pass the footage's sequence URL from `add_media` or `invideo asset upload`. |
| `invideo status --wait` ends and the folder is empty | `status` does not download. Run `invideo notebook show <card-url> --out <dir>`. |
| A new line overlaps the next | Shorten the line, or open time on every track with `edit_time_range` mode `insert`. |
| `seed_audio_1_0.ta2a` refuses the inputs | It needs `reference_audio_1`, and each clip must meet the limits `get_model` lists. |
| `sync_lipsync_v3.video` refuses the speech | It takes one audio file. Join the takes with `audio.concat`, or export the dubbed timeline with `export` mode `audio` and pass that. |
| The lips drift at the end | The speech and video lengths differ. Set `sync_mode` on `sync_lipsync_v3.video`, or trim the speech first. |
| The music is gone | The original sound was muted whole. Place the music stem from `audio.separate`. |
| The cloned voice sounds off | Use clean reference clips with one speaker and no music. |
| Exit code 6, or `status` is `held` | A safety review, not a refusal. Wait, then run `invideo status <url> --wait` again. Do not resend. If it ends `failed`, change the input the review flagged. |
| `timeout` on `generate` | Do not call it again. `read` the thread or call `status`. |
| Avoid: delete the original sound | Instead: mute it with `set_audio_muted` on a branch. |

## Prompts to try

Each prompt is a real variation of this job.

```text
Re-voice this narrator in a younger female voice, same words, same timing.
No lip sync, the narrator is off screen.
```

```text
Make a quick German draft of this English video in a stock voice, one
take per line. Keep the music. I'll decide on lip sync after.
```

```text
Dub only the first speaker in this panel into French. Keep the others in
English.
```

## When to use something else

Clone a voice only with the speaker's consent. For many speakers, many languages and a review workflow, a dubbing service such as ElevenLabs Dubbing or HeyGen video translation does the whole job, and invideo finishes the cut and captions. invideo does not judge translation quality, so a native speaker reviews the lines.

## Related

- [Translate a training video and retime the cut to fit the new voice](/how-to/training/translate-and-retime)
- [Voice a script or narrate a course](/how-to/training/voice-a-script-or-narrate-a-course)
- [Localize a winning ad](/how-to/marketing/localize-a-winning-ad)
- [Use ElevenLabs with invideo](/integrations/elevenlabs)
- [Models](/reference/models)