---
title: Cut a customer testimonial or raw creator footage into an ad, or the case study set
description: Turn a customer interview or raw UGC footage into a 30 second ad, with the best lines picked from the transcript, b-roll, a name title, captions and music.
---
Cut a customer testimonial, a customer story interview or raw creator footage (UGC) into a 30 second ad. Your agent transcribes the talk, picks the strongest lines by reading it, cuts out the rest, covers the jumps with b-roll, adds the speaker's name and captions, and lays music under the voice. From the same footage it can cut the case study set: a 30 second ad, a 60 second story and a 15 second cutdown, each on its own branch.

This is ordinary ad work for a real customer and a real product. The agent cuts the footage you have the rights to. You get the customer's release, and you switch the ad on in the ad platform. The agent never publishes an ad or sends an email.

Use invideo for this when the cut should come from the customer's own words: the agent reads every word with its time, so you can ask for "the line about saving time" instead of scrubbing.

## Quickest path

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

```text
Here is a 20 minute interview with my customer Dana Ruiz, Ops Lead at
Fieldwork: ./interview.mp4. Transcribe it, pick the 4 strongest lines about
the problem, the switch and the result, and cut a 30 second ad. Cover the
jumps with the b-roll in ./broll, add her name and title, captions, and
music under her voice. Show me the lines you picked before you cut.
```

In a chat app, you drag the interview and b-roll into the project. In a terminal, the agent uploads them from the folders you name.

## What you need

The agent needs the footage and the facts about the speaker.

- The interview or the raw creator footage, with clear sound.
- B-roll: the product in use, the customer's place, screen recordings.
- The speaker's name and title, spelled the way they want.
- The customer's signed release for ads. You keep this; the agent does not check it.
- The shape: 9:16 for Reels and TikTok, 1:1 or 4:5 for feeds, 16:9 for YouTube.
- The mood of the music.

## Steps

The agent picks lines by reading, and shows you the picks before it cuts.

<Steps>
<Step title="Transcribe with speakers">
The agent prices `audio.transcript` on the interview's sequence URL with `dry_run`, tells you the price, starts it 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, so it can skip the interviewer's questions.
</Step>
<Step title="Pick the lines by reading">
The agent reads the transcript and picks lines for a simple arc: the problem, the switch, the result, and a line to close. It lists each pick with its start and end time and the exact words. You approve or swap them. It sees your answer in the chat.
</Step>
<Step title="Cut the talk down">
The agent makes the timeline with `new_timeline` and branches with `timeline_history`, naming the branch `ad-30`. It places each picked line with `place_media` and `linked_audio` set to true, in story order. To trim inside a line, it uses `split_clip` and `delete_clip` with mode `close_gap`. It sees a talk track about 30 seconds long.
</Step>
<Step title="Cover the jumps with b-roll">
The agent places b-roll above each cut with `place_media` mode `place_on_top`, so the voice runs on under it. It sees b-roll clips on the track above the talk.
</Step>
<Step title="Add the name and captions">
The agent adds a name title with `add_text` in the first seconds the speaker is on screen: name on one line, title and company on the next. It runs `audio.transcript` on the cut's talk clips for word times, or reuses the first transcript, and adds captions with `add_captions`. It sees a text clip and a caption clip.
</Step>
<Step title="Lay in music under the voice">
The agent runs `find_models` with the mood in words, picks one, and reads its inputs with `get_model`. A good pick is `elevenlabs_music_v2_5_instrumental`. It calls `generate` with `dry_run: true`, a `prompt` and a `duration` of 30, and tells you the price. Then it calls `generate` with `max_credits` set to that price and an `idempotency_key`. It reads the sequence URL it gets back with `read`, places the track on `A2`, adds the `ducker` audio effect, and fades the music out with `set_audio_fades`. A new timeline ducks under `A1` (Voice) by default. It sees a sequence URL for the music, then the music clip and the effect.
</Step>
<Step title="Check, then export">
The agent calls `capture` at each cut, the name title and the end card. It runs `media` `audio.loudness` on the interview source, and `check_timeline`. Then it runs `export` with a `target_lufs` for the platform. It sees an export card with status `completed`.
</Step>
</Steps>

For the case study set, the agent creates one branch per cut from the approved 30 second branch: `story-60` with more lines and `cut-15` with the one best line. Each branch exports on its own.

The terminal version:

```bash
invideo agent register <project-url> --name <name>
export INVIDEO_AS=<name>
invideo asset upload <project-url> ./interview.mp4 ./broll/*.mp4
invideo media audio.transcript <sequence-url> --dry-run
invideo media audio.transcript <sequence-url> --max-credits <price>
invideo edit new <project-url> --name "Fieldwork testimonial" \
  --width 1080 --height 1920
invideo edit show <edit-url> --json
invideo branch new <edit-url> --name ad-30 --token <state_token>
invideo edit show <branch-url> --json
invideo edit change <branch-url> --timeline T1 \
  --token <state_token> --operations @cut-30.json
invideo gen models <project-url> --query "warm instrumental music bed"
invideo gen model <project-url> --model elevenlabs_music_v2_5_instrumental
invideo gen run <page-url> --model elevenlabs_music_v2_5_instrumental \
  --dry-run -i prompt="warm hopeful acoustic" -i duration=30
invideo gen run <page-url> --model elevenlabs_music_v2_5_instrumental \
  --max-credits <price> --idempotency-key fieldwork-music-v1 \
  -i prompt="warm hopeful acoustic" -i duration=30
invideo edit capture <branch-url> --at 0.5,3,8,15,22,29 \
  --out ./frames
invideo edit export <branch-url> --format video --quality 1080 \
  --target-lufs -14 --file-name fieldwork_ad30_9x16_v1 --json
invideo status <card-url> --wait
invideo notebook show <card-url> --out ./out
mv ./out/export.mp4 ./out/fieldwork_ad30_9x16_v1.mp4
```

