# invideo Documentation
> invideo makes and edits video on a real timeline, for people and for the AI agents they use (Claude, ChatGPT, Cursor, Codex and others, through invideo MCP or the invideo CLI). These docs have one page per video task, in the words people use to ask for it, plus setup, reference, comparisons and the invideo help center.
### Quick start
Source: https://docs.invideo.io/agents.md
Connect your agent to invideo to generate video, images, voice and music with the leading models and edit them on a real timeline. Setup lines for every client, the work loop and the rules.
invideo puts the leading video, image, voice and music models next to a professional timeline editor, in the cloud. Your agent generates the shots, voices and music a project lacks, cuts them with your own footage, and exports the result, all in one project. Agents and people work in the same project, and you watch every edit land live in the browser.
Agents with a terminal use the invideo plugin and the CLI. The plugin installs the skills, and the agent installs the CLI, one binary with no sudo, on the first video job. In Cursor, add the skills with `npx skills add invideoio/skills`. The Cursor Marketplace plugin is the MCP route in Cursor, and it carries the MCP server only. Agents without a shell use the MCP server at `https://mcp.invideo.io/mcp`.
```bash
claude plugin marketplace add invideoio/skills
claude plugin install invideo@invideo
```
```bash
codex plugin marketplace add invideoio/skills
codex plugin add invideo@invideo
```
```bash
agy plugin install https://github.com/invideoio/skills
```
```bash
npx skills add invideoio/skills
```
```bash
npx skills add invideoio/skills
```
```bash
curl -fsSL https://agents.invideo.io | sh
```
```powershell
irm https://raw.githubusercontent.com/invideoio/cli/main/install.ps1 | iex
```
A chat app, or an agent with no shell, uses MCP. In ChatGPT, the person installs the official invideo plugin: **Customize**, then **Plugins**. In Claude on the web or desktop, the person adds invideo as a connector: **Customize**, then **Connectors**. In the Gemini app, the person adds invideo as a custom app with the server URL: **Settings**, then **Connected Apps**. Antigravity, Claude Code and Codex use the CLI only. Cursor uses the CLI first, and its Marketplace plugin is the MCP route. Gemini CLI and VS Code have no invideo plugin, so they take the MCP server with the lines below.
```bash
code --add-mcp \
'{"name":"invideo","type":"http","url":"https://mcp.invideo.io/mcp"}'
```
```bash
gemini mcp add --transport http --scope user invideo \
https://mcp.invideo.io/mcp
```
```json
{
"mcpServers": {
"invideo": { "url": "https://mcp.invideo.io/mcp" }
}
}
```
Each start page below has the full steps.
If you are the agent reading this, run the setup yourself. Installing the plugin, the CLI or the server changes the person's machine, so ask once ("Should I add invideo to your Claude Code?") and do it on a yes. Give the person a command to run only when nothing you can do works.
#### Start pages
Each client has its own page with the setup, the sign-in, a first prompt and the known failures.
- [Set up invideo in Claude Code](/start/claude-code)
- [Set up invideo in Codex](/start/codex)
- [Set up invideo in Cursor](/start/cursor)
- [Set up invideo in Antigravity](/start/antigravity)
- [Use invideo in ChatGPT](/start/chatgpt)
- [Use invideo in Gemini](/start/gemini)
- [Make a video with invideo without a terminal](/start/no-terminal)
The plugin for Claude Code, Codex and Antigravity, and the skills command for Cursor, install the invideo skills, and the skills are CLI-first: the agent installs the CLI and signs in on the first job. The plugin carries no MCP config. ChatGPT uses the official invideo plugin. The Gemini app takes invideo as a custom app, Claude on the web as a connector, and Gemini CLI and VS Code as an MCP server. None of them gets the skills.
#### CLI or MCP
Both reach the same projects, the same timelines and the same account. Pick one per session and stay on it. Mixing them in one session splits your state and your name in the project.
- **Use the CLI** in a terminal agent with the invideo plugin (Claude Code, Codex, Cursor, Antigravity), and in a script or a server. It is the only way in that the start pages give for those agents, and the way the skills teach. Each command prints the URLs and the next step. Add `--json` to get one JSON object. `--out
` saves files on `invideo notebook show` and `invideo edit capture`; `invideo status` follows a job and saves nothing.
- **Use MCP** in a chat app (Claude on the web, ChatGPT, the Gemini app), in VS Code or Gemini CLI, which have no invideo plugin, or in any agent with no shell. You call tools like `read` and `edit_timeline` and pass a URL as `url`. Gemini CLI has a shell, so it can also use the CLI instead; see [Use invideo in Gemini](/start/gemini).
Check the CLI install with this command. It prints `invideo` and the version.
```bash
invideo --version
```
#### Sign in
There are no API keys. Every call runs as a signed-in person, in the workspaces they chose when they signed in.
- **CLI.** Run `invideo auth login` in the background or with a long timeout. It opens the browser and waits. Tell the person in one message that a sign-in tab opened.
- **MCP in a chat app.** Claude on the web, ChatGPT and the Gemini app run the browser sign-in themselves when the server asks.
- **MCP in VS Code, Gemini CLI or another client.** The client runs the browser sign-in when the server asks, or has its own authenticate step. In Gemini CLI that step is `/mcp auth invideo`. Signing in the invideo CLI does not sign in an MCP connection. Never run a login command that needs an interactive terminal from the agent's shell.
- **No browser.** On ssh, in a container or on a cloud machine, use a device code.
```bash
INVIDEO_NO_BROWSER=1 invideo auth login
invideo auth status
```
The first command prints a link and a code at once, then waits for approval. Send the person the link and the code in one message. They open the link on their own device and approve. `invideo auth status` shows who is signed in.
The device code above signs in the CLI only. An MCP client with no browser uses its own device flow over HTTP. The steps are on [Sign in, device codes and sessions](/account/sign-in).
Never ask the person for a password, an email code, or the device code back. One sign-in at a time: while a code is pending, do not request another.
#### The work loop
Every job follows the same loop. Call `get_help` once for the full guide, or `get_help` with a `topic` for one section.
Find the project with `list_projects`, or make one with `new_project`. Call `read` on its URL to see pages, threads and sequences. In a project, call `register` once and pass your name as `as` on every call there. You see the project URL and its notebook.
Add files with `add_media`. For a local file, call `start_upload`, PUT the bytes to the `upload_url`, then call `add_media` with the `upload_id`. In Claude on the web or the desktop app, the PUT goes from Claude's sandbox to the `upload_url` on m.invideo.io. If that PUT cannot reach m.invideo.io (the host does not resolve, the connection is refused, or the sandbox network blocks it), do not describe the files in text instead, and do not ask the person to send them again. Tell the person to allow m.invideo.io in Claude under **Settings**, then **Capabilities**, then **Allowed websites** (the steps are on [How do I let Claude upload my files to invideo?](/ai-agents/how-do-i-let-claude-upload-my-files-to-invideo)), and retry in a new chat once they have. Find stock with `search_library`. You see a sequence URL for each file.
For a shot, an image, a voice, music or a sound that nobody has, find a model with `find_models` and the job in words, read its inputs with `get_model`, then call `generate` with `dry_run: true` to price it. Follow the cost rule below, then call `generate` again with `max_credits` and an `idempotency_key`. Media inputs are sequence URLs from this project. You see a thread URL at once and a sequence URL when the result lands; `read` it, do not call `generate` again to check.
Make a timeline with `new_timeline`. Create a branch with `timeline_history` unless the person asks you to edit main. Read with `get_timeline`, look up fields with `timeline_catalog`, then send `edit_timeline` with the latest `expected_state_token`. You see a new `state_token`.
Look before you say done. Call `capture` at the moments that matter, `check_timeline` for structure, and `media` with `video.frames` or `audio.loudness` for a source. You see frames and findings. Fix what is wrong and look again.
Call `export` with the timeline URL and a `format`, and an `idempotency_key`. `video` renders a 1080p MP4. `premiere`, `davinci` and `final_cut` make a project file that Premiere Pro, DaVinci Resolve or Final Cut Pro opens. You see an export card URL. `read` it until its status is `completed`. For `premiere`, `final_cut` and `davinci`, read it again until the files list is there; `completed` can come before the media package.
Tell the person what changed, with timecodes and URLs. Each URL opens exactly that thing in invideo.
The same loop on the CLI. The CLI calls a timeline an edit: `invideo edit new` makes a timeline, and the `` it prints is the timeline's URL.
```bash
invideo project new --title "Launch teaser"
invideo agent register --name scout
export INVIDEO_AS=scout
invideo asset upload ./shots/*.mp4
invideo notebook new-page --title "Shots"
invideo gen models --query "cinematic product shot"
invideo gen model --model seedance_2_5.r2v
invideo gen run --model seedance_2_5.r2v --dry-run \
-i prompt="slow push on the bottle, studio light"
invideo gen run --model seedance_2_5.r2v \
--max-credits --idempotency-key teaser-shot-1 \
-i prompt="slow push on the bottle, studio light"
invideo edit new --name "Teaser v1"
invideo edit show --json
invideo edit change --timeline T1 --token \
--operations @ops.json
invideo edit check
invideo edit capture --at 1,3.5 --out ./frames
invideo edit export --format video --quality 1080 \
--idempotency-key teaser-export-1 --json
invideo status --wait
invideo notebook show --out ./out
```
Where each value comes from:
- `` comes from `invideo project new`. `INVIDEO_AS` passes your registered name on every later command, like `--as`.
- `` comes from `invideo notebook new-page`. A generation on a page starts a new thread there.
- `` is the `estimated_credits` in the dry run's answer. Pass the same `-i` inputs on the dry run and the paid run.
- `` comes from `invideo edit new`. `` comes from `invideo edit show`, and each `edit change` returns a fresh one.
- `ops.json` holds a bare JSON list of operations, `[ {...}, {...} ]`, with no `operations` wrapper.
- `` is `.sequences[0]` in the export's answer. `invideo status` waits for the job and saves nothing. `invideo notebook show` with `--out` saves the file as `export.mp4`; rename it after.
#### The cost rule
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.
Your agent runs a dry run first. For any call that costs 1 credit or more, it tells you the price and waits for your yes. A call under 1 credit runs without asking, and your agent tells you what it cost.
The agent follows these rules on every paid call.
- On MCP, call `generate` with `dry_run: true` before the first paid call. It checks the inputs and returns the price. Then call it with `max_credits` set, so nothing starts above that price.
- Paid `media` ops, like `audio.transcript` and `video.upscale`, take the same `dry_run` and `max_credits`.
- On the CLI, use `--dry-run` first, then `--max-credits`.
- Pass an `idempotency_key` on every `generate` and `export`. A retry after a timeout then finds the first job instead of paying twice.
- Models retire. A retired model is refused as not found. Run `find_models` with the job in words, use the best match, run the dry run again, and tell the person which model you used.
Prices and the balance are on [Credits and what they cost](/account/credits). For a question about plans, prices or credits, read the credits FAQ and its sibling pages, not only the pricing page.
#### Check before you say done
Never tell the person a video is done without looking at it. Call `capture` on the timeline, read the frames, and call `check_timeline`. On the CLI, run `invideo edit capture` and `invideo edit check`. `check_timeline` reports structure only. It does not judge the cut, so you still look at the frames.
#### Conventions
These are the places where agents guess wrong. The left column is the usual guess. The right column is what invideo does.
| You'd guess | invideo uses |
| --- | --- |
| An id or a name points to a project | A URL is the handle. Every tool takes a URL as `url` and every result gives the next URLs. |
| Calls are anonymous | `register` a name in the project, then pass it as `as` on every call. |
| Each operation lands on its own | An `edit_timeline` call lands all its operations or none. |
| After a timeout, read again and resend | Retry the identical request with the same `expected_state_token`. A conflict means it landed. |
| Decide the next call yourself | Follow `next` in every result. It names the tools and URLs to use. |
| Call `generate` again to check on it | `read` the URL or call `status`. `retry_after_s` says when to look again. |
| A model id from a page or a saved prompt is forever | Models retire. A retired model is refused as not found. Run `find_models` with the job in words, use the best match, and tell the person. |
| Edit the main timeline | Create a branch with `timeline_history`. Edit main only when asked. |
| Mix the CLI and MCP | Pick one per session. The skills teach the CLI wherever there is a shell. |
| Ask the person for the sign-in code | Send the link and the code once. Never ask for them back. |
| Say done after the last edit | `capture` and `check_timeline` first. |
#### If something goes wrong
Read the refusal. A refused call returns `status`, `code` and `message`. The `message` says how to fix it once.
| Symptom | Fix |
| --- | --- |
| invideo's tools do not appear in the client. | Finish the sign-in, then restart the client. In a chat app, turn invideo on for the chat. Then follow your start page again. |
| Refusal `code` `unavailable` | The timeline is starting. Retry the same request in a few seconds. |
| Refusal `code` `timeout` on a read, capture or `media` look | Nothing changed. Read again. |
| Refusal `status` `completion_unknown` | Retry the identical request with the same token. |
| Refusal `code` `failed` | Try once more. If it fails again, file `report_bug` and tell the person. |
| Job `status` `needs_confirmation` | Only the person can confirm it, in invideo. Tell them. |
| CLI exit code 4 | Not signed in. Run `invideo auth login`. |
| CLI exit code 3 | The timeline changed since your read. Run `invideo edit show` and rebuild. |
| Exit code 6, or `status` is `held` | A safety review, not a refusal. Wait, then run `invideo status --wait` again. Do not resend. If it ends `failed`, change the input the review flagged. |
| `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. |
| The same refusal comes back | Stop, tell the person, and file `report_bug`. |
File `request_feature` when the person asks for something these tools cannot do, and say what you did instead. If invideo still does not connect, tell the person this: Contact support from the chat button on the invideo site, or email support@invideo.io. Include the client's name, the exact error and a screenshot of the connection status.
#### How-to guides for every job
These pages cover habits that apply to every job.
- [Check your video before you say it is done](/how-to/check-your-video)
- [Know the cost before you spend](/how-to/cost-before-you-spend)
- [Run invideo in CI and on servers](/how-to/ci-and-servers)
- [Export to Premiere Pro, DaVinci Resolve or Final Cut Pro](/how-to/export-to-premiere-resolve-final-cut)
#### Reference
- [MCP tools](/reference/mcp-tools)
- [CLI commands and flags](/reference/cli)
- [Exit codes](/reference/exit-codes)
- [Sign in, device codes and sessions](/account/sign-in)
- [Is there an invideo API?](/api)
### Know the cost first
Source: https://docs.invideo.io/how-to/cost-before-you-spend.md
Your agent prices every paid call with a dry run, caps it with max_credits, and tells you the price before it spends.
Your agent knows the price of a generation, a transcript or an upscale before it starts one. It runs a dry run, which prices the call and spends nothing. Then it runs the real call with a cap, so nothing starts above the price you agreed to. A retry after a timeout does not pay twice.
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. For prices, see [Credits](/account/credits).
Your agent runs a dry run first. For any call that costs 1 credit or more, it tells you the price and waits for your yes. A call under 1 credit runs without asking, and your agent tells you what it cost.
#### Quickest path
Set up your agent first with [invideo for AI agents](/agents). Then give it this rule once, at the start of the session:
```text
Before any call that costs credits, run it with dry_run and tell me the
price. Pass max_credits and an idempotency_key on every paid call. Wait
for my yes before you spend.
```
In a chat app, your agent shows you the price in the chat. In a terminal, it prints the dry run answer, then runs the real command with `--max-credits`.
#### How it works
Three fields control spending. They work the same on `generate` and on the `media` ops that cost credits.
- **`dry_run`** checks the inputs and returns the price. Nothing starts and nothing is spent.
- **`max_credits`** is the most a call may cost. Nothing starts when the price is higher. `generate` needs it on every call that is not a dry run. A `media` op needs it when the op costs anything.
- **`idempotency_key`** is any text unique to one call, like a UUID. A retry with the same key finds the first call's result instead of paying twice.
Credit prices by model are on the video, image and audio pricing pages. The dry run is the price of one specific call. Some prices are estimates. When `pricing` says the price is an estimate, the final charge can end above it.
##### What costs credits and what is free
Most of the work on a timeline is free. The paid calls are generation, transcripts, the model ops in `media`, and stock.
| Free | Costs credits |
| --- | --- |
| Every `edit_timeline` change | `generate` with any model |
| `export`, in every format | `media` `audio.transcript`, on the call that starts it |
| `capture`, `check_timeline`, `get_timeline` | `media` model ops: `audio.separate`, `audio.clean`, `video.upscale`, `image.upscale`, `video.removeBackground`, `image.removeBackground` |
| `media` reads: `metadata`, `video.frames`, `audio.waveform`, `audio.loudness`, `audio.content`, `audio.beats` and the other audio reads | `add_media` with `stock`, at the cost the search result shows |
| `find_models`, `get_model`, `search_library` | |
`document.pages` and the `media` tool ops that make a file without a model, like `video.trim`, `video.loop` and `image.crop`, can show a charge. The dry run shows its price before anything starts.
A transcript charges once. Your agent calls `audio.transcript` again with the same file to follow it, and later calls spend nothing.
Stock has no dry run. `search_library` shows each result's `cost`, and searching and looking are free. Your agent tells you the cost before it adds a paid stock item with `add_media`.
##### Check, then preview, then render
Spend credits last. Work in this order:
1. **Check.** Run the dry run. It checks the inputs and gives the price for free.
2. **Preview.** Look before you pay for the full thing. On a timeline, `capture` shows frames for free. For a generation, try a short or low resolution take first, where the model's inputs allow it. `get_model` lists the inputs.
3. **Render.** Run the full call with `max_credits` and an `idempotency_key`.
#### Steps
The agent follows these steps for every paid call.
The agent runs `find_models` with the job in words and picks a model. A good pick for a video shot is `seedance_2_5.r2v`. It runs `get_model` for the model's inputs, then calls `generate` with `dry_run: true`. Expected result: a price and no new sequence.
The agent tells you the price in credits, and says when it is an estimate. It follows the rule at the top of this page. Expected result: your yes, or a change to the request.
The agent calls `generate` with `max_credits` at the agreed price and a new `idempotency_key`. Expected result: a thread URL at once.
The agent follows the job with `status` or `read`, at the time `retry_after_s` gives. Expected result: status `completed`. The agent never calls `generate` again to check on a job.
When the status is `needs_confirmation`, only you can confirm it, in invideo. When it is `held`, a safety review is running and the job is not over. Expected result: the agent tells you and waits.
The same flow from a terminal:
```bash
invideo gen models --query "slow product shot"
invideo gen model --model seedance_2_5.r2v
invideo gen run --model seedance_2_5.r2v --dry-run \
-i prompt="slow push on the bottle"
invideo gen run --model seedance_2_5.r2v \
--max-credits --idempotency-key bottle-push-1 \
-i prompt="slow push on the bottle"
invideo status --wait
```
Where each value comes from: `` from `invideo project new`, and `` from `invideo notebook show `, which lists the notebook pages. `` is the price the person agreed to, from the dry run's `.estimated_credits`. `` is the `url` the paid run returns. A retry uses the same key, so it finds the first run.
A `media` op that costs takes the same flags:
```bash
invideo media audio.transcript --dry-run
invideo media audio.transcript --max-credits
invideo media video.upscale --dry-run
```
`` is the file's sequence URL, from `add_media` or `invideo asset upload`. Ops that cost or make a file refuse a media bin id with `--asset`.
The key MCP call is the arguments of `generate` for a dry run. `` is a notebook page from `read` on the project.
```json
{
"url": "",
"model": "seedance_2_5.r2v",
"inputs": { "prompt": "slow push on the bottle" },
"dry_run": true
}
```
Your agent checks the credits you have left with `current_user`. In a terminal, `invideo auth status` shows them.
#### If something goes wrong
Most cost problems come from a retry without a key, or a cap that is too low.
| 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. |
| Refused because the price is above `max_credits` | Nothing started. Tell the person the new price, then run again with a new cap. |
| CLI exit 5, a limit: credits or quota | Stop and tell the person. Credits are listed in [Credits](/account/credits). |
| CLI exit 8, needs the person's confirmation | Stop. The person confirms it in invideo. |
| Exit code 6, or `status` is `held` | A safety review, not a refusal. Wait, then run `invideo status --wait` again. Do not resend. If it ends `failed`, change the input the review flagged. |
| Status `needs_confirmation` | Only the person can confirm it, in invideo. Tell them and wait. |
| The dry run refuses an input | Fix the input the `message` names, and run the dry run again. |
| A call returns `completion_unknown`, or times out | Retry the same call with the same `idempotency_key`. It finds the first call. |
| The final charge is above the dry run price | The price was an estimate. Say so before the next estimated call. |
| Avoid: calling `generate` again to check on a job | Instead: `read` or `status` the thread URL. |
| Avoid: a retry with a new `idempotency_key` | Instead: retry with the same key, so it does not pay twice. |
| Avoid: spending first and telling the person after | Instead: dry run, tell the price, then spend. |
#### Related
- [Credits](/account/credits)
- [Check your video before you say it is done](/how-to/check-your-video)
- [Run invideo in CI and on servers](/how-to/ci-and-servers)
- [Models reference](/reference/models)
- [Exit codes](/reference/exit-codes)
### Check your video
Source: https://docs.invideo.io/how-to/check-your-video.md
Your agent looks at frames and listens to the sound before it reports done, then exports a video that has no cropped faces or silent clips.
Your agent checks the video before it tells you the video is done. It looks at frames of the timeline, listens to the sound of each clip, and checks the timeline's structure. Then it exports. A cut that "should be fine" often has a cropped face, a caption off the edge of a phone, or a clip with no sound.
Use invideo for this when your agent edits on a timeline: it can render the timeline as it looks now and read every source file's sound, for free, before the export.
#### Quickest path
Set up your agent first with [invideo for AI agents](/agents). Then add this line to any edit request:
```text
Before you say it is done, capture frames across the timeline, check the
sound of every clip, run check_timeline, and tell me what you found.
```
In a chat app, your agent shows you the frames it captured. In a terminal, it saves them to a folder with `--out`, and you open the folder.
#### How it works
Three tools do the checking. None of them changes the timeline. `capture`, `check_timeline` and the `media` reads cost no credits.
- **`capture` looks at the timeline.** It renders frames of the timeline as it looks now, with every layer, effect and text, at the times in seconds you list in `at_seconds`. In the CLI it is `invideo edit capture` with `--at`.
- **`media` looks at and listens to one source file.** `video.frames` gives frames of the file. `metadata` gives its streams and duration. `audio.waveform` gives its levels, `audio.loudness` its integrated loudness, true peak and range, and `audio.content` where its sound is speech, music, effects or silence. `video.contactSheet` makes one image of frames across a video, in a grid.
- **`check_timeline` checks the structure.** It finds problems like a clip that points at something deleted, or a clip that ends before it starts. Each finding says what is wrong and how to fix it. In the CLI it is `invideo edit check`.
`check_timeline` does not judge the edit. A timeline can pass the check and still look wrong. That is why the agent also looks and listens.
The `media` sound reads work on one file, not on the live timeline. The agent checks each clip's source before the export, sets the loudness of the mix with `target_lufs` on `export`, then runs `audio.loudness` on the export card's URL to confirm the final mix. The card is a sequence, so `media` takes it as it is.
#### Steps
The agent runs this checklist after the last edit and before the export. It fixes what it finds, then runs the checklist again.
The agent runs `check_timeline` on the timeline URL. It fixes each finding with `edit_timeline`, as the finding says. Expected result: no findings.
The agent runs `capture` at `0` and at one frame before the end. Ranges are half-open, so the end time itself is past the last frame. Expected result: two frames that open and close the video the way you asked, with no black frame and no stray clip.
The agent picks times at each cut, each text, and each caption, and runs `capture` with them. It looks for faces cut off at the edge, heads cut at the top, and text near the frame edge. Expected result: every face in frame.
The agent looks at each captured frame with text as a person sees it on a phone. Text that is thin, small, or low in contrast fails this check. Expected result: you can read every line at a glance.
The agent runs `media` with `metadata` on each source that should have sound, and checks it has an audio stream. Then it runs `audio.content` to see where the sound is speech, music, effects or silence. Expected result: sound on every clip that needs it, and silence only where you want a pause.
The agent runs `audio.loudness` on the voice and music sources, and `audio.waveform` where it needs the shape of the sound. It compares them with the target loudness for the platform. Expected result: every source in range. For the final mix, the agent sets `target_lufs` on the export to the platform's target.
The agent tells you what it checked, what it fixed, and what needs your eye, with timecodes and the timeline URL. Then it runs `export`. Expected result: an export card with status `completed`.
The same checklist from a terminal:
```bash
invideo edit check
invideo edit capture --at 0,4.5,9,14.9 --out ./frames
invideo media metadata
invideo media audio.content
invideo media audio.loudness
invideo media audio.waveform
invideo media video.frames --at 0,2,4 --out ./frames
invideo edit export --format video --quality 1080
```
Where each value comes from: `` from `invideo edit new`, and `` from the URL `invideo asset upload` or `add_media` returns. For a read of a file in an edit's media bin, like `metadata` or `audio.loudness`, pass the edit's URL and `--asset` with the bin id from `invideo edit show`. Ops that make a file, like `video.contactSheet`, refuse `--asset`; pass the sequence URL.
The key MCP call is `capture`, with frames at the cuts and the last frame. `` is the timeline's URL, from `new_timeline` or `invideo edit new`.
```json
{
"url": "",
"at_seconds": [0, 4.5, 9, 14.9]
}
```
To see a whole source video in one image, the agent runs `media` with `video.contactSheet`. It needs all four of `columns`, `rows`, `resolution` and `style`, and `style` is `labeled` or `plain`. It takes the file's sequence URL, and it makes a new image beside the file. It is a tool op; the dry run shows its price before anything starts. Like every op that makes a file, it still takes `dry_run` and `idempotency_key`.
#### What to check
This list is the minimum. Add your own checks to the project's rules in `invideo_rules.md`, and your agent reads them before it works.
| Check | Tool | Pass |
| --- | --- | --- |
| Structure | `check_timeline` | No findings |
| First and last frame | `capture` | Opens and closes as asked |
| Faces | `capture` | No face cut at an edge |
| Text | `capture` | Readable on a phone |
| Sound on each clip | `metadata`, `audio.content` | Sound where it is needed |
| Silence | `audio.content` | Silence only where planned |
| Loudness | `audio.loudness`, `audio.waveform` | In range for the platform |
#### If something goes wrong
Most problems come from a stale read or from saying done too early.
| Symptom | Fix |
| --- | --- |
| `capture` returns `timeout` | Nothing changed. Run the same `capture` again. |
| `capture` returns `unavailable` | The timeline is starting. Retry the same call in a few seconds. |
| `check_timeline` is refused with an old `expected_state_token` | The timeline changed since that read. Read it again with `get_timeline`, then check again. |
| CLI exit 3 | The edit changed since your read. Run `invideo edit show` again. |
| CLI exit 7 | Nothing ran. Run the same command again. |
| A finding comes back after the fix | Read the timeline again with `get_timeline`, fix it once more, and tell the person if it stays. |
| A face is cropped in a capture | Move or zoom the clip with `set_position` or `set_zoom`, then capture again. |
| Text is hard to read at phone size | Change the text with `set_text_style`, then capture again. |
| A clip is silent | Check that its sound is on the timeline and not muted. |
| The export card status is `failed` | Read the card for the reason, and tell the person. |
| A frame looks wrong after a fix | Capture again. A capture is tied to a revision, so an old capture shows the old edit. |
| The export is too loud or too quiet | Export again with `target_lufs` set to the platform's target. |
| Avoid: saying done after `check_timeline` alone | Instead: capture frames and read the sound too. The check does not judge the edit. |
| Avoid: checking the sound of the export only | Instead: check each source with `media`, so you know which clip to fix. |
#### Related
- [Know the cost before you spend](/how-to/cost-before-you-spend)
- [Run invideo in CI and on servers](/how-to/ci-and-servers)
- [Export to Premiere Pro, DaVinci Resolve or Final Cut Pro](/how-to/export-to-premiere-resolve-final-cut)
- [MCP tools reference](/reference/mcp-tools)
- [Export loudness, track volume and peak level](/guides/how-is-export-loudness-different-from-track-volume-and-peak-level)
### CI and servers
Source: https://docs.invideo.io/how-to/ci-and-servers.md
Sign in once with a device code, then run the invideo CLI from a script with JSON answers, capped spending, safe retries and a file on disk.
You can run invideo from a CI job, a cron script or a server with no browser. A person signs in once with a device code. After that, the script makes projects, uploads files, generates with a cap, exports, and saves the result to disk. Every answer is JSON, and every failure has an exit code your script can act on.
There are no API keys. Every command runs as a signed-in person, in their workspace.
#### How it works
The CLI is one binary, and the same commands work in a terminal and in a script. Six things make it fit for a pipeline.
- **Device sign-in.** `INVIDEO_NO_BROWSER=1 invideo auth login` prints a link and a code, then waits until a person approves on their own device. The CLI keeps the sign-in on this machine. `invideo auth logout` forgets it.
- **A name in the project.** `invideo agent register` takes a name in a project, like `ci-render`. Set it once as `INVIDEO_AS`, or pass `--as` on every command. People watching the project see who did what.
- **JSON answers.** `--json` prints the whole answer as one JSON object, with `next` included. `INVIDEO_JSON=1` does that on every command. Errors and notices always go to stderr.
- **Waiting.** A command that starts a job returns the URL to follow at once. `invideo status --wait` checks back until the job is over. `--timeout ` stops waiting after that many seconds. `invideo status` saves no file. `invideo notebook show --out ` saves what an export made, with the card's URL from the export answer's `.sequences[0]`.
- **Exit codes.** Each failure has its own code, so the script knows when to retry and when to stop and tell a person.
- **Safe retries and caps.** `--idempotency-key` on `invideo gen run` and `invideo edit export` makes a retry find the first job. `--max-credits` on `invideo gen run` stops a generation that costs more than the cap.
##### Sign in on a machine with no browser
A person signs in once, on the machine that runs the jobs. Run this in the background, because it waits for the approval:
```bash
INVIDEO_NO_BROWSER=1 invideo auth login
```
It prints a link and a code at once. Send both to the person in one message. They open the link on their own device, enter the code, and choose the workspaces. Then check it:
```bash
invideo auth status
```
Never ask the person for a password, an email code or the code back.
A fresh container starts with no sign-in, and the CLI exits with code 4. For a runner that does not keep its state, use the MCP server over HTTP with the device sign-in and a refresh token kept in your secret store. The steps are on [Sign in, device codes and sessions](/account/sign-in).
##### Exit codes in scripts
These come from `invideo help exit-codes`. Your script acts on each one.
| Code | Meaning | What the script does |
| --- | --- | --- |
| 0 | ok | Go on |
| 1 | failed (invideo's side, or unknown) | Stop and log the `message` |
| 2 | usage, or a request that cannot be done as asked | Fix the command |
| 3 | conflict: the timeline changed since your read; read it again | Run `invideo edit show` again and rebuild the change |
| 4 | not signed in: run `invideo auth login` | Stop. A person runs `invideo auth login` |
| 5 | a limit: credits or quota | Stop and tell a person |
| 6 | held by moderation: a safety review, not a refusal | Wait, then run `invideo status --wait` again |
| 7 | transient: nothing ran | Run the same command again |
| 8 | needs the person's confirmation | Stop and tell a person |
| 9 | update required: run `invideo update`, then run it again | Run `invideo update`, then run it again |
With `--wait`, a command tries again by itself when nothing ran (exit 7). A retry is the same request, so it never starts anything twice.
##### The confirmation hold
Some jobs stop with the status `needs_confirmation`, or exit 8. Only a person can confirm it, in invideo. A pipeline cannot confirm it. The script stops, logs the URL, and tells someone. When the person has confirmed it, run `invideo status --wait` to pick the job up again.
#### Steps
The script below runs one full job. It checks the sign-in, makes a project and an edit, uploads a file, generates a clip with a cap, places the upload on the timeline, checks the edit, exports, waits for the card, and saves the video. It needs `jq`.
Your agent runs a dry run first. For any call that costs 1 credit or more, it tells you the price and waits for your yes. A call under 1 credit runs without asking, and your agent tells you what it cost. In a pipeline, the person gives that yes once, as the cap.
A person runs the device sign-in above on the machine that runs the jobs. Expected result: `invideo auth status` shows their name.
Price the generation once, by hand, before the first run.
1. Run `invideo gen models --query ""`, the CLI's `find_models`. A good pick is `seedance_2_5.r2v`. Your agent can search for others with `find_models`.
2. Run `invideo gen model --model seedance_2_5.r2v` to read its inputs.
3. Run `invideo gen run` on the same URL as the script, with `--dry-run` and the real inputs, and tell the person the price.
`` is any project of yours, from `invideo project new`. Set `MAX_CREDITS` to the price the person agreed to, from the dry run's `.estimated_credits`. Expected result: the script refuses to start without it.
Run the script below. Expected result: `./out/export.mp4` on disk and the export card's URL in the log.
Look at the frames in `./out/frames` before you ship the video. Expected result: the video looks as planned.
```bash
#!/usr/bin/env bash
# One invideo render from a pipeline. Needs jq and a signed-in CLI.
set -euo pipefail
export INVIDEO_JSON=1
RUN="${RUN_ID:?set RUN_ID, unique to this run}"
MAX_CREDITS="${MAX_CREDITS:?set the agreed cap}"
OUT=./out
stop() {
case "$rc" in
4) echo "Not signed in: a person runs invideo auth login" ;;
5) echo "Out of credits or quota: tell the account owner" ;;
6) echo "Held by moderation: tell the account owner" ;;
8) echo "Needs a person to confirm it in invideo" ;;
9) echo "Run invideo update, then run the job again" ;;
*) echo "invideo failed with exit $rc" ;;
esac >&2
exit "$rc"
}
iv() { invideo "$@" || { rc=$?; stop; }; }
# 1. Signed in?
iv auth status >/dev/null
# 2. A project, and a name in it
PROJECT=$(iv project new --title "Nightly $RUN" | jq -r .url)
iv agent register "$PROJECT" --name ci-render >/dev/null
export INVIDEO_AS=ci-render
# 3. An edit, and the source file in its media bin
EDIT=$(iv edit new "$PROJECT" --name "Nightly cut" | jq -r .url)
iv asset upload "$EDIT" ./media/intro.mp4 --wait >/dev/null
# 4. Generate: read the inputs, price it, then run it with a cap and a key
iv gen model "$PROJECT" --model seedance_2_5.r2v >/dev/null
iv gen run "$EDIT" --model seedance_2_5.r2v --dry-run \
-i prompt=@prompt.txt
GEN=$(iv gen run "$EDIT" --model seedance_2_5.r2v \
--max-credits "$MAX_CREDITS" --idempotency-key "$RUN-gen" \
-i prompt=@prompt.txt | jq -r .url)
iv status "$GEN" --wait >/dev/null
# 5. Place the upload, then check and capture
SHOW=$(iv edit show "$EDIT")
TOKEN=$(jq -r .state_token <<<"$SHOW")
TL=$(jq -r .main_timeline_id <<<"$SHOW")
ASSET=$(iv edit show "$EDIT" --mode collection --collection asset \
| jq -r '.objects[0].id')
jq -n --arg a "$ASSET" \
'[{op:"insert_media",asset:$a,track:"V1",at_f:0,ripple:true}]' \
> ops.json
iv edit change "$EDIT" --timeline "$TL" --token "$TOKEN" \
--operations @ops.json
iv edit check "$EDIT"
iv edit capture "$EDIT" --at 0,1,2 --out "$OUT/frames"
# 6. Export, wait for the card, then save the file
CARD=$(iv edit export "$EDIT" --format video --quality 1080 \
--idempotency-key "$RUN-export" | jq -r '.sequences[0]')
iv status "$CARD" --wait >/dev/null
iv notebook show "$CARD" --out "$OUT" >/dev/null
test -f "$OUT/export.mp4" || { echo "No file saved: $CARD" >&2; exit 1; }
echo "Exported: $CARD"
```
Notes on the script:
- `insert_media` places the picture alone. To place the file with its sound, use `place_media` with `linked_audio`. `invideo edit catalog --ops place_media` lists its fields.
- The generated clip lands in the edit's thread. To place it too, read its sequence URL with `invideo notebook show`, and pass that URL as `asset` in the operation.
- `invideo status` waits for the export and saves nothing. `invideo notebook show` on the card saves the file as `$OUT/export.mp4`, whatever `--file-name` says. The script checks the file is there before it reports success.
- The script reads the card's URL, the state token and the main timeline's id from the JSON answers. Run each command once with `--json` and check the keys before you rely on the script.
- A key only matches in its own project, for a generation, or on its own timeline, for an export. A rerun of the whole script makes a new project, so it pays again. To retry one step, run that command again with the same URL and the same key.
#### If something goes wrong
Most pipeline failures are a lost sign-in, a stale token, or a hold that needs a person.
| 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 4 on the first command | The machine has no sign-in. A person runs `INVIDEO_NO_BROWSER=1 invideo auth login` on it. |
| Exit 3 on `invideo edit change` | Someone edited first. Run `invideo edit show` again for a new token and rebuild the change. |
| Exit 5 | Credits or quota ran out. Stop and tell the account owner. |
| Exit code 6, or `status` is `held` | A safety review, not a refusal. Wait, then run `invideo status --wait` again. Do not resend. If it ends `failed`, change the input the review flagged. |
| Exit 8, or status `needs_confirmation` | Only a person can confirm it, in invideo. Stop, log the URL, and tell someone. |
| Exit 7 | Nothing ran. Run the same command again, or add `--wait`. |
| Exit 9 | Run `invideo update`, then run the job again. |
| `--wait` stops before the job is over | Raise `--timeout`, or run `invideo status --wait` again later. |
| The answer says you acted as a shared name | Register a name with `invideo agent register` and set `INVIDEO_AS`. |
| The script stops with "No file saved" | Read the card with `invideo notebook show`, then run the last line again. |
| The frames in `./out/frames` look wrong | Run the checks in [Check your video before you say it is done](/how-to/check-your-video). |
| Exit 0, but `./out` has no video | `invideo status` saves nothing. Run `invideo notebook show --out ./out`, with the card's URL from `.sequences[0]` of the export answer. |
| Avoid: running `invideo gen run` again to check a job | Instead: `invideo status --wait`. |
| Avoid: a new `--idempotency-key` on a retry | Instead: the same key, so the retry finds the first job. |
| Avoid: asking the person to paste a code or password | Instead: send the link and code from the device sign-in, and wait. |
#### Related
- [Know the cost before you spend](/how-to/cost-before-you-spend)
- [Check your video before you say it is done](/how-to/check-your-video)
- [Sign-in](/account/sign-in)
- [CLI reference](/reference/cli)
- [Exit codes](/reference/exit-codes)
- [Environment variables](/reference/environment)
### Export to Premiere, Resolve or Final Cut
Source: https://docs.invideo.io/how-to/export-to-premiere-resolve-final-cut.md
Your agent exports an invideo timeline as a project file that Premiere Pro, DaVinci Resolve or Final Cut Pro opens, with its media in a folder or a zip.
Your agent turns an invideo timeline into a project file that Premiere Pro, DaVinci Resolve or Final Cut Pro opens. `export` with `format` set to `premiere`, `davinci` or `final_cut` makes it. The project comes with its media in a folder or a zip, so an editor can open it in their app and keep cutting. Exports are free.
Use this when the rough cut happens in invideo, with your agent, and the finish happens in another editing app.
#### Quickest path
Set up your agent first with [invideo for AI agents](/agents). Then give it the timeline URL and this prompt:
```text
Check this timeline, then export it as a Premiere Pro project with its media
in a zip. Give me the download when it is ready.
```
In a chat app, your agent gives you the export card's URL, and you download the files from invideo. In a terminal, your agent saves the files to a folder with `invideo notebook show --out `.
#### How it works
`export` takes a timeline's URL and a `format`. The branch in the URL is what gets exported, so export the branch you approved.
| `format` | What you get |
| --- | --- |
| `premiere` | A project Premiere Pro opens, with its media |
| `davinci` | A project DaVinci Resolve opens, with its media |
| `final_cut` | A project Final Cut Pro opens, with its media |
| `invideo` | An invideo project export |
| `video` | A rendered video file |
For `premiere`, `davinci` and `final_cut`, the option `media_packaging` says how the media comes with the project:
- **`folder`** is the default. The card gives the project file and a zip of the media.
- **`zip`** gives the project file, and a zip that holds the media and a second copy of the project.
- **`none`** sends the project without its media. Use it when the editor has the source files. They point the clips at their own copies in the app.
The export returns its thread's URL at once, and the export card's URL once it appears. The agent reads the card for its status. Once it is `completed`, the card gives the files, with fresh download URLs on every read, and the project's export page. `completed` can arrive before the media zip is packed. When the card says the media is still packing, the agent reads the card again until the zip is in the files list.
Exports are free. They need export permission on the project. In invideo Editor, the same export is **Editable project** in the **Export** window, where DaVinci Resolve is the **OpenTimelineIO** option.
##### Check the project in the app before you deliver
The project opens in the other app as an editable timeline. Some parts of an invideo edit may look different there. Open the project and check these before you deliver:
- Transitions, and custom transitions
- Effects, and custom effects made with `add_custom_effect`
- Text and titles: fonts, styles and position
- Captions
- Color grades
- Audio levels, fades and audio effects
- Keyframed moves, like zooms and fades
- Clip timing at each cut
Compare the project with a frame from invideo at the same time. `capture` gives frames of the timeline as it looks in invideo.
#### Steps
The agent runs these steps for one project export.
The agent runs the checklist in [Check your video before you say it is done](/how-to/check-your-video), with `check_timeline` and `capture`. Expected result: no findings, and frames that look right.
The agent saves a checkpoint with `timeline_history`, or marks the branch final. Expected result: a checkpoint you can name in the hand-off.
The agent calls `export` with the timeline URL, the `format` for the app, `media_packaging`, and an `idempotency_key`. Expected result: a thread URL at once.
The agent follows the card with `status` or `read`, at the time `retry_after_s` gives. Expected result: status `completed`, then the project file and the media zip in the files list.
The agent gives you the download, or saves it with `invideo notebook show --out ` in a terminal. Expected result: a project file and a zip of its media. Then you open it in the app and run the check above.
The same export from a terminal:
```bash
invideo edit check
invideo edit capture --at 0,5,10 --out ./frames
invideo edit show
invideo branch checkpoint --title "to the editor" \
--token
invideo edit export --format premiere --media-packaging zip \
--idempotency-key "handoff-v1" --json
invideo status --wait
invideo notebook show
invideo notebook show --out ./handoff
```
Where each value comes from:
- `` is the URL of the branch you approved, from `invideo edit new` or `invideo branch new`. `` comes from `invideo edit show`.
- `` is `.sequences[0]` of the export answer. `invideo status` waits and saves nothing.
- The first `notebook show` reads the card. Run it again until the media zip is in its files, then save with `--out`.
For DaVinci Resolve use `--format davinci`, and for Final Cut Pro use `--format final_cut`. For a timeline inside the edit, add `--timeline` with its id from `invideo edit show`.
The key MCP call is `export`. `` is the URL of the branch you approved, from `new_timeline` or `timeline_history`.
```json
{
"url": "",
"format": "davinci",
"options": { "media_packaging": "folder" },
"idempotency_key": "handoff-v1"
}
```
#### If something goes wrong
Most export problems come from the wrong branch, a missing permission, or a retry without a key.
| Symptom | Fix |
| --- | --- |
| The export is refused for permission | The project needs export permission for you. Ask the person who shares the project. |
| The project shows an earlier state of the edit | The URL named another branch. Export the URL of the branch you approved. |
| The export card returns `failed` | Read the card for why. Try once more. If it fails again, file `report_bug` with the URL and what you sent. |
| A call times out or returns `completion_unknown` | Retry with the same `idempotency_key`. It finds the first export. |
| CLI exit 7 | Nothing ran. Run the same command again. |
| The clips have no media in the app | With `none`, no media came with the project. Point the clips at your copies, or export again with `folder` or `zip`. |
| The card says `completed` but lists only the project file | The media zip is still packing. Read the card again in a few seconds until the zip is listed. |
| The checks before export find a problem | Fix the edit first, then check again. |
| You want to keep editing after the hand-off | The agent works on a new branch, so the checkpoint you sent stays as it was. |
| CLI exit 0, but the folder is empty | `invideo status` saves nothing. Run `invideo notebook show --out `. |
| A download link stops working | Read the card again for a fresh URL. |
| Avoid: calling `export` again to check on one | Instead: `status` or `read` the card's URL. |
| Avoid: delivering without opening the project | Instead: open it in the app and run the check above. |
#### Related
- [Check your video before you say it is done](/how-to/check-your-video)
- [Know the cost before you spend](/how-to/cost-before-you-spend)
- [Run invideo in CI and on servers](/how-to/ci-and-servers)
- [Export video, audio or a still from invideo Editor](/projects-and-exports/how-to-export-video-audio-or-a-png-still-from-invideo-editor)
- [Security and data](/account/security-and-data)
### Is there an API?
Source: https://docs.invideo.io/api.md
There are no invideo API keys, by design. Use invideo MCP, the invideo CLI, or device sign-in for servers and scripts.
There are no API keys, by design. Every call to invideo, whether it generates a shot with a model or cuts a timeline, runs as a signed-in person, in that person's workspace. You reach invideo through three routes: invideo MCP for AI apps and agents, the invideo CLI for shells and scripts, and device sign-in for servers with no browser.
```bash
curl -fsSL https://agents.invideo.io | sh
invideo auth login
invideo project list --json
```
#### Why there are no API keys
A key is a secret that works for anyone who holds it. invideo uses a person's sign-in instead, for these reasons.
- **Every call has an owner.** Each edit, generation and export is made by a named person, in a workspace they chose. People in the project see who did what.
- **Credits come from the right place.** Paid calls spend the credits of the workspace the person works in. A leaked key cannot spend them.
- **Access matches the person.** Your agent can open only what you can open.
- **Nothing to rotate in code.** There is no key to paste into a config file, a repo or a chat.
If a tool asks you for an invideo API key, it is not one of invideo's routes. Use one of the routes below.
#### Route 1: invideo MCP
invideo MCP is a remote MCP server at `https://mcp.invideo.io/mcp`. MCP (Model Context Protocol) is a standard way for AI apps to call outside tools. It is the route for clients without a shell: Claude on the web, ChatGPT, the Gemini app, VS Code, Gemini CLI, and any client that supports remote MCP servers over Streamable HTTP. Terminal agents such as Claude Code, Codex, Cursor and Antigravity use the CLI instead.
```json
{
"mcpServers": {
"invideo": { "url": "https://mcp.invideo.io/mcp" }
}
}
```
The client asks you to sign in to invideo in your browser when it connects. Then your agent calls tools such as `list_projects`, `edit_timeline`, `generate` and `export`. See [invideo for AI agents](/agents) for every client, and [MCP tools](/reference/mcp-tools) for the full list.
The server is plain JSON-RPC over HTTP POST. An agent with no MCP client can call it with `curl`, with an access token from device sign-in. [Sign in, device codes and sessions](/account/sign-in) has the device flow over HTTP.
#### Route 2: the invideo CLI
The invideo CLI is one program for macOS, Linux and Windows. It does the same work as MCP from a shell, and saves files to disk. Use it for scripts and coding agents that run commands.
```bash
invideo project new --title "Launch teaser"
invideo asset upload ./shots/clip.mp4
invideo edit new --name "Teaser v1"
invideo edit export --format video --quality 1080 --json
invideo status --wait
invideo notebook show --out ./out
```
`` comes from `invideo project new`, and `` from `invideo edit new`. `` is `.sequences[0]` in the export's answer. `invideo status` waits and saves nothing. `invideo notebook show` with `--out` saves the video as `export.mp4`.
Pass `--json` on every command in a script. Each answer is one JSON object, with `next` saying what to run after it. Use one route per session. Do not mix MCP and CLI calls in the same session. See [CLI commands and flags](/reference/cli).
#### Route 3: device sign-in for servers
On a machine with no browser, such as a CI runner, a container or a cloud agent, sign in with a device code. A person approves once, on a phone or laptop.
```bash
INVIDEO_NO_BROWSER=1 invideo auth login
```
The command prints a link and a code. The person opens the link, enters the code, chooses the workspaces and approves. The sign-in then lasts on that machine. This code signs in the CLI only. Over HTTP without the CLI, the agent runs its own device flow, gets an access token and a refresh token, and refreshes the access token when it expires.
Your agent never asks the person for a password or for the code back. It sends the link and the code in one message and waits. See [Sign in, device codes and sessions](/account/sign-in) and [Run invideo in CI and on servers](/how-to/ci-and-servers).
#### What each route is good for
Pick the route that fits where your code or agent runs.
| You have | Use |
| --- | --- |
| A chat app such as Claude or ChatGPT | invideo MCP, as the ChatGPT plugin or a Claude connector |
| A coding agent such as Claude Code, Codex or Cursor | The CLI, through the invideo plugin |
| A shell script or a batch job on your laptop | The CLI, signed in with `invideo auth login` |
| A CI job, a container or a cloud agent | The CLI or MCP over HTTP, with device sign-in |
| Your own app that serves other users | Each user signs in with their own invideo account |
#### Cost
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. The same rules hold on every route. See [Credits and what they cost](/account/credits).
#### If something goes wrong
Most problems come from looking for a key that does not exist.
| Symptom | Fix |
| --- | --- |
| A tool or guide asks for an invideo API key. | Avoid keys. Instead, sign in with the browser or a device code. |
| A call returns a sign-in challenge. | The client is not signed in. Finish the sign-in, then retry the call. |
| The CLI exits with code 4. | Run `invideo auth login`, or `INVIDEO_NO_BROWSER=1 invideo auth login`. |
| An HTTP call returns 401. | The access token expired. Refresh it with the refresh token. |
| The agent mixes MCP tools and CLI commands. | Avoid this. Instead, pick one route for the whole session. |
| A server job needs a person each time. | Keep the refresh token in a secret store, so one approval lasts. |
#### Related
- [invideo for AI agents](/agents)
- [Run invideo in CI and on servers](/how-to/ci-and-servers)
- [Sign in, device codes and sessions](/account/sign-in)
- [CLI commands and flags](/reference/cli)
- [MCP tools](/reference/mcp-tools)
- [Credits and what they cost](/account/credits)
## Connect your agent
### Claude Code
Source: https://docs.invideo.io/start/claude-code.md
Install the invideo plugin in Claude Code, sign in once in the browser, and have your agent generate video, voice and music and edit it on a real timeline you can watch live.
Claude Code gets the invideo skills and the CLI, opens your projects by URL, generates the shots, voices and music a video lacks, and edits it all on a timeline you watch live in the browser. In Claude Code, invideo runs through the CLI, not through an MCP server.
#### Set up
Install the invideo plugin. Run these in your terminal, or ask Claude Code to run them for you.
```bash
claude plugin marketplace add invideoio/skills
claude plugin install invideo@invideo
```
The plugin installs the invideo skills: how to connect, the work loop, the cost rule, and the steps for each kind of video. The skills are CLI-first. On your first video request, Claude Code installs the invideo CLI, a single binary with no sudo, and opens a browser tab for you to sign in.
Run the two lines above. You see invideo in the plugin list.
Give Claude Code a video job. It installs the CLI with the install script and runs `invideo auth login`. A sign-in tab opens in your browser.
Sign in and choose the workspaces your agent may use. Come back to Claude Code. It carries on with your job, and `invideo auth status` shows your name.
You can also install the CLI yourself, before the first job.
```bash
curl -fsSL https://agents.invideo.io | sh
invideo --version
invideo auth login
```
On Windows, install with the PowerShell line on [invideo for AI agents](/agents).
#### Sign in
With the CLI, `invideo auth login` opens the browser and waits for you to approve. Claude Code runs it in the background or with a long timeout and tells you a tab opened.
Where no browser can open, on ssh, in a container or on a cloud machine, the agent uses a device code.
```bash
INVIDEO_NO_BROWSER=1 invideo auth login
```
It prints a link and a code. Open the link on another device and enter the code. Your agent never asks you to send the code back, and never asks for a password.
#### First prompt
Start with a prompt that proves the connection works.
```text
Use invideo to show me my projects.
```
Then give it a real job. Name the folder with your files.
```text
Make a new invideo project called "Launch teaser". Upload the clips in
./shots, cut a short teaser on a branch, add a title card and a
short generated opening shot. Tell me the price before you spend,
and show me the frames before you export.
```
Claude Code replies with project and timeline URLs. Open them to watch the edit land.
Your agent runs a dry run first. For any call that costs 1 credit or more, it tells you the price and waits for your yes. A call under 1 credit runs without asking, and your agent tells you what it cost.
#### If something goes wrong
Most problems come from the install path or the sign-in.
| Symptom | Fix |
| --- | --- |
| `claude plugin install` says the plugin is not found | Run `claude plugin marketplace add invideoio/skills` first, then install again. |
| The agent says `invideo` is not found after the install | The install script wrote `~/.local/bin/invideo`. Run it by its full path, or add that folder to `PATH`. |
| CLI exit 4, not signed in | Run `invideo auth login`, or the device code line above. |
| The sign-in tab never opened | Run `INVIDEO_NO_BROWSER=1 invideo auth login` and open the link it prints. |
| The agent cannot see a workspace | Run `invideo auth login` again and choose that workspace in the browser. |
| Avoid: the agent asks for your code or password | Instead: you approve in the browser, and the agent waits. |
| Avoid: the agent adds invideo as an MCP server in Claude Code | Instead: use the CLI. The skills teach it, and the plugin carries no MCP config. |
If invideo still does not appear, check the plugin with `claude plugin list`.
#### Next
- [invideo for AI agents](/agents), the rules your agent follows
- [Check your video before you say it is done](/how-to/check-your-video)
- [Know the cost before you spend](/how-to/cost-before-you-spend)
- [Claude Code as your AI film editor](/how-to/film/claude-code-as-film-editor)
- [CLI commands and flags](/reference/cli)
### Cursor
Source: https://docs.invideo.io/start/cursor.md
Add the invideo skills to Cursor, sign in once in the browser, and have Cursor's agent generate video, voice and music and edit it on a real timeline you can watch live.
Your agent in Cursor gets the invideo skills and the CLI, opens your projects by URL, generates the shots, voices and music a video lacks, and edits it all on a timeline you watch live in the browser. In Cursor, invideo runs through the CLI first.
#### Set up
Add the invideo skills to your project. The agent then drives invideo through the CLI in its terminal, like Claude Code and Codex do.
```bash
npx skills add invideoio/skills
```
The skills hold how to connect, the work loop, the cost rule, and the steps for each kind of video. On your first video request the agent installs the invideo CLI, a single binary with no sudo, and opens a browser tab for you to sign in.
Run the line above in your project. You see the invideo skills in your project's skills folder.
Give the agent a video job. It installs the CLI with the install script and runs `invideo auth login`. A sign-in tab opens in your browser.
Sign in and choose the workspaces the agent may use. Come back to Cursor. It carries on, and `invideo auth status` shows your name.
You can also install the CLI yourself, before the first job.
```bash
curl -fsSL https://agents.invideo.io | sh
invideo --version
invideo auth login
```
On Windows, install with the PowerShell line on [invideo for AI agents](/agents).
##### The Cursor Marketplace plugin, for MCP
The invideo plugin on the Cursor Marketplace is the MCP route. Click **Customize**, search `invideo`, then click **Install**. The plugin adds the invideo MCP server to Cursor and nothing else: no skills, no CLI. Use it when you want the tools in the chat without a terminal. Pick the CLI or MCP for a session, not both. Over MCP the agent has no skills, so it reads the rules from [invideo for AI agents](/agents) and from `get_help`.
#### Sign in
With the CLI, `invideo auth login` opens the browser and waits for you to approve. Sign in and choose the workspaces the agent may use.
Where no browser can open, on ssh or in a container, the agent uses a device code.
```bash
INVIDEO_NO_BROWSER=1 invideo auth login
```
It prints a link and a code. Open the link on another device and enter the code. Your agent never asks you to send the code back, and never asks for a password.
#### First prompt
Start with a prompt that proves the connection works.
```text
Use invideo to show me my projects.
```
Then give it a real job. Name the folder with your files.
```text
Make a new invideo project called "Release notes video". Upload the
screen recordings in ./recordings, cut them on a branch, add a title
for each one and a short generated opening shot. Tell me the price
before you spend, and show me the frames before you export.
```
The agent replies with project and timeline URLs. Open them to watch the edit land.
Your agent runs a dry run first. For any call that costs 1 credit or more, it tells you the price and waits for your yes. A call under 1 credit runs without asking, and your agent tells you what it cost.
#### If something goes wrong
Most problems come from the install path or the sign-in.
| Symptom | Fix |
| --- | --- |
| The Marketplace plugin is installed, but the agent has no skills | That plugin carries only the MCP server. Add the skills with `npx skills add invideoio/skills` for the CLI path. |
| The agent says `invideo` is not found after the install | The install script wrote `~/.local/bin/invideo`. Run it by its full path, or add that folder to `PATH`. |
| CLI exit 4, not signed in | Run `invideo auth login`, or the device code line above. |
| The sign-in tab never opened | Run `INVIDEO_NO_BROWSER=1 invideo auth login` and open the link it prints. |
| The agent cannot see a workspace | Run `invideo auth login` again and choose that workspace in the browser. |
| Avoid: the agent asks for your code or password | Instead: you approve in the browser, and the agent waits. |
| Avoid: the agent uses the CLI and the MCP plugin together | Instead: pick one for the session. |
#### Next
- [invideo for AI agents](/agents), the rules your agent follows
- [Check your video before you say it is done](/how-to/check-your-video)
- [Know the cost before you spend](/how-to/cost-before-you-spend)
- [CLI commands and flags](/reference/cli)
### Codex
Source: https://docs.invideo.io/start/codex.md
Install the invideo plugin in Codex, sign in once in the browser, and have Codex generate video, voice and music and edit it on a real timeline you can watch live.
Codex gets the invideo skills and the CLI, opens your projects by URL, generates the shots, voices and music a video lacks, and edits it all on a timeline you watch live in the browser. In Codex, invideo runs through the CLI, not through an MCP server.
#### Set up
Install the invideo plugin. Run these in your terminal, or ask Codex to run them for you.
```bash
codex plugin marketplace add invideoio/skills
codex plugin add invideo@invideo
```
The plugin installs the invideo skills: how to connect, the work loop, the cost rule, and the steps for each kind of video. The skills are CLI-first. On your first video request, Codex installs the invideo CLI, a single binary with no sudo, and opens a browser tab for you to sign in.
Run the two lines above. You see invideo in `codex plugin list`.
Give Codex a video job. It installs the CLI with the install script and runs `invideo auth login`. A sign-in tab opens in your browser.
Sign in and choose the workspaces your agent may use. Come back to Codex. It carries on with your job, and `invideo auth status` shows your name.
You can also install the CLI yourself, before the first job.
```bash
curl -fsSL https://agents.invideo.io | sh
invideo --version
invideo auth login
```
On Windows, install with the PowerShell line on [invideo for AI agents](/agents).
#### Sign in
With the CLI, `invideo auth login` opens the browser and waits for you to approve. Codex runs it in the background or with a long timeout and tells you a tab opened.
Where no browser can open, on ssh, in a container or on a cloud machine, the agent uses a device code.
```bash
INVIDEO_NO_BROWSER=1 invideo auth login
```
It prints a link and a code. Open the link on another device and enter the code. Your agent never asks you to send the code back, and never asks for a password.
#### First prompt
Start with a prompt that proves the connection works.
```text
Use invideo to show me my projects.
```
Then give it a real job. Name the folder with your files.
```text
Make a new invideo project called "Product demo". Upload the clips in
./footage, cut them into one timeline on a branch, add captions and a
short generated opening shot. Tell me the price before you spend,
and show me the frames before you export.
```
Codex replies with project and timeline URLs. Open them to watch the edit land.
Your agent runs a dry run first. For any call that costs 1 credit or more, it tells you the price and waits for your yes. A call under 1 credit runs without asking, and your agent tells you what it cost.
#### If something goes wrong
Most problems come from the install path or the sign-in.
| Symptom | Fix |
| --- | --- |
| `codex plugin add` says the plugin is not found | Run `codex plugin marketplace add invideoio/skills` first, then add again. |
| The agent says `invideo` is not found after the install | The install script wrote `~/.local/bin/invideo`. Run it by its full path, or add that folder to `PATH`. |
| CLI exit 4, not signed in | Run `invideo auth login`, or the device code line above. |
| The sign-in tab never opened | Run `INVIDEO_NO_BROWSER=1 invideo auth login` and open the link it prints. |
| The agent cannot see a workspace | Run `invideo auth login` again and choose that workspace in the browser. |
| Avoid: the agent asks for your code or password | Instead: you approve in the browser, and the agent waits. |
| Avoid: the agent adds invideo as an MCP server in Codex | Instead: use the CLI. The skills teach it, and the plugin carries no MCP config. |
#### Next
- [invideo for AI agents](/agents), the rules your agent follows
- [Check your video before you say it is done](/how-to/check-your-video)
- [Know the cost before you spend](/how-to/cost-before-you-spend)
- [Run invideo in CI and on servers](/how-to/ci-and-servers)
- [CLI commands and flags](/reference/cli)
### ChatGPT
Source: https://docs.invideo.io/start/chatgpt.md
Install the official invideo plugin in ChatGPT, sign in once, and ask ChatGPT to generate video, voice and music and edit it on a real timeline.
ChatGPT gets the invideo tools through the official invideo plugin, opens your projects by URL, generates the shots, voices and music a video lacks, and edits it all on a timeline you watch live in the browser.
#### Set up
invideo is an official ChatGPT plugin. Install it once from ChatGPT's plugin list.
In ChatGPT, open **Customize** in the sidebar, then **Plugins**. invideo is listed under **Creativity** as "Edit videos & motion graphics". You can also search for `invideo` in the plugin search box.
Click the plus next to invideo. It appears under **Installed** in the left column.
Ask ChatGPT for a video. The first call opens the invideo sign-in in your browser. Sign in and choose the workspaces ChatGPT may use. ChatGPT retries the call after you approve.
ChatGPT gets the plugin only. It gets no invideo skills, so ChatGPT learns the rules from the server's own guide, `get_help`. If you want to work in Codex instead, use the Codex plugin and the CLI: see [Set up invideo in Codex](/start/codex).
#### Sign in
ChatGPT runs the sign-in in the browser. When a call needs your account, ChatGPT shows the invideo sign-in and retries the call after you approve. You never paste a code or a password into the chat.
ChatGPT agents that run in the cloud cannot use the plugin. They call the server over HTTP with a device code instead. The steps for that are on [Sign in, device codes and sessions](/account/sign-in).
#### First prompt
Start with a prompt that proves the connection works.
```text
Use invideo to show me my projects.
```
Then give it a real job. In ChatGPT, your files go into the invideo project first. Open the project in invideo and drag them into the Media panel, then name them in the chat.
```text
In my invideo project "Summer trip", use the clips I uploaded to make
a short vertical video with music made to fit. Generate a short
opening shot. Tell me the price before you generate anything, and show
me frames before you export.
```
ChatGPT replies with project and timeline URLs. Open them to watch the edit land, and change anything by hand.
#### Cost
A ChatGPT plan does not include invideo. You sign in with your own invideo account, and its plan pays for the credits.
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.
Your agent runs a dry run first. For any call that costs 1 credit or more, it tells you the price and waits for your yes. A call under 1 credit runs without asking, and your agent tells you what it cost.
See [Credits and what they cost](/account/credits).
#### If something goes wrong
Most problems come from the plugin not being installed or the sign-in not being finished.
| Symptom | Fix |
| --- | --- |
| invideo is not in the plugin list | Search for `invideo` under **Customize**, then **Plugins**. In a managed workspace, your admin controls which plugins you can add; ask them. |
| ChatGPT does not use invideo | Check that invideo is under **Installed**, then name invideo in the prompt: "Use invideo to ...". |
| ChatGPT asks you to sign in again | Finish the invideo sign-in in the browser. ChatGPT retries the call. |
| ChatGPT cannot see a workspace | Sign in again from the plugin and choose that workspace. |
| A ChatGPT agent in the cloud cannot connect | Cloud agents cannot use the plugin. They use the device code over HTTP. |
| Avoid: ChatGPT asks for your code or password | Instead: you sign in through the invideo page that ChatGPT opens. |
| Avoid: ChatGPT says done without a link | Instead: ask it to `capture` frames and give you the timeline URL. |
#### Next
- [invideo for AI agents](/agents), the rules ChatGPT follows
- [Make a video with ChatGPT, Claude or Gemini](/how-to/personal/with-chatgpt-claude-gemini)
- [Make a video with invideo without a terminal](/start/no-terminal)
- [Know the cost before you spend](/how-to/cost-before-you-spend)
### Gemini
Source: https://docs.invideo.io/start/gemini.md
Connect invideo to the Gemini app as a custom app, or to Gemini CLI as an MCP server, and have Gemini generate video, voice and music and edit it on a real timeline.
Gemini gets the invideo tools, opens your projects by URL, generates the shots, voices and music a video lacks, and edits it all on a timeline you watch live in the browser. The Gemini app takes invideo as a custom app. Gemini CLI takes it as an MCP server.
#### Use invideo in the Gemini app
In the Gemini app, invideo is a custom connected app. You add it once in the web app, and it works in the web app and the mobile app. Gemini gets the MCP server only, with no invideo skills, so it learns the rules from the server's own guide, `get_help`.
Google sets the rules for custom apps. You must be 18 or over, in the US, signed in with a personal Google Account, with Keep Activity on. Work and school accounts cannot add custom apps, and custom apps work in English only. Check Google's custom apps help page for its rules.
On your computer, go to gemini.google.com. At the bottom, click **Settings**, then **Connected Apps**. If you do not see it, click **Personal Intelligence** first.
Under **Custom apps**, click **Add a custom app**, and enter the MCP server URL: `https://mcp.invideo.io/mcp`. Click **Next**. invideo supports dynamic client registration, so you enter no credentials.
Follow the on-screen steps. Your browser opens on the invideo sign-in. Sign in and choose the workspaces Gemini may use. You see invideo under Custom apps.
In a chat, type `@` and select invideo, then ask for a video. Gemini asks you to confirm each action that changes something.
To disconnect, turn invideo off under **Custom apps**, or open **More details** and choose **Disconnect**. **Remove app** unlinks the server from your Google Account.
#### Use invideo in Gemini CLI
Run this in your terminal, or ask Gemini CLI to run it for you.
```bash
gemini mcp add --transport http --scope user invideo \
https://mcp.invideo.io/mcp
```
`--scope user` adds invideo to `~/.gemini/settings.json`, so it works in every folder. Without it, Gemini CLI adds the server to the current project only.
Gemini gets the MCP server only. It gets no invideo skills, so Gemini learns the rules from the server's own guide, `get_help`.
You can also add the server to `~/.gemini/settings.json` by hand.
```json
{
"mcpServers": {
"invideo": { "httpUrl": "https://mcp.invideo.io/mcp" }
}
}
```
Run the `gemini mcp add` line, or save the config above. If the file has other servers, add the `invideo` entry to the same `mcpServers` object.
Exit Gemini CLI and start it again so it loads invideo.
Run `gemini mcp list` in your terminal. You see invideo with its connection status.
##### Use the CLI instead
Gemini CLI can also drive invideo through the invideo CLI in its shell. Use it when you want files on disk, work from scripts, or run on a machine with no browser. Pick MCP or the CLI for a session, not both.
```bash
curl -fsSL https://agents.invideo.io | sh
invideo --version
invideo auth login
```
On ssh, in a container or on a cloud machine, sign in the CLI with a device code instead.
```bash
INVIDEO_NO_BROWSER=1 invideo auth login
```
It prints a link and a code. Open the link on another device and enter the code. This signs in the invideo CLI only, not the MCP server. If a CLI command exits with code 4, it is not signed in: run `invideo auth login` again.
#### Sign in
Gemini CLI runs the browser sign-in when invideo asks for it. If it does not, run this in Gemini CLI:
```text
/mcp auth invideo
```
Your browser opens on the invideo sign-in. Sign in and choose the workspaces Gemini may use. Run the same command again if the sign-in lapses. Gemini never asks you to send a code back, and never asks for a password.
Signing in the invideo CLI does not sign in this MCP connection. On a machine with no browser, use the CLI path in "Use the CLI instead" for that session. An MCP client that runs its own device flow follows the steps on [Sign in, device codes and sessions](/account/sign-in).
#### First prompt
Start with a prompt that proves the connection works.
```text
Use invideo to show me my projects.
```
Then give it a real job. Name the folder with your files.
```text
Make a new invideo project called "Recipe reel". Upload the clips in
./cooking, cut a vertical reel on a branch with captions, and add a
short generated opening shot. Tell me the price before you spend,
and show me the frames before you export.
```
Gemini replies with project and timeline URLs. Open them to watch the edit land.
Your agent runs a dry run first. For any call that costs 1 credit or more, it tells you the price and waits for your yes. A call under 1 credit runs without asking, and your agent tells you what it cost.
#### If something goes wrong
Most problems come from Google's rules for custom apps, the scope, the restart or the sign-in.
| Symptom | Fix |
| --- | --- |
| Gemini app: there is no Connected Apps or Custom apps section | Your account does not meet Google's rules for custom apps: a personal account, 18 or over, in the US, with Keep Activity on. |
| Gemini app: Gemini does not use invideo for a prompt | Type `@` and select invideo at the start of the prompt. |
| Gemini CLI: the invideo tools do not appear | Exit Gemini CLI and start it again. |
| invideo works in one folder only | You added it without `--scope user`. Add it again with that flag. |
| invideo asks for authentication | Run `/mcp auth invideo` and sign in in the browser. |
| Gemini CLI with no browser cannot finish `/mcp auth` | Switch to the CLI path for the session and sign in with a device code. |
| Gemini cannot see a workspace | Run `/mcp auth invideo` and choose that workspace. |
| Avoid: Gemini asks for your code or password | Instead: you approve in the browser, and the agent waits. |
| Avoid: Gemini uses MCP and the CLI together | Instead: pick one for the session. |
#### Next
- [invideo for AI agents](/agents), the rules your agent follows
- [Make a video with ChatGPT, Claude or Gemini](/how-to/personal/with-chatgpt-claude-gemini)
- [Check your video before you say it is done](/how-to/check-your-video)
- [Know the cost before you spend](/how-to/cost-before-you-spend)
### Antigravity
Source: https://docs.invideo.io/start/antigravity.md
Install the invideo plugin in the Antigravity CLI or IDE, sign in once in the browser, and have the agent generate video, voice and music and edit it on a real timeline you can watch live.
Your agent in Antigravity gets the invideo skills and the CLI, opens your projects by URL, generates the shots, voices and music a video lacks, and edits it all on a timeline you watch live in the browser. The same plugin works in the Antigravity CLI, `agy`, and in the Antigravity IDE. In Antigravity, invideo runs through the CLI, not through an MCP server.
#### Set up
Install the invideo plugin from its GitHub URL. Run this in your terminal, or ask the agent to run it for you.
```bash
agy plugin install https://github.com/invideoio/skills
```
The plugin installs the invideo skills: how to connect, the work loop, the cost rule, and the steps for each kind of video. Antigravity converts the plugin on install and lists its skills. The skills are CLI-first. On your first video request, the agent installs the invideo CLI, a single binary with no sudo, and opens a browser tab for you to sign in.
Run the line above. You see `invideo` marked ok with its skills processed. `agy plugin list` shows it as enabled.
Give the agent a video job, or type `/invideo` to open the primary skill. The agent installs the CLI with the install script and runs `invideo auth login`. A sign-in tab opens in your browser.
Sign in and choose the workspaces the agent may use. Come back to Antigravity. It carries on with your job, and `invideo auth status` shows your name.
You can also install the CLI yourself, before the first job.
```bash
curl -fsSL https://agents.invideo.io | sh
invideo --version
invideo auth login
```
On Windows, install with the PowerShell line on [invideo for AI agents](/agents).
If the Antigravity CLI is not on this machine, Google's install line is the same shape, and the binary lands in the same folder.
```bash
curl -fsSL https://antigravity.google/cli/install.sh | bash
agy --version
```
#### Sign in
With the CLI, `invideo auth login` opens the browser and waits for you to approve. The agent runs it in the background or with a long timeout and tells you a tab opened.
Where no browser can open, on ssh, in a container or on a cloud machine, the agent uses a device code.
```bash
INVIDEO_NO_BROWSER=1 invideo auth login
```
It prints a link and a code. Open the link on another device and enter the code. Your agent never asks you to send the code back, and never asks for a password.
#### First prompt
Start with a prompt that proves the connection works.
```text
Use invideo to show me my projects.
```
Then give it a real job. Name the folder with your files.
```text
Make a new invideo project called "Sprint demo". Upload the screen
recordings in ./demo, cut them into one timeline on a branch, add a
title for each one, and generate a short opening shot and a music
bed. Tell me the price before you spend, and show me the frames
before you export.
```
The agent replies with project and timeline URLs. Open them to watch the edit land.
Your agent runs a dry run first. For any call that costs 1 credit or more, it tells you the price and waits for your yes. A call under 1 credit runs without asking, and your agent tells you what it cost.
#### If something goes wrong
Most problems come from the install path or the sign-in.
| Symptom | Fix |
| --- | --- |
| `agy plugin install` says the target must be a directory | You passed `invideoio/skills`. Pass the full URL, `https://github.com/invideoio/skills`, or a local clone's path. |
| `agy plugin install invideo@invideo` says unknown marketplace | invideo is not in Antigravity's marketplace. Install from the GitHub URL. |
| The agent says `invideo` is not found after the install | The install script wrote `~/.local/bin/invideo`. Run it by its full path, or add that folder to `PATH`. |
| `agy` is not found after Google's install script | The script prints where it put the binary. Add that folder to `PATH`, or run `agy install`, which sets the path for you. |
| CLI exit 4, not signed in | Run `invideo auth login`, or the device code line above. |
| The sign-in tab never opened | Run `INVIDEO_NO_BROWSER=1 invideo auth login` and open the link it prints. |
| The agent cannot see a workspace | Run `invideo auth login` again and choose that workspace in the browser. |
| Avoid: the agent asks for your code or password | Instead: you approve in the browser, and the agent waits. |
| Avoid: the agent adds invideo as an MCP server in Antigravity | Instead: use the CLI. The skills teach it, and the plugin carries no MCP config. |
#### Next
- [invideo for AI agents](/agents), the rules your agent follows
- [Check your video before you say it is done](/how-to/check-your-video)
- [Know the cost before you spend](/how-to/cost-before-you-spend)
- [Run invideo in CI and on servers](/how-to/ci-and-servers)
- [CLI commands and flags](/reference/cli)
### Without a terminal
Source: https://docs.invideo.io/start/no-terminal.md
Generate and edit a video with Agent Two in the invideo app, or with Claude, ChatGPT or the Gemini app in the browser. No install, no commands.
You can make a video with invideo without a terminal: generate the shots, voices and music it needs, cut them with your own footage, and export. Use Agent Two inside the invideo app, or connect invideo to Claude, ChatGPT or the Gemini app in the browser.
In ChatGPT, install the official invideo plugin. For Claude or the Gemini app, add invideo as a connector or custom app with this name and URL. Agent Two needs no setup.
```text
Name: invideo
MCP server URL: https://mcp.invideo.io/mcp
```
#### Pick a path
Every path works on the same invideo projects. You can start in one and continue in another.
| Path | Where you ask | Set up |
| --- | --- | --- |
| Agent Two | The agent panel in the invideo app | None. Sign in at invideo.io. |
| Claude | A chat on claude.ai or the Claude desktop app | Add invideo as a connector once. |
| ChatGPT | A chat on chatgpt.com | Install the official invideo plugin once, from Customize, then Plugins. |
| Gemini app | A chat on gemini.google.com or the Gemini mobile app | Add invideo as a custom app once, in the web app. Google limits who can. |
#### Agent Two in the invideo app
Agent Two works inside invideo, next to your timeline. Nothing to install.
Sign in at invideo.io. Open a project, or create a blank timeline.
Open the Media panel and select Upload, or drag your files into the panel. You see each file in the panel when its upload ends.
Open the agent panel on the left and describe the video. Say the length, the shape, which files to use, and what must stay the same.
Watch the result in the Monitor and on the timeline. Change anything by hand, or give Agent Two a short follow-up.
#### Claude, no install
Claude on the web or the desktop app uses invideo as a connector.
In Claude, open **Customize**, then **Connectors**, and click **Add**. Choose the custom web connector. Name it `invideo`. Use the URL `https://mcp.invideo.io/mcp`.
Select Connect. Sign in to invideo in the browser and choose your workspaces.
In a conversation, open the + menu, choose Connectors, and turn on invideo.
So Claude can upload the files you attach to a chat, open **Settings**, then **Capabilities**, and add m.invideo.io under **Allowed websites**. You do this once. You see m.invideo.io in the list. The full steps are on [How do I let Claude upload my files to invideo?](/ai-agents/how-do-i-let-claude-upload-my-files-to-invideo).
In a Claude organization, an owner adds invideo to the organization's connectors first.
#### ChatGPT, no install
ChatGPT has an official invideo plugin. The full steps are on [Use invideo in ChatGPT](/start/chatgpt). In short: open **Customize**, then **Plugins**, add invideo from the Creativity list, then ask for a video and sign in when ChatGPT opens the invideo page.
#### Gemini app, no install
The Gemini app uses invideo as a custom app. The full steps and Google's rules for who can add one are on [Use invideo in Gemini](/start/gemini). In short: in the Gemini web app, open **Settings**, then **Connected Apps**, add a custom app with the URL `https://mcp.invideo.io/mcp`, sign in, then type `@` and select invideo in a chat.
#### Continue an existing project
To work on a project you already have, open it in invideo Editor, copy the URL from the browser's address bar, and paste it into the chat. Ask your agent to open it and list its timelines, then say which one to change. The URL is how every tool in invideo finds a project, so the agent needs no other id.
#### Where your files go
Put your files in the invideo project, then name them in the chat.
- **In the invideo app**, drag files into the Media panel.
- **In Claude, ChatGPT or the Gemini app**, open the project in invideo and drag the files into its Media panel. Then tell the chat which files to use. You can also attach a file to the chat, and the agent brings it into the project. In Claude, this needs m.invideo.io allowed once. See [How do I let Claude upload my files to invideo?](/ai-agents/how-do-i-let-claude-upload-my-files-to-invideo).
Use folder and file names in your request, like "use product.mp4 first". The agent finds the files by name.
#### How to ask
Describe the result, not the clicks. Say the length, the shape (9:16 vertical, 16:9 wide, 1:1 square), the files, the music mood, and the text to show.
```text
Make a short vertical birthday video for Maya from the photos in
this project. Open on the beach photo, add soft music made for it, a
voice that says "Happy birthday, Maya" and the same words on screen
at the end. Tell me the price before you generate anything.
```
```text
Shorten this timeline and keep the speaker's lines in order. Add
captions. Show me frames from the start, middle and end.
```
The agent works on a branch, so your main cut stays as it was until you accept the change.
#### See the cost
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.
Your agent runs a dry run first. For any call that costs 1 credit or more, it tells you the price and waits for your yes. A call under 1 credit runs without asking, and your agent tells you what it cost.
Agent Two's work and the media it makes use credits. An outside agent edits for free and pays only for generation, transcripts and paid stock.
Here is where you see the cost on each path.
- **In the invideo app**, select the three dots on an agent message to see what that reply used. For the whole project, select the three dots at the top right and choose Project Usage. The permission toggle in the chat box can make Agent Two ask before every generation.
- **In Claude, ChatGPT or the Gemini app**, the agent tells you the price of each paid call in the chat.
See [Credits and what they cost](/account/credits) for prices.
#### If something goes wrong
Most problems come from invideo not being on in the chat.
| Symptom | Fix |
| --- | --- |
| Claude does not use invideo | Open the + menu, choose Connectors, and turn on invideo. |
| Claude says it cannot upload your files to invideo | Allow m.invideo.io in Claude under **Settings**, then **Capabilities**, then **Allowed websites**, and start a new chat. See [How do I let Claude upload my files to invideo?](/ai-agents/how-do-i-let-claude-upload-my-files-to-invideo). |
| ChatGPT does not use invideo | Check that invideo is installed under **Customize**, then **Plugins**, and name invideo in the prompt. |
| The chat cannot find your files | Check that they are in the project's Media panel, and use their names. |
| The chat cannot see a workspace | Reconnect invideo and choose that workspace in the sign-in. |
| Avoid: the chat asks for a code or password | Instead: you sign in on the invideo page that opens. |
| Avoid: the chat says done without a link | Instead: ask for the timeline URL and frames from the video. |
#### Next
- [Make a video with ChatGPT, Claude or Gemini](/how-to/personal/with-chatgpt-claude-gemini)
- [Make a birthday montage from photos and clips](/how-to/personal/birthday-montage)
- [Use invideo in ChatGPT](/start/chatgpt)
- [Know the cost before you spend](/how-to/cost-before-you-spend)
## Personal videos
### Video invitation
Source: https://docs.invideo.io/how-to/personal/video-invitation.md
A vertical video invitation, or evite, with your photos, the event details, music and a QR code that opens your RSVP page.
You get a vertical video invitation, also called a video invite or an evite, for a birthday, a wedding, a baby shower, a gender reveal or a save the date. It shows your photos or a generated scene, the names, the date, the place and a QR code for the RSVP. You send the MP4 in a chat or by email, and guests watch it on their phones.
Use invideo for this when you want to change the date, the venue or a name later: the invitation stays an editable timeline, and a changed invitation is one export away.
#### Quickest path
Set up your agent first with [invideo for AI agents](/agents). Then give it this prompt.
```text
Make a vertical video invitation for Maya's 30th birthday. Use the
photos in ./maya. Show "Maya turns 30", "Saturday 14 June, 7 pm",
"The Garden Room, 12 Hill Street" and a QR code for
https://example.com/rsvp. Add happy music. Show me frames before you
export, and tell me the price before you generate anything.
```
In a chat app, you drag your photos 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
Collect these before you start. The text matters most, so check the spelling of every name.
- **The event details.** Who, what, when, where, and the dress code if there is one.
- **The RSVP link.** A web page or form for replies. Links in a video are not clickable, so the agent turns the link into a QR code image.
- **Photos or clips.** JPG, PNG, MP4 or MOV from your phone. Or ask for a generated background scene instead.
- **The shape.** Vertical 9:16 for phones. Ask for 1:1 if you post it in a feed.
- **The mood.** Elegant, playful, cute, a theme color.
Characters from a film, a show or a game, such as a princess, a superhero or a cartoon dog, belong to their owners, and some image models decline them. Ask for an original look in the same spirit, like "a friendly dinosaur in green and gold".
#### Steps
Your agent works in your invideo project and tells you what it made at each step.
The agent makes a project with `new_project`, a 9:16 timeline with `new_timeline`, and a branch with `timeline_history`, so the main timeline stays as it is. For each local file, it calls `start_upload`, sends the bytes to the `upload_url`, then calls `add_media` with the `upload_id` on the branch URL. Expected result: a branch URL, and one sequence URL and media bin id per photo.
The agent makes a QR code image for your RSVP link with its own tool, like a QR code library on your machine, and saves it as a PNG. An image model can redraw the squares, so the agent does not make the code with one. Then it brings the PNG in with `start_upload` and `add_media`. Expected result: a QR code in the media bin.
With no photos, the agent finds a scene with `search_library` in `stock_video`, or generates one. To generate, it runs `find_models` with the job in words, like "vertical party background image", and `get_model` on its pick. A good pick is `nanobanana_pro`. Your agent can search for others with `find_models`. It calls `generate` with a `prompt`, `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 background sequence URL, which the agent reads with `read`, never with a second `generate`.
The agent places the photos with `place_media` or `insert_media`, fills the frame with `fill_frame`, and adds each line of text with `add_text`. It puts the QR code on a track above with `place_media` and sizes it with `set_zoom` and `set_position`. Fades come from `add_edge_transition` with the `fade` transition. Expected result: a new `state_token` and the clips on the timeline.
The agent searches `stock_music` with `search_library`, plays you the previews, and adds your pick with `add_media` and `stock`. It fades the end with `set_audio_fades`. Expected result: music on an audio track that ends with the video.
The agent runs `capture` at the title, the details and the QR code, and `check_timeline`. You scan the QR code frame on your screen with your phone. Expected result: every line is readable, no text sits at the edge, and the code opens your RSVP page.
The agent runs `export` with `format` video and an `idempotency_key`, then reads the export card until its status is `completed`. In a terminal, it saves the file with `invideo notebook show` on the card's URL. Expected result: a 1080p MP4 you download and send.
The same job from a terminal:
```bash
invideo project new --title "Maya 30 invite"
invideo agent register --name
export INVIDEO_AS=
invideo edit new --name "Invite" --width 1080 --height 1920
invideo asset upload ./maya/*.jpg ./rsvp-qr.png
invideo library search --library stock_music \
--query "happy party"
invideo asset add --stock
invideo gen models --query "vertical party background image"
invideo gen model --model nanobanana_pro
invideo gen run --model nanobanana_pro --dry-run \
-i prompt="warm garden party at dusk, string lights" -i aspect_ratio=9:16
invideo gen run --model nanobanana_pro \
--max-credits --idempotency-key maya-bg-1 \
-i prompt="warm garden party at dusk, string lights" -i aspect_ratio=9:16
invideo edit show --json
invideo edit change --timeline T1 --token \
--operations @ops.json
invideo edit capture --at 1,5,9 --out ./frames
invideo edit check
invideo edit export --format video --quality 1080 --json
invideo status --wait
invideo notebook show --out ./out
```
Where each value comes from:
- `` is the name you choose in `invideo agent register`. `INVIDEO_AS` passes it to every later command.
- `` comes from `invideo project new`, and `` from `invideo edit new`. `` comes from `library search`.
- The `gen` lines are only for a generated background. `` is a notebook page; `invideo notebook show ` lists them. `` is the dry run's `.estimated_credits`. The paid run returns a thread URL; `invideo notebook show` on it lists the image's sequence URL once it lands.
- `` comes from `invideo edit show`. `ops.json` holds a bare list of operations, like the `operations` list in the call below.
- `` 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 places the QR code and the details text in one `edit_timeline` call. `` is the `branch_url` that `timeline_history` returns. The `` and the ids, like `m4` for the QR code, come from `get_timeline`. Times ending in `_f` are frames at the timeline's rate.
```json
{
"url": "",
"timeline_id": "T1",
"expected_state_token": "",
"operations": [
{ "op": "place_media", "asset": "m4", "track": "V2", "at_f": 240,
"source_start_seconds": 0, "source_end_seconds": 5,
"mode": "place_on_top", "linked_audio": false },
{ "op": "add_text", "track": "V3", "start_f": 0, "end_f": 390,
"text": "Saturday 14 June, 7 pm\nThe Garden Room",
"style": { "font_size": 72, "align": "center",
"color": "#ffffff", "shadow": true } }
]
}
```
#### 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 layout, text, transitions, the QR code your agent makes, frame checks and the export.
- **Costs credits:** paid stock music or stock video with `add_media`, and any generated background with `generate`.
A photo invitation takes a few minutes. A generated background adds the time the model takes. See [Credits and what they cost](/account/credits) for prices.
#### If something goes wrong
Most problems are the QR code, text at the edge, or a character a model declines.
| 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 --wait` again. Do not resend. If it ends `failed`, change the input the review flagged. |
| The QR code does not scan | Make it larger with `set_zoom`, and keep it on a plain background. Scan the `capture` frame again. |
| The timeline is not 9:16 | The agent sets the size with `set_timeline_settings`, then `capture` again. |
| Text is cut at the edge of a phone | Move it in with `set_position`, and `capture` again. |
| A model declines a character | The provider does not allow that character. Ask for an original look in the same colors. |
| `edit_timeline` refused as a conflict | The timeline changed since the read. Read it with `get_timeline` and send again with the new token. |
| CLI exit 5 | A credit limit. Use your own photos, or check your balance. |
| CLI exit 7 | Nothing ran. Run the same command again. |
| Avoid: putting the RSVP link as text only | Instead: add the QR code, and paste the link in the message you send with the video. |
| Avoid: saying done without looking | Instead: `capture` frames and scan the QR code first. |
#### Prompts to try
Each prompt is a different kind of invitation.
```text
Make a 15 second gender reveal teaser, vertical. Pink and blue
balloons, the words "Boy or girl? Find out 3 May" and a QR code for
our RSVP form. Soft music. No characters from films.
```
```text
Make an elegant wedding save the date from our engagement photos in
./photos. Gold text, "Priya and Sam, 20.09.2027, Jaipur", a slow fade
between photos, and a QR code for our wedding website.
```
```text
Make a baby shower invite in 1:1 for Instagram and 9:16 for WhatsApp,
one branch each. Same text, same QR code, gentle music.
```
#### When to use something else
A video file cannot hold a link you tap or a reply button. Use your RSVP tool, like a form or an event page, for the replies, and send its link with the video.
For a printed card, use a design tool and export a PDF. invideo exports video, not print files.
#### Related
- [Make a birthday montage from photos and clips](/how-to/personal/birthday-montage)
- [Make a photo slideshow for any occasion: anniversary, graduation, retirement, baby's first year, family recap](/how-to/personal/occasion-slideshow)
- [Make a personalized video message from Santa or an AI presenter](/how-to/personal/personalized-message-video)
- [Make a video with ChatGPT, Claude or Gemini](/how-to/personal/with-chatgpt-claude-gemini)
- [Check your video before you say it is done](/how-to/check-your-video)
### Make a birthday montage from photos and clips
Source: https://docs.invideo.io/how-to/personal/birthday-montage.md
A birthday montage, or birthday slideshow, cut to the beat of the music, with gentle zooms on the photos and a title card.
You get a birthday montage, also called a birthday slideshow or photo video, from your photos and phone clips. Each photo and clip changes on a beat of the song, the photos move with a gentle zoom, and a title card opens it with the name and the age.
Use invideo for this when you want the cuts to land on the beat without counting them by hand: your agent reads the beats of the song, places every cut on one, and you can still move any photo after on the timeline.
#### Quickest path
Set up your agent first with [invideo for AI agents](/agents). Then give it this prompt.
```text
Make a birthday montage for Leo's 8th birthday from the photos and
clips in ./leo. Cut on the beat of a happy song from stock music,
add a slow zoom on each photo and open with "Happy 8th birthday, Leo".
16:9 for the TV. Show me frames before you export.
```
In a chat app, you drag your files 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 the files in the order you like, or let the agent sort them by date.
- **Photos and clips.** JPG, PNG, HEIC, MP4 or MOV from a phone or camera.
- **The title text.** The name, the age, and a closing line if you want one.
- **The song or the mood.** A track from stock music, or your own file if you have the rights to it.
- **The shape.** 16:9 for a TV or a laptop, 9:16 for a phone story, 1:1 for a feed.
- **The length.** A song length, or a number of seconds.
#### Steps
Your agent edits on a branch and tells you what changed at each step.
The agent makes a project with `new_project`, a timeline with `new_timeline` in the shape you asked for, and a branch with `timeline_history`. For each local file it calls `start_upload`, sends the bytes, then calls `add_media` with the `upload_id` on the branch URL. Expected result: a branch URL, and a sequence URL and a media bin id for each file.
The agent searches `stock_music` with `search_library`, for example "happy birthday" or "upbeat kids". You listen to the previews and pick one, and the agent adds it with `add_media` and `stock`. Expected result: the song in the media bin.
The agent runs `find_models` with the job in words, such as "birthday song that sings a name", and reads its pick with `get_model`. A good pick is `elevenlabs_music_v2_5_song`. Your agent can search for others with `find_models`. It calls `generate` with `prompt`, `duration` 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 thread URL, then the song's sequence URL, which the agent reads with `read`.
The agent reads the beat times with `media` `audio.beats` and the big moments with `audio.sections`, and turns each time into a frame: seconds times the frame rate. Then it places each photo and clip with `insert_media` or `place_media` and moves each cut to a beat with `trim_clip`, all in one `edit_timeline` call. Expected result: every cut on a beat.
In one call, the agent adds the `dynamic_zoom` effect to each photo with `add_effect`, and the title with `add_text`. Then it reads the timeline with `get_timeline` for the new ids. In a second call it sets the end zoom with `set_effect_parameter` on the effect's id, for a slow push, and fades the title in with `set_keyframes` on `opacity` on the title clip's id, like `t1`. Expected result: the photos move gently and the title fades in.
The agent runs `capture` at the title, a few cuts and the end, and `check_timeline`. It checks that no face is cut off by the zoom. Expected result: frames where every face is in view.
The agent runs `export` with `format` video, then reads the export card until its status is `completed`. In a terminal, it saves the file with `invideo notebook show` on the card's URL. Expected result: a 1080p MP4.
The same job from a terminal:
```bash
invideo project new --title "Leo turns 8"
invideo agent register --name
export INVIDEO_AS=
invideo edit new --name "Montage"
invideo asset upload ./leo/*
invideo library search --library stock_music \
--query "happy birthday"
invideo asset add --stock
invideo gen models --query "birthday song that sings a name"
invideo gen model --model elevenlabs_music_v2_5_song
invideo gen run --model elevenlabs_music_v2_5_song --dry-run \
-i prompt="upbeat kids pop song, sing happy birthday Leo" -i duration=60
invideo gen run --model elevenlabs_music_v2_5_song \
--max-credits --idempotency-key leo-song-1 \
-i prompt="upbeat kids pop song, sing happy birthday Leo" -i duration=60
invideo status --wait
invideo media audio.beats
invideo edit show
invideo edit change --timeline T1 --token \
--operations @ops.json
invideo edit capture --at 0,4,12,24 --out ./frames
invideo edit check
invideo edit export --format video --quality 1080 --json
invideo status --wait
invideo notebook show --out ./out
```
Where each value comes from:
- `` is the name you choose in `invideo agent register`. `INVIDEO_AS` passes it to every later command.
- `` comes from `invideo project new`, and `` from `invideo edit new`. `` comes from `library search`.
- The `gen` lines and the `status` line are only for a custom song; skip them for stock. `` is a notebook page; `invideo notebook show ` lists them. `` is the dry run's `.estimated_credits`.
- `` is the `url` the paid run returns. `` is the song's sequence URL: the one `asset add` returns, or the one `invideo notebook show ` lists once the song lands.
- `` comes from `invideo edit show`. `ops.json` holds a bare list of operations, like the first call's `operations` list below.
- `` is `.sequences[0]` of the export result. `invideo status` waits and saves nothing. `invideo notebook show` saves the file as `./out/export.mp4`; rename it after.
The key MCP call is the full call. It trims a photo to the next beat, adds the zoom and adds the title. At 30 fps, a beat at 2.4 seconds is frame 72. `` is the `branch_url` that `timeline_history` returns, and `` comes from `get_timeline` on that branch.
```json
{
"url": "",
"timeline_id": "T1",
"expected_state_token": "",
"operations": [
{ "op": "trim_clip", "clip": "c2", "edge": "tail", "from_f": 90,
"to_f": 72, "mode": "close_gap", "linked": true },
{ "op": "add_effect", "clip": "c2", "effect": "dynamic_zoom" },
{ "op": "add_text", "track": "V2", "start_f": 0, "end_f": 90,
"text": "Happy 8th birthday, Leo",
"style": { "font_size": 96, "align": "center", "shadow": true } }
]
}
```
The end zoom needs the effect's id, which only `get_timeline` returns. So the agent reads the timeline, finds the effect's id and the title clip's id, like `t1`, and sends the second call with the new `state_token`. The fade on the title is in it:
```json
{
"url": "",
"timeline_id": "T1",
"expected_state_token": "",
"operations": [
{ "op": "set_keyframes", "clip": "t1", "property": "opacity",
"points": [ { "at_f": 0, "value": 0, "ease": "ease_out" },
{ "at_f": 20, "value": 1 } ] }
]
}
```
#### 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 cuts, the zoom, the title, frame checks and the export.
- **Tool ops:** `audio.beats` and `audio.sections`. The dry run shows its price before anything starts.
- **Costs credits:** a paid stock song, or a song made with `elevenlabs_music_v2_5_song`. Your own music file costs nothing.
A montage takes a few minutes for the agent. Picking the photos and the song takes you longer. See [Credits and what they cost](/account/credits) for prices.
#### If something goes wrong
Most problems come from beats that do not suit the photos, or a zoom that crops a face.
| 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 --wait` again. Do not resend. If it ends `failed`, change the input the review flagged. |
| The cuts feel too fast | Ask for a cut on every second or fourth beat. The agent skips beats in the list. |
| A face is cut off by the zoom | Lower the end zoom of `dynamic_zoom`, or move its end point onto the face, then `capture` again. |
| `trim_clip` refused as stale | The edge moved. Read with `get_timeline` and send the trim with the new `from_f`. |
| `audio.beats` finds few beats | The song has a soft beat. Read `audio.markers` for tempo, or pick a song with a clear beat. |
| The music ends before the video | Trim the last photo, or ask for a longer song. |
| CLI exit 3 | The timeline changed since your read. Run `invideo edit show` and rebuild the ops. |
| Avoid: the agent edits main | Instead: it works on a branch from `timeline_history`, and you accept the change. |
| Avoid: saying done without looking | Instead: `capture` frames at the title, the cuts and the end. |
#### Prompts to try
Each prompt is a real variation of the job.
```text
Make a 60 second vertical birthday montage for Grandma's 90th from the
old photos in ./grandma. Slow, warm music, a cut on every second beat,
and "Happy 90th, Grandma Rose" at the start and the end.
```
```text
Cut my son's year in clips to this song, ./song.mp3. Put the cuts on
the strong beats, the fun clips on the chorus, and his name and age on
a title card. 16:9.
```
```text
Make a 30 second Instagram story montage of the party clips in ./party.
Fast cuts on every beat, a flash transition on the drop, and
"Best night ever" at the end.
```
#### When to use something else
invideo does not print photo books or cards. Use a print service for those, with stills from the montage made by `export` in `mode` image.
If you want the song to react to faces or dance moves, like a CapCut template, see [Make a trend edit like CapCut: slow-mo, meme, freeze frame](/how-to/personal/trend-edit). Templates you bought for CapCut or After Effects do not open in invideo. Your agent rebuilds the look on the timeline instead.
#### Related
- [Make a photo slideshow for any occasion: anniversary, graduation, retirement, baby's first year, family recap](/how-to/personal/occasion-slideshow)
- [Make a video invitation for a birthday, wedding, baby shower or gender reveal](/how-to/personal/video-invitation)
- [Make a trend edit like CapCut: slow-mo, meme, freeze frame](/how-to/personal/trend-edit)
- [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)
### Make a vertical travel video from phone clips
Source: https://docs.invideo.io/how-to/personal/travel-video.md
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.
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.
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.
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.
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`.
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.
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.
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`.
The same job from a terminal:
```bash
invideo project new --title "Portugal trip"
invideo agent register --name
export INVIDEO_AS=
invideo edit new --name "Reel" --width 1080 --height 1920
invideo asset upload ./portugal/*.mov
invideo media metadata
invideo media video.frames --at 0,3,6 --out ./frames
invideo gen models \
--query "reframe a wide video to vertical"
invideo gen model --model ltx_2_3_pro.reframe
invideo gen run --model ltx_2_3_pro.reframe --dry-run \
-i video= -i aspect_ratio=9:16
invideo gen run --model ltx_2_3_pro.reframe \
--max-credits --idempotency-key reframe-c3 \
-i video= -i aspect_ratio=9:16
invideo edit show
invideo edit change --timeline T1 --token \
--operations @ops.json
invideo edit capture --at 1,10,20,40 --out ./frames
invideo edit export --format video --quality 1080 --json
invideo status --wait
invideo status --wait
invideo notebook show --out ./out
```
Where each value comes from:
- `` is the name you choose in `invideo agent register`. `INVIDEO_AS` passes it to every later command.
- `` comes from `invideo project new`, and `` from `invideo edit new`. `` is the URL `invideo asset upload` returns for each clip.
- `` is a notebook page; `invideo notebook show ` lists them. `` is the dry run's `.estimated_credits`. `` is the `url` the paid run returns; `invideo notebook show ` lists the new clip's sequence URL once it lands.
- `` comes from `invideo edit show`. `ops.json` holds a bare list of operations, like the `operations` list in the call below.
- `` 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. `` is the `branch_url` that `timeline_history` returns. The `` and the clip ids, like `c3`, come from `get_timeline`.
```json
{
"url": "",
"timeline_id": "T1",
"expected_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 --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)
### Photo slideshow for any occasion
Source: https://docs.invideo.io/how-to/personal/occasion-slideshow.md
A photo slideshow, or photo video, for an anniversary, a graduation, a retirement, a baby's first year or a family recap, with music, titles and an optional loop.
You get a photo slideshow, also called a photo video or a photo montage, for an anniversary, a graduation, a retirement party, a baby's first year or a family year in review. The photos run in order with soft transitions and music, with a title at the start and short captions where you want them. For a party screen, the slideshow can loop for as long as the party runs.
Use invideo for this when you have many photos and want to fix one later: every photo stays a clip on an editable timeline, so you swap a photo or a caption without starting over.
#### Quickest path
Set up your agent first with [invideo for AI agents](/agents). Then give it this prompt.
```text
Make a 16:9 photo slideshow for my parents' 40th anniversary from the
photos in ./anniversary, oldest first. Five seconds a photo, soft
fades, gentle music, and "40 years, Anna and Raj" at the start. Make
a one hour loop for the party screen. Show me frames before you export.
```
In a chat app, you drag your photos 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
Sort your photos into a folder first. File names like `01.jpg`, `02.jpg` keep the order you want.
- **Photos.** JPG, PNG or HEIC, scans of old prints too. Short clips work as well.
- **The order.** By file name, by date, or by chapter (school, wedding, kids).
- **The words.** A title, an end line, and captions such as a year or a place.
- **The music.** A mood for stock music, or your own licensed file.
- **The shape and the screen.** 16:9 for a TV or a projector, 9:16 for a phone, 1:1 for a feed.
- **A loop, if needed.** How long it must play at the event.
#### Steps
Your agent edits on a branch and tells you what changed at each step.
The agent makes a project with `new_project`, a timeline with `new_timeline` in your shape, and a branch with `timeline_history`. It uploads each photo with `start_upload` and `add_media` on the branch URL. Expected result: a branch URL, and every photo in the media bin with an id.
For a small or soft scan, the agent runs `find_models` with the job in words, like "upscale an old photo scan", and `get_model` on its pick. A good pick is `crystal_upscaler`. Your agent can search for others with `find_models`. It calls `generate` with `source_image` set to the scan's sequence URL, `scale_factor` 2 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`. The original stays. Expected result: a sequence URL for the sharper image, which the agent reads with `read`, never with a second `generate`.
The agent places the photos with `insert_media` and `ripple` true, then sets each one's length with `trim_clip`. It sends them in one `edit_timeline` call. Portrait photos in a wide frame get the `fit_with_blur` effect with `add_effect`, so there are no black bars. Expected result: one clip per photo, in order, each the length you asked.
The agent adds a `fade` or `light_leak` transition between photos with `add_transition`. It adds the title, captions and the end line with `add_text`. Expected result: soft changes between photos and text on the right photos.
The agent finds a track with `search_library` in `stock_music` and adds it with `add_media`. It reads the beats with `media` `audio.beats` if you want the changes on the beat, and fades the end with `set_audio_fades`. Expected result: music that ends with the last photo.
For a party screen, the agent puts the whole slideshow into one clip with `nest_clips` in `mode` nest. It copies that clip with `duplicate_clip` until the timeline is long enough, and adds a `fade` between the copies with `add_transition`. Expected result: one timeline that plays as long as the event.
The agent runs `capture` at the title, a few photos, a loop seam and the end, and `check_timeline`, and looks for every face in frame, readable text and no black frame at the seam. 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`.
The same job from a terminal:
```bash
invideo project new --title "Anna and Raj, 40 years"
invideo agent register --name
export INVIDEO_AS=
invideo edit new --name "Slideshow"
invideo asset upload ./anniversary/*.jpg
invideo gen models --query "upscale an old photo scan"
invideo gen model --model crystal_upscaler
invideo gen run --model crystal_upscaler --dry-run \
-i source_image= -i scale_factor=2
invideo gen run --model crystal_upscaler \
--max-credits --idempotency-key scan-1 \
-i source_image= -i scale_factor=2
invideo library search --library stock_music \
--query "gentle piano"
invideo asset add --stock
invideo edit show
invideo edit change --timeline T1 --token \
--operations @ops.json
invideo edit capture --at 0,30,120,600 --out ./frames
invideo edit export --format video --quality 1080 --json
invideo status --wait
invideo notebook show --out ./out
```
Where each value comes from:
- `` is the name you choose in `invideo agent register`. `INVIDEO_AS` passes it to every later command.
- `` comes from `invideo project new`, and `` from `invideo edit new`. `` comes from `library search`.
- The `crystal_upscaler` lines are only for a small scan. `` is the URL `invideo asset upload` returns for that photo. `` is a notebook page; `invideo notebook show ` lists them. `` is the dry run's `.estimated_credits`.
- `` comes from `invideo edit show`. `ops.json` holds a bare list of operations, like the `operations` list in the calls below.
- `` is `.sequences[0]` of the export result. `invideo status` waits and saves nothing. `invideo notebook show` saves the file as `./out/export.mp4`.
To loop a finished file instead of the timeline, the CLI runs `video.loop` on its sequence URL. The dry run shows its price before anything starts.
```bash
invideo media video.loop --target-duration 3600 --dry-run
invideo media video.loop --target-duration 3600 \
--max-credits --idempotency-key loop-1
```
`` is an export's ``, or the URL `invideo asset upload` returns. `` is the `credits` value of the dry run.
The key MCP calls are full calls. They nest the slideshow into one clip, copy it, and fade between the copies. `` is the `branch_url` that `timeline_history` returns, and each `` comes from `get_timeline` on that branch. The agent reads the timeline between calls, so each call uses the real id of the nested clip and its copy. The first call nests the photos:
```json
{
"url": "",
"timeline_id": "T1",
"expected_state_token": "",
"operations": [
{ "op": "nest_clips", "video_clips": ["c1", "c2", "c3", "c4"],
"audio_clips": ["a1"], "name": "Slideshow", "mode": "nest" }
]
}
```
The second call copies the nested clip, here `c9` from `get_timeline`:
```json
{
"url": "",
"timeline_id": "T1",
"expected_state_token": "",
"operations": [ { "op": "duplicate_clip", "clip": "c9" } ]
}
```
The third call adds the fade between `c9` and the copy, whose id, like `c10`, comes from `get_timeline`:
```json
{
"url": "