---
title: Make a vertical travel video from phone clips
description: A vertical travel video, or travel reel, from your phone clips: wide shots reframed to 9:16, music, a route title and text clear of the app buttons.
---
You get a vertical travel video, also called a travel reel or trip recap, from the clips on your phone. Wide clips are reframed to 9:16 so nothing important falls off the side. The video has music, a route title like "Lisbon to Porto to Faro", and text that stays clear of the buttons and captions the app puts on top.

Use invideo for this when your clips are a mix of vertical and wide shots: your agent reframes each wide clip on the timeline, keeps the subject in frame, and you can nudge any shot after.

## Quickest path

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

```text
Make a 45 second vertical travel video from the clips in ./portugal.
Reframe the wide clips to 9:16 and keep the people in frame. Add
upbeat music, the title "Lisbon to Porto to Faro" and keep text out
of the Reels buttons. Show me frames before you export.
```

In a chat app, you drag your clips into the project's Media panel in invideo, then name them in the chat. In a terminal, you name the folder and the agent uploads it.

## What you need

Pick your best clips. Fewer, better clips make a better reel.

- **Clips.** MP4 or MOV from your phone, vertical or wide, in any order.
- **The route or the places.** The title text, and a place name per clip if you want labels.
- **The platform.** Instagram Reels, TikTok or YouTube Shorts. Each puts its buttons in a different place.
- **The music mood.** Upbeat, chill, cinematic, or your own licensed track.
- **The length.** A number of seconds, or "the length of the song".

## Steps

Your agent edits on a branch and tells you what changed at each step.