Where the values come from: `<project-url>` from `invideo project new`, and `<edit-url>` from `invideo edit new`. `<name>` is the name you choose in `invideo agent register`, and `<page-url>` is a notebook page from `invideo notebook show <project-url>`. `<sequence-url>` is the `url` that `invideo asset upload` returns for the interview. `<state_token>` comes from `invideo edit show` on the edit, and on the branch from `invideo edit show <branch-url>`. The main token is stale there. `<branch-url>` is the `branch_url` that `invideo branch new` returns. Each `<price>` is its dry run's `estimated_credits`. `<card-url>` is `.sequences[0]` of what the export returns. `invideo status` does not download, and the saved file is always `export.mp4`.

The ops file `cut-30.json` is a bare list. It holds two picked lines, a name title and the ducker on the music, at 30 frames a second. A new timeline has `V1` (A-roll), `V2` (B-roll), `V3` (Titles), `A1` (Voice) and `A2` (Music).

```json
[
  { "op": "place_media", "asset": "<sequence-url>",
    "track": "V1", "at_f": 0, "source_start_seconds": 312.4,
    "source_end_seconds": 318.9, "mode": "append", "linked_audio": true },
  { "op": "place_media", "asset": "<sequence-url>",
    "track": "V1", "at_f": 195, "source_start_seconds": 744.0,
    "source_end_seconds": 751.2, "mode": "append", "linked_audio": true },
  { "op": "add_text", "track": "V3", "start_f": 15, "end_f": 105,
    "text": "Dana Ruiz\nOps Lead, Fieldwork",
    "style": { "font_size": 44, "align": "left", "shadow": true } },
  { "op": "add_audio_effect", "target": "A2", "target_kind": "track",
    "effect": "ducker" }
]
```

The ducker reacts to `A1` (Voice), the default sidechain source of a new timeline. If the voice sits on another track, the agent adds `set_sidechain_source` with `timeline` `T1` and that track.

## 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 of the interview, paid stock music or a generated track, and `audio.clean` if the voice needs it.
- Free: picking lines, every cut, b-roll placement, titles, captions, `capture` and every `export`.
- `audio.loudness`: the dry run shows its price before anything starts.

A 30 second ad from a long interview takes under an hour, most of it your review of the picks. See [Credits](/account/credits) for prices.

## If something goes wrong

Most problems are a line cut mid-word or music that buries the voice.

| 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. |
| 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. |
| You want stock music instead | Run `search_library` in `stock_music`, and bring the pick in with `add_media`. |
| A line starts or ends mid-word | Use the word times from the transcript, and trim with `trim_clip` to the word's edge. |
| The jump in the face is visible | Cover the cut with b-roll on the track above, or add a short `cross_zoom` transition with `add_transition`. |
| The interviewer's voice is in the cut | Check the speaker of each word in the transcript, and cut those spans. |
| Music drowns the voice | Check that the voice is on `A1`, or point `set_sidechain_source` at the track it is on. Raise the ducker's `reduction` with `set_audio_effect_parameter`, using its id from `get_timeline`. |
| A noisy room | Run `media` `audio.clean` on the interview with `dry_run` first. It makes a new file; the original stays. |
| The transcript call returns a status, not words | It is still running. Call again with the same file after `retry_after_s`. Only the first call costs credits. |
| The download folder is empty | `invideo status` does not download. Run `invideo notebook show <card-url> --out ./out`. |
| CLI exit 3 on the branch | The token is the main timeline's. Run `invideo edit show <branch-url>` and use its token. |
| Avoid: changing what the customer meant | Instead: keep each line whole in meaning. Show the person every pick with its words. |
| Avoid: editing main | Instead: one branch per cut with `timeline_history`. |

## Prompts to try

Each prompt is a real variation of this job.

```text
Here are 6 raw selfie clips from a creator. Find the best hook in the first
sentence of each, and cut a 20 second UGC ad that opens on the best one.
```

```text
From the approved 30 second ad, make the case study set: a 60 second story
and a 15 second cutdown, one branch each, same music and look.
```

```text
Pull every line where the customer says a number, like hours saved or
money saved, with timecodes. Do not cut anything.
```

## When to use something else

invideo does not get releases or check claims. Get the customer's written release for ads, and check that any number they say is true before it runs. For a filmed interview with lights and two cameras, hire a videographer, then bring the footage in. For a full long-form documentary edit, see the documentary and interview page.

## Related

- [Documentary and interview edit](/how-to/film/documentary-and-interview-edit)
- [Make a UGC ad from product photos with an AI actor](/how-to/marketing/ugc-ad-with-ai-actor)
- [Word-by-word captions](/how-to/creators/word-by-word-captions)
- [Music and sound effects](/how-to/creators/music-and-sound-effects)
- [Make performance ads for Meta, TikTok and YouTube, named and iterated from results](/how-to/marketing/performance-ads)
- [Resize one master ad for every placement, with safe zones](/how-to/marketing/every-placement-from-one-master)