---
title: Voice a script or narrate a course
description: Get a voiceover for a script or narration for every lesson of a course, from a stock voice or a consented clone, as clean audio files.
---
Get a voiceover or narration for your script: an AI voiceover, text to speech, a narrated course, one voice across every lesson. Your agent finds voices and sends you previews, voices the script, cleans up a rough recording if you have one, and delivers audio files or places the voice on a timeline.

Use invideo for this when you want the voice to land on the same timeline as your slides or footage, and fix one line later without a new session.

## Quickest path

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

```text
Voice script.txt in invideo. Send me a few calm, clear narrator voices to
preview first. After I pick one, voice it in parts, one take per
paragraph, and export the narration as a WAV file.
```

In a chat app, you paste the script and listen to the previews in the chat. In a terminal, the agent reads the file and prints the preview links.

## What you need

Your agent asks for these.

- The script, or one script per lesson.
- The voice you want, in words: calm, warm, young, British, and so on.
- The language of the script.
- For a clone: short clean clips of the speaker, and their consent.
- What you want back: audio files, or the voice placed on a timeline.

## Steps

The agent previews before it spends.

<Steps>
<Step title="Pick the model">
The agent runs `find_models` with the job in words, such as "narration voice", picks one, and reads its inputs with `get_model`. A good pick is `seed_tts_2_0`, which has `speech_rate` and `silence_duration`. `elevenlabs_text_to_speech.elevenlabs_tts_v4` takes emotion tags in the text, and `gemini_3_1_flash_tts` takes a second speaker, for a dialogue. Your agent can search for others with `find_models`. It sees the model's input schema.
</Step>
<Step title="Find voices and send previews">
The agent calls `search_library` with the voice library of the model it picked, and a query such as "calm narrator". A voice id works only with its own model. `elevenlabs_text_to_speech.elevenlabs_tts_v4` takes a voice from `elevenlabs_text_to_speech_voices`. `seed_tts_2_0` takes a voice from `seed_tts_2_0_voices`, which returns a list to browse when the query is left out. Each voice has a `preview`. Your workspace's own cloned voices are listed too. It sees voice ids and preview links, and sends you the links.
</Step>
<Step title="Voice a test line">
1. The agent calls `generate` with `dry_run: true`, one paragraph and your chosen `voice_id`, and tells you the price.
2. 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 gets a thread URL and, when the result lands, a sequence URL. It reads that URL with `read`, never with a second `generate`, and sends you the link.
</Step>
<Step title="Voice the whole script in parts">
The agent voices the rest, one take per paragraph or lesson, the same way. It runs a dry run and tells you the price. 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` per take. It sees one sequence URL per take, and reads each with `read`.
</Step>
<Step title="Clean a rough recording">
If you recorded some lines yourself, the agent runs `media` `audio.clean` on the recording's sequence URL with `dry_run: true`, 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 a new sequence beside your original, which stays.
</Step>
<Step title="Check the sound">
The agent runs `media` `audio.loudness` and `audio.content` on each take. It looks for long silences and level jumps between takes. It sees the loudness and the speech and silence spans.
</Step>
<Step title="Deliver">
For files, the agent places the takes on `A1` in order with `insert_media`, each take's sequence URL as the `asset`. It calls `export` with `mode` `audio`, `audio_format` `wav`, and `target_lufs` for your platform. For a video, it places the voice with `insert_media` next to the picture. It sees an export card with status `completed`.
</Step>
</Steps>

The terminal version:

```bash
invideo gen models <project-url> --query "narration voice"
invideo gen model <project-url> --model seed_tts_2_0
invideo library search <project-url> \
  --library seed_tts_2_0_voices --query "calm narrator"
invideo gen run <page-url> --model seed_tts_2_0 --dry-run \
  -i prompt=@lesson-01.txt -i voice_id=<voice-id>
invideo gen run <page-url> --model seed_tts_2_0 --max-credits <price> \
  --idempotency-key lesson-01 -i prompt=@lesson-01.txt \
  -i voice_id=<voice-id>
invideo status <thread-url> --wait
invideo notebook show <thread-url>
invideo media audio.loudness <sequence-url>
invideo edit new <project-url> --name "Narration"
invideo edit show <edit-url> --json
invideo edit change <edit-url> --timeline T1 --token <state_token> \
  --operations @takes.json
invideo edit export <edit-url> --format video --mode audio \
  --audio-format wav --target-lufs -16 --json