<Steps>
<Step title="Make a vertical timeline">
The agent makes a project with `new_project` and a 9:16 timeline with `new_timeline`. It creates a branch with `timeline_history`. Expected result: a vertical timeline URL on the new branch.
</Step>
<Step title="Bring in the clips and look at them">
The agent uploads each clip with `start_upload` and `add_media`. It runs `media` with `metadata` for each clip's size and length, and `video.frames` to see what is in it. Expected result: a sequence URL per clip, and a note of which clips are wide.
</Step>
<Step title="Reframe the wide clips by hand">
For each wide clip, the agent places it with `place_media`, fills the frame with `fill_frame`, then slides it with `set_position` so the person or the view stays in the middle. It uses `set_zoom` to go closer. Expected result: no black bars, and the subject in the middle.
</Step>
<Step title="Reframe with a model when a crop loses too much">
The agent runs `find_models` with the job in words, like "reframe a wide video to vertical", and `get_model` on its pick. A good pick is `ltx_2_3_pro.reframe`. Your agent can search for others with `find_models`, such as `luma_ray_32.reframe`, which also needs a `prompt`. These models paint new picture at the edges. They do not track a face. The agent calls `generate` with `video`, `aspect_ratio` 9:16 and `dry_run: true`, 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`. Expected result: a new vertical sequence URL to place instead of the wide clip, which the agent reads with `read`, never with a second `generate`.
</Step>
<Step title="Mark the safe zones">
The agent adds guides with `create_guide` ops: one horizontal guide under the top bar, one above the caption area, and one vertical guide left of the buttons. It takes the positions from the platform's own safe zone guide. Expected result: guide lines in the editor that you see too.
</Step>
<Step title="Add the music and the route title">
The agent finds a song with `search_library` in `stock_music` and adds it with `add_media`. It reads the beats with `media` `audio.beats` and trims the cuts to them with `trim_clip`. It adds the route title and place names with `add_text`, inside the guides. Expected result: music under the whole video and text inside the safe area.
</Step>
<Step title="Check and export">
The agent runs `capture` at the title and at each place name, and `check_timeline`, and looks for no face cut off and no text under the guides. Then it runs `export` with `format` video and reads the card. In a terminal, it saves the file with `invideo notebook show` on the card's URL. Expected result: an export card with status `completed`.
</Step>
</Steps>

The same job from a terminal:

```bash
invideo project new --title "Portugal trip"
invideo agent register <project-url> --name <name>
export INVIDEO_AS=<name>
invideo edit new <project-url> --name "Reel" --width 1080 --height 1920
invideo asset upload <edit-url> ./portugal/*.mov
invideo media metadata <sequence-url>
invideo media video.frames <sequence-url> --at 0,3,6 --out ./frames
invideo gen models <project-url> \
  --query "reframe a wide video to vertical"
invideo gen model <project-url> --model ltx_2_3_pro.reframe
invideo gen run <page-url> --model ltx_2_3_pro.reframe --dry-run \
  -i video=<sequence-url> -i aspect_ratio=9:16
invideo gen run <page-url> --model ltx_2_3_pro.reframe \
  --max-credits <price> --idempotency-key reframe-c3 \
  -i video=<sequence-url> -i aspect_ratio=9:16
invideo edit show <edit-url>
invideo edit change <edit-url> --timeline T1 --token <state_token> \
  --operations @ops.json
invideo edit capture <edit-url> --at 1,10,20,40 --out ./frames
invideo edit export <edit-url> --format video --quality 1080 --json
invideo status <thread-url> --wait
invideo status <card-url> --wait
invideo notebook show <card-url> --out ./out
```

Where each value comes from:

- `<name>` is the name you choose in `invideo agent register`. `INVIDEO_AS` passes it to every later command.
- `<project-url>` comes from `invideo project new`, and `<edit-url>` from `invideo edit new`. `<sequence-url>` is the URL `invideo asset upload` returns for each clip.
- `<page-url>` is a notebook page; `invideo notebook show <project-url>` lists them. `<price>` is the dry run's `.estimated_credits`. `<thread-url>` is the `url` the paid run returns; `invideo notebook show <thread-url>` lists the new clip's sequence URL once it lands.
- `<state_token>` comes from `invideo edit show`. `ops.json` holds a bare list of operations, like the `operations` list in the call below.
- `<card-url>` is `.sequences[0]` of the export result. `invideo status` waits and saves nothing. `invideo notebook show` saves the file as `./out/export.mp4`.

The key MCP call is the full call. It reframes one wide clip by hand and adds the guides. The guide positions below are an example; the agent uses the platform's own spec. `<branch-url>` is the `branch_url` that `timeline_history` returns. The `<state_token>` and the clip ids, like `c3`, come from `get_timeline`.

```json
{
  "url": "<branch-url>",
  "timeline_id": "T1",
  "expected_state_token": "<state_token>",
  "operations": [
    { "op": "fill_frame", "clip": "c3" },
    { "op": "set_position", "clip": "c3", "x": -220, "y": 0 },
    { "op": "create_guide", "timeline": "T1",
      "orientation": "horizontal", "position": 0.1, "locked": true },
    { "op": "create_guide", "timeline": "T1",
      "orientation": "horizontal", "position": 0.8, "locked": true },
    { "op": "create_guide", "timeline": "T1",
      "orientation": "vertical", "position": 0.85, "locked": true },
    { "op": "add_text", "track": "V2", "start_f": 0, "end_f": 90,
      "text": "Lisbon to Porto to Faro",
      "style": { "font_size": 80, "align": "center", "stroke": true,
                 "stroke_color": "#000000", "stroke_width": 4 } }
  ]
}
```

`set_position` takes canvas pixels from the center, so a negative `x` slides the picture left.

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

- **Free:** uploads, the manual reframe with `fill_frame`, `set_position` and `set_zoom`, the guides, text and the export.
- **Tool ops:** `metadata`, `video.frames`, `audio.beats` and `video.trim`. The dry run shows its price before anything starts.
- **Costs credits:** paid stock music, and the reframe models, which charge per second of the source clip.

A reel takes a few minutes with manual reframes. Model reframes add the time the model takes for each clip. See [Credits and what they cost](/account/credits) for prices.

## If something goes wrong

Most problems are a subject that leaves the frame, or text under the app's buttons.

| 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. |
| A person walks out of the vertical frame | Split the clip with `split_clip` and set a new `set_position` for each part, or use a reframe model. |
| The reframe model is refused for the clip's length or format | Trim the clip with `media` `video.trim` first, and send the shorter file. |
| A reframed edge looks invented | It is: the model paints the sides. Use the manual crop for faces and signs. |
| Text sits under the like and share buttons | Move it inside the guides with `set_position`, and `capture` again. |
| `fill_frame` is refused | The clip's zoom is animated. Remove the animation, or set the size with `set_zoom`. |
| CLI exit 5 | A credit limit. Reframe by hand, which is free. |
| Avoid: the agent resends `generate` to check on a reframe | Instead: it reads the thread URL, or calls `status`. |
| Avoid: saying done without looking | Instead: `capture` frames at every text and every reframed clip. |

## Prompts to try

Each prompt is a real variation of the job.

```text
Make a 30 second TikTok from my Japan clips in ./japan. Fast cuts on
the beat, a place name on each clip, and keep everything clear of the
TikTok buttons.
```

```text
I have wide drone shots in ./drone. Make a vertical reel, reframe them
with a model so nothing is cropped, and tell me the price first.
```

```text
Make a one minute road trip recap from ./roadtrip. Open with a map
image I give you, then the title "Route 66, 12 days". Chill music.
```

## When to use something else

invideo does not draw animated route maps from GPS data. Make the map in a map tool, export an image or a clip, and your agent brings it in with `add_media`.

For stabilizing very shaky footage, use your phone's own stabilization when you shoot, or a desktop editor. invideo has no stabilize op.

## Related

- [Repurpose one video for every platform, one branch each](/how-to/creators/repurpose-for-every-platform)
- [Make Reels and Shorts from your footage: hook, captions, transitions, sound effects](/how-to/creators/reels-and-shorts-from-your-footage)
- [Make a birthday montage from photos and clips](/how-to/personal/birthday-montage)
- [Add music and sound effects that clear rights](/how-to/creators/music-and-sound-effects)
- [Check your video before you say it is done](/how-to/check-your-video)