---
title: "Assemble video"
method: POST
path: "/api/v1/tools/assemble_video"
---

# Assemble video

`POST /api/v1/tools/assemble_video`

## Request

- Base URL: `https://www.trezalabs.com`
- URL: `https://www.trezalabs.com/api/v1/tools/assemble_video`
- Auth: HTTP bearer

Join clips the account already has into ONE finished video, in the order given, optionally over a voiceover (narrationUrl) and a music bed, which become separate tracks with the clips ducked under the voice, and with burned-in captions. The bed is a track from the library (musicUrl) or one composed for this cut from a description (musicPrompt), never both. One clip plus a track is how to put music or narration under a single video: "add music to my video" is clipUrls [that video] with a musicPrompt, and no pipeline to build. Every url must come from list_assets or a run's outputs in get_run. Stills hold 3 seconds each, or stretch so a longer voiceover plays in full; lengthSec in the result is how long the cut will run, and any warning in it should be passed on. Renders in the background: poll get_run with the returned pipelineId and runId. Free once the account has bought credits (the result carries charged: false), so never ask a paying account to top up before assembling; charged on a trial. The same request made again within a few minutes returns the render already started (repeat: true) rather than starting another; to render the same cut a second time on purpose, pass a new requestId. Needs the pipelines:run and pipelines:write scopes.

## Request body

- object
  - `clipUrls` string[], required: Video or image urls in play order: two or more, or exactly one when narrationUrl, musicUrl or musicPrompt is set.
  - `narrationUrl` string: Voiceover laid over the whole edit. Everything else ducks under it.
  - `musicUrl` string: Music bed under the whole edit, beneath the clips' own sound and any narration: an audio url from list_assets. Leave it out when passing musicPrompt.
  - `musicPrompt` string: Compose the music bed for this cut instead of taking one from the library: genre, mood, tempo and instruments, e.g. "calm cinematic ambient, soft piano, gentle pads, no vocals". It sits and ducks exactly like a musicUrl bed and takes the same fades. Written by the Audio Generation node's default model (get_node_type music-gen), or its full-song model when the cut runs past 30 seconds; the result names the model as composedMusic. Leave it out when passing musicUrl.
  - `audioFadeInSec` number: Fade the music bed (or the only track) up over this many seconds at the start. Applies to a composed bed too.
  - `audioFadeOutSec` number: Fade the music bed (or the only track) down over this many seconds at the end, with the picture left as it is; 2 to 4 reads as a deliberate ending. Narration is never faded when there is a bed. For the whole ending to fade, use fadeOutSec.
  - `fadeInSec` number: Fade the opening up from black over this many seconds: the first shot's picture and the music bed together.
  - `fadeOutSec` number: Fade the whole ending to black and silence over this many seconds: the last shot's picture eases down and the music bed fades with it. This is "fade out at the end"; 1.5 to 3 reads as a deliberate ending. The transition argument is the cut between shots and does not touch the ending.
  - `captions` boolean: Transcribe the finished edit and burn captions in. Only worth it when the clips or narration contain speech.
  - `orientation` 'vertical' | 'horizontal' | 'match_first': "vertical" is 1080x1920 for Shorts, Reels and TikTok, "horizontal" is 1920x1080, "match_first" (the default) keeps the first clip's shape. Shots that do not fill the frame are letterboxed, never cropped.
  - `transition` 'none' | 'fade' | 'fadeblack' | 'wipeleft' | 'zoomin': How each shot hands off to the next: the join between two shots, applied at every cut. Defaults to a hard cut. "fadeblack" dips to black between shots; it is not a fade-out at the end, which is fadeOutSec.
  - `name` string: What to call the render, e.g. "Beach trip cut".
  - `requestId` string: Any id you choose for this one request. A call with an id that already started a render returns that render; a new id renders again even when everything else is identical.

## Response `200`

The tool's result.

- object

## Other responses

- `400`: The arguments were refused. `error` says why.
- `401`: No API key, or one that is not valid.
- `402`: The credit balance does not cover this run.
- `403`: The API key lacks a permission this tool needs.
- `404`: No such tool, or the pipeline, run or asset is not on this account.
- `429`: Too many runs started or in progress. Wait, then try again.

---

[API](https://skmtc.dev/trezalabs/apis/treza-pipelines-api.md) · [All operations](https://skmtc.dev/trezalabs/apis/treza-pipelines-api/llms.txt) · [OpenAPI document](https://skmtc.dev/trezalabs/apis/treza-pipelines-api/revisions/a76574c1731b?raw)