invideo status <card-url> --wait
invideo notebook show <card-url> --out ./out
```

Where each value comes from:

- `<project-url>` comes from `invideo project new`, and `<edit-url>` from `invideo edit new`.
- `<page-url>` is a notebook page of the project, listed by `invideo notebook show <project-url>`.
- `<voice-id>` comes from the `seed_tts_2_0_voices` search. For `elevenlabs_text_to_speech.elevenlabs_tts_v4`, search `elevenlabs_text_to_speech_voices` instead. A voice id from one library is refused by the other model.
- `<price>` is the dry run's `.estimated_credits`.
- `<thread-url>` is the `url` the paid `gen run` returns, and `<sequence-url>` is the take that `notebook show` on it lists.
- `<state_token>` comes from `invideo edit show`. `@takes.json` reads a bare JSON list of `insert_media` ops, one per take, on `A1`.
- `<card-url>` is `.sequences[0]` of the export answer. `invideo status` waits but saves nothing. `invideo notebook show` saves the file in `./out`.

### Clone a voice, with consent

Clone a voice only when the speaker agrees, in writing. The agent brings in short clean clips of them with `add_media`. It runs `find_models` with "seed audio", and reads the inputs of its pick with `get_model`. A good pick is `seed_audio_1_0.ta2a`. Your agent can search for others with `find_models`. The first clip goes in `reference_audio_1`, and more clips in `reference_audio_2` and `reference_audio_3`. The prompt tags each clip as `@Audio1` to `@Audio3`.

This is the full MCP `generate` call, as the dry run. The agent tells you its price. For a price of 1 credit or more it waits for your yes. Then it sends the paid call, the same with `max_credits` set to the price and an `idempotency_key`, without `dry_run`. It returns a thread URL, and the agent reads the clone take's sequence URL with `read`. `<speaker-clip-sequence-url>` is the sequence URL `add_media` returned for a clip.

```json
{
  "model": "seed_audio_1_0.ta2a",
  "dry_run": true,
  "inputs": {
    "prompt": "@Audio1, warm and steady: \"Welcome to lesson one.\"",
    "reference_audio_1": "<speaker-clip-sequence-url>"
  }
}
```

## 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: every voice take, a clone take, and `audio.clean`.
- Free: searching voices, the previews, every edit and each export.
- `audio.loudness` and `audio.content`: the dry run shows the price before anything starts.

TTS prices go by the length of the text. A short script takes a few minutes. A full course takes longer, so run it in parts. See [Credits](/account/credits) for prices.

## If something goes wrong

Most problems are a wrong voice id or a text that is too long.

| 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. |
| `generate` refuses the voice: "voice_ids are model-specific" | The voice comes from another model's library. Search the library `get_model` names on the voice input: `seed_tts_2_0_voices` for `seed_tts_2_0`. |
| `invideo status --wait` ends and the folder is empty | `status` does not download. Run `invideo notebook show <card-url> --out <dir>`. |
| `generate` refuses the text | It is longer than the model takes. Split it into paragraphs. |
| Takes sound different from each other | Use the same model, voice and settings for every take. |
| A word is said wrong | Spell it as it sounds in the prompt, then voice that line again. |
| Long pauses between takes | Trim them on the timeline with `trim_clip`, or lower `silence_duration` on `seed_tts_2_0`. |
| `timeout` on `generate` | Do not call it again. `read` the thread or call `status`. |
| CLI exit 5 | Credits ran out. Stop and tell the person. |
| Avoid: clone a voice without consent | Instead: use a stock voice, or ask the speaker first. |

## Prompts to try

Each prompt is a real variation of this job.

```text
Narrate every lesson in ./course with the same voice. Do a few lessons,
send them to me, and wait for my yes before the rest.
```

```text
Make a dialogue from dialogue.txt with gemini_3_1_flash_tts.
Speaker A is calm, speaker B is curious.
```

```text
Clean up my own recording, intro.wav, and voice the missing last line in a
clone of my voice. I consent to the clone.
```

## When to use something else

For a long audiobook with chapter files and a narrator's review, a dedicated tool such as ElevenLabs Studio fits better, and you can bring its files into invideo. A human voice actor still beats TTS for brand voices and emotional reads.

## Related

- [Turn slides into a narrated video, one deck or hundreds](/how-to/training/slides-to-narrated-video)
- [Turn a syllabus into a course of lesson videos](/how-to/training/online-course)
- [Dub existing footage](/how-to/training/dub-existing-footage)
- [Use ElevenLabs with invideo](/integrations/elevenlabs)
- [Models](/reference/models)
- [Know the cost before you spend](/how-to/cost-before-you-spend)