Skip to content

DocsReference

Operations

The JSON request in full: inputs, output types, every option with its range and the types that take it, defaults, and the per-operation endpoints.

A JSON request describes the files you want; the API picks the ffmpeg arguments. It is one of two bodies POST /v1/jobs takes:

  • the operations body: named inputs and a list of outputs, each with a type and options, or a preset;
  • the command body: input_files, output_files and an ffmpeg command (Command rules).

Both may carry webhook_url and metadata. A body with the fields of both, or of neither, is 400 invalid_request. Unknown fields return invalid_request, with the location of each invalid field, so a typo never goes unnoticed.

The API validates the body, fills in presets and defaults (the job's spec) and compiles it to one ffmpeg command within the command rules. The response includes the normalized request, the compiled command with its file maps, and ffmpeg_command, one line that runs the same thing with a local ffmpeg. POST /v1/jobs/compile returns the same without creating a job or using credits.

bash
curl "$VIDEXO_API_URL/v1/jobs" \
  -H "Authorization: Bearer $VIDEXO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "inputs": {
    "talk": "https://videxo.io/samples/talk.mp4"
  },
  "outputs": [
    {
      "name": "web",
      "preset": "web-720p"
    },
    {
      "name": "poster",
      "preset": "poster"
    }
  ],
  "metadata": {
    "order": "1234"
  }
}'

Times are seconds as a number (90, 12.5) or a string HH:MM:SS.fraction ("00:01:30"). Sizes are pixels. Colours are "black", "white" or "#RRGGBB".

Inputs#

inputs maps 1 to 16 names to sources. A name is a lowercase letter, then up to 39 lowercase letters, digits or underscores. A source is an http or https URL, videxo://uploads/<id> for a completed upload, or the object form { "url": "..." } or { "upload_id": "..." }. Every input must be used by an output. An upload that is missing, not completed or expired is 404 not_found (reason upload).

Outputs#

outputs lists 1 to 16 outputs. Each one has:

  • name: required when the job has several outputs, main when it has one. Its alias is out_<name> and the job's outputs[].name is this name.
  • type (video, audio, image, storyboard, gif, metadata or scenes), or a preset, or both when they agree. metadata reads an input with ffprobe and takes no options.
  • input: the input it reads, required when the job has several, or concat to play several in a row.
  • filename: the stored file name, <name>.<format> by default; its extension must match format.

An image output with times writes one file per time: <name>_1, <name>_2, and so on. The 16 file limit counts these files.

Formats and codecs#

TypeFormatVideo codecsAudio codecs
videomp4 (default)h264, h265, av1aac, mp3, opus, alac
movh264, h265aac, alac, pcm
webmvp9, av1opus, vorbis
mkvh264, h265, vp9, av1aac, mp3, opus, vorbis, flac
tsh264, h265aac, mp3
audiomp3 (default)nonemp3
m4anoneaac, alac
aacnoneaac
wavnonepcm
flacnoneflac
oggnonevorbis
opusnoneopus
imagejpg (default)nonenone
pngnonenone
storyboardjpg (default)nonenone
pngnonenone
gifgif (default)nonenone
metadatajson (default)nonenone
scenesjson (default)nonenone

Every field#

A field the output's type does not take is 400 invalid_request with its pointer, not ignored.

FieldTypeOutput types
namestring. Required when the job has several outputs; main when it has one. The alias is out_<name>.all
typeone of video, audio, image, storyboard, gif, metadata, scenes. Required unless preset is set.all
presetstring. A preset name (GET /v1/presets); its options are the defaults and the output's own fields win.all
inputstring. The input it reads; required when the job has several, unless concat is set.video, audio, image, storyboard, gif, metadata, scenes
concatlist of string, or object, 1 to 16 (fields). Parts played one after another: input names, { "input", "start", "end", "transition" }, still images, { "image", "duration", "motion", "transition" }, or title cards, { "color", "duration", "text", "transition" }. Not with input.video, audio
filenamestring, up to 105 characters. The stored file name, <name>.<format> by default; one extension, matching format.all
formatone of mp4, mov, webm, mkv, ts, mp3, m4a, aac, wav, flac, ogg, opus, jpg, png, gif, json. The container or image format; from filename, or the type's default.all
strip_metadataboolean. Drop the input's global and stream metadata (GPS, device tags).video, audio, image, storyboard, gif
trimobject (fields). The part of the input to use: start, and end or duration.video, audio, storyboard, gif, scenes
cropobject (fields). A rectangle of the input frame, in input pixels.video, image, storyboard, gif
rotate90, or 180, or 270. Degrees clockwise.video, image, storyboard, gif
flipone of horizontal, vertical. Mirror the picture.video, image, storyboard, gif
resizeobject (fields). A new size: width, height, aspect, and how the picture fits (fit).video, image, gif
speednumber, 0.25 to 4. How fast it plays, 0.25 to 4; the sound keeps its pitch.video, audio, gif
fpsnumber, 1 to 120. A constant frame rate.video, gif
colorobject (fields). Brightness, contrast, saturation, gamma and hue.video, image, storyboard, gif
sharpenobject (fields). An unsharp mask.video, image, gif
blurobject (fields). A box blur of the whole frame, or of up to 4 regions.video, image, gif
overlayslist of object, 1 to 4 (fields). Images or videos drawn over the picture, in order.video, image, gif
textlist of object, 1 to 8 (fields). Up to 8 text layers, drawn after overlays in order.video, image, gif
subtitlesobject (fields)video
fadeobject (fields). Fade in at the start and out at the end; out needs trim.duration or trim.end.video, audio, gif
videoobject (fields). The video encoder: codec, quality or CRF, preset, maximum bitrate, pixel format.video
audiofalse, or object (fields). false for no sound, or the audio encoder and filters.video, audio
visualizeobject (fields). A waveform or spectrum video drawn from the input's sound.video
atnumber, 0 to 86,400, or HH:MM:SS string, or "auto". The frame time, or "auto" for the most representative of the first 300 frames.image
timeslist of number, 0 to 86,400, or HH:MM:SS string, 1 to 16. One image per time, <name>_1, <name>_2, ...; not with at.image
qualityone of low, medium, high. JPEG quality; PNG ignores it.image, storyboard
intervalnumber, 0.5 to 3,600. Seconds between frames.storyboard
columnsinteger, 1 to 16. Frames across.storyboard
rowsinteger, 1 to 16. Frames down.storyboard
tile_widthinteger, 16 to 640. Each frame fits in a square of this many pixels.storyboard
exact_timesboolean. Decode every frame for exact tile times, instead of key frames only.storyboard
colorsinteger, 2 to 256. Colours in the palette.gif
thresholdnumber, 1 to 100. The score, 1 to 100, at which a frame starts a new scene (default 10, scdet's).scenes

What a type fills in#

The defaults, as the API fills them in for the smallest output of each type. A preset's options and your own fields replace them.

{ "type": "video" } becomes:

JSON
{
  "type": "video",
  "input": "talk",
  "video": {
    "codec": "h264",
    "crf": 23,
    "encoder_preset": "veryfast",
    "pixel_format": "yuv420p",
    "faststart": true
  },
  "audio": {
    "codec": "aac",
    "bitrate": "128k"
  },
  "name": "main",
  "filename": "main.mp4",
  "format": "mp4"
}

{ "type": "audio" } becomes:

JSON
{
  "type": "audio",
  "input": "talk",
  "audio": {
    "codec": "mp3",
    "bitrate": "128k"
  },
  "name": "main",
  "filename": "main.mp3",
  "format": "mp3"
}

{ "type": "image" } becomes:

JSON
{
  "type": "image",
  "input": "talk",
  "at": "auto",
  "quality": "high",
  "name": "main",
  "filename": "main.jpg",
  "format": "jpg"
}

{ "type": "storyboard" } becomes:

JSON
{
  "type": "storyboard",
  "input": "talk",
  "interval": 10,
  "columns": 5,
  "rows": 4,
  "tile_width": 320,
  "exact_times": false,
  "quality": "medium",
  "name": "main",
  "filename": "main.jpg",
  "format": "jpg"
}

{ "type": "gif" } becomes:

JSON
{
  "type": "gif",
  "input": "talk",
  "fps": 12,
  "colors": 256,
  "resize": {
    "width": 480,
    "enlarge": false
  },
  "trim": {
    "start": 0,
    "duration": 60
  },
  "name": "main",
  "filename": "main.gif",
  "format": "gif"
}

{ "type": "metadata" } becomes:

JSON
{
  "type": "metadata",
  "input": "talk",
  "name": "main",
  "filename": "main.json",
  "format": "json"
}

{ "type": "scenes" } becomes:

JSON
{
  "type": "scenes",
  "input": "talk",
  "threshold": 10,
  "name": "main",
  "filename": "main.json",
  "format": "json"
}

Options#

The order is fixed, so a request means one thing: trim (and the time of an image), crop, rotate and flip, resize, speed, fps, color and sharpen, blur, overlays in list order, text in list order, then fade. crop uses input pixels; blur regions, overlays and text use the frame after resize.

  • resize: contain fits the whole picture in the box, cover fills it and crops the overflow at anchor, pad fits it and fills the rest with pad_color. Only width or only height keeps the shape. enlarge: false keeps an input smaller than the size at its own size. aspect alone crops or pads the input to that shape at its own size. Sizes from the input are kept inside 7,680 on a side and 33,554,432 pixels.
  • speed changes the picture and the sound, which keeps its pitch; sound from audio.mix stays at its own speed, and a copied stream cannot change speed.
  • fade.out needs the output's length: set trim.duration or trim.end.
  • overlays keep their last frame when they end, and the output ends with its main picture, also under a longer video overlay; set end to hide one. An overlay's sound is used only when audio.input names it.
  • Sound from another input (audio.input) is padded with silence when it is shorter and cut when it is longer, so the output ends with its picture; audio.mix and audio.codec: "copy" are not available with it.
  • audio.mix puts more sound under this one (background music); audio.normalize: true is -16 LUFS, -1.5 dBTP, LRA 11.
  • video.codec: "copy" and audio.codec: "copy" pass a stream through without decoding it, so options that need decoded frames or samples are rejected with it. A copied video cuts at key frames.
  • visualize draws a waveform or spectrum video from the input's sound and keeps the sound.
  • text draws up to 8 text layers, each one drawtext per line with a font from Fonts: position, margin and the offsets place the block of lines, which align to the side of position; box, outline and shadow style it; start, end and fade time it on the output's timeline (an image takes none of them). The text is drawn exactly as sent: quotes, colons, backslashes and %{...} are characters, never ffmpeg syntax. A character the font cannot draw, or a control character other than \n, is refused with the text's pointer.
  • subtitles burns the captions of an SRT or WebVTT input (UTF-8, at most 1 MB) into a video output after its text, each cue at its time, in a font from Fonts, centred at the bottom or top. size, margin and the outline width are pixels on a 1,080-line frame and scale with the output. Cue times follow the output's input, so a trimmed or sped up output shows the cues of the moments it shows; a concat follows its joined parts. The file's own tags and styles are removed before ffmpeg reads it, so every cue is plain text. The input is only for the subtitles: no other field may read it, and each output needs its own. A file that is not UTF-8 SRT or WebVTT fails the job with processing_failed (subtitles_invalid), a larger one with limit_exceeded (subtitles_size), before ffmpeg runs.
JSON
{
  "inputs": { "talk": "https://example.com/talk.mp4", "captions": "https://example.com/talk.srt" },
  "outputs": [{ "type": "video", "input": "talk", "subtitles": { "input": "captions", "font": "inter-bold", "size": 44 } }]
}

concat#

Taken by: video, audio.

FieldTypeRequired
inputstring. A lowercase letter, then up to 39 lowercase letters, digits or underscores.yes
startnumber, 0 to 86,400, or HH:MM:SS string. Seconds, or HH:MM:SS(.fraction).no
endnumber, 0 to 86,400, or HH:MM:SS string. Seconds, or HH:MM:SS(.fraction).no
textlist of object, 1 to 4. Up to 4 text layers, drawn after overlays in order.no
transitionobjectno

concat.text#

FieldTypeRequired
textstring. The text, 1 to 500 characters and at most 6 lines (\n).yes
fontone of inter, inter-bold, roboto, roboto-bold, open-sans, open-sans-bold, montserrat, montserrat-bold, poppins, poppins-bold, oswald, oswald-bold, bebas-neue, playfair-display, playfair-display-bold, merriweather, merriweather-bold, noto-sans, noto-sans-bold, noto-serif, noto-serif-bold, jetbrains-mono, jetbrains-mono-bold. A font id from GET /v1/fonts (default inter).no
sizeinteger, 8 to 1,000. The font size in pixels (default 48).no
colorone of black, white, or string. The text colour (default #FFFFFF).no
opacitynumber, 0 to 1. The text opacity, 0 to 1 (default 1).no
line_heightnumber, 1 to 3. The distance between lines, times size (default 1.25).no
positionone of top-left, top, top-right, left, center, right, bottom-left, bottom, bottom-right. Where the text sits (default center); lines align to its side.no
margininteger, 0 to 1,000. Pixels from the edges position touches (default 48).no
offset_xinteger, -7,680 to 7,680. Pixels to move right (negative: left).no
offset_yinteger, -7,680 to 7,680. Pixels to move down (negative: up).no
boxobject. A box behind each line: color (default black), opacity (0.5), padding in pixels (16).no
outlineobject. An outline around the letters: width in pixels (default 2), color (default black).no
shadowobject. A drop shadow: x and y in pixels (default 2), color (default black), opacity (0.5).no
startnumber, 0 to 86,400, or HH:MM:SS string. When the text appears, on the output's timeline (a part's own on a part).no
endnumber, 0 to 86,400, or HH:MM:SS string. When the text goes; the end of the output by default.no
fadeobject. Seconds of fade in and fade out; out needs end.no

concat.transition#

FieldTypeRequired
typeone of fade, fade-black, fade-white, dissolve, wipe-left, wipe-right, wipe-up, wipe-down, slide-left, slide-right, slide-up, slide-down, circle-open, circle-close, radial, zoom-in, pixelizeyes
durationnumber, 0.1 to 5. Seconds the two parts overlap, 0.1 to 5 (default 1).no

trim#

Taken by: video, audio, storyboard, gif, scenes.

FieldTypeRequired
startnumber, 0 to 86,400, or HH:MM:SS string. Seconds, or HH:MM:SS(.fraction).no
endnumber, 0 to 86,400, or HH:MM:SS string. Seconds, or HH:MM:SS(.fraction).no
durationnumber, 0 to 86,400, or HH:MM:SS string. Seconds, or HH:MM:SS(.fraction).no

crop#

Taken by: video, image, storyboard, gif.

FieldTypeRequired
xinteger, 0 to 7,680yes
yinteger, 0 to 7,680yes
widthinteger, 16 to 7,680yes
heightinteger, 16 to 7,680yes

resize#

Taken by: video, image, gif.

FieldTypeRequired
widthinteger, 16 to 7,680, evenno
heightinteger, 16 to 7,680, evenno
aspectone of 16:9, 9:16, 1:1, 4:5, 4:3, 3:4no
fitone of contain, cover, padno
anchorone of center, top, bottom, left, rightno
pad_colorone of black, white, or string. black, white or #RRGGBB.no
enlargeboolean. Scale a smaller input up to the size (default true); false keeps it at its size, never larger.no

color#

Taken by: video, image, storyboard, gif.

FieldTypeRequired
brightnessnumber, -1 to 1no
contrastnumber, 0 to 3no
saturationnumber, 0 to 3no
gammanumber, 0.1 to 10no
huenumber, -180 to 180no

sharpen#

Taken by: video, image, gif.

FieldTypeRequired
amountnumber, 0 to 3no
size3, or 5, or 7no

blur#

Taken by: video, image, gif.

FieldTypeRequired
strengthinteger, 1 to 50yes
regionslist of object, 1 to 4no

blur.regions#

FieldTypeRequired
xinteger, 0 to 7,680yes
yinteger, 0 to 7,680yes
widthinteger, 16 to 7,680yes
heightinteger, 16 to 7,680yes

overlays#

Taken by: video, image, gif.

FieldTypeRequired
inputstring. A lowercase letter, then up to 39 lowercase letters, digits or underscores.yes
positionone of top-left, top, top-right, left, center, right, bottom-left, bottom, bottom-rightno
margininteger, 0 to 1,000no
widthinteger, 8 to 7,680no
opacitynumber, 0 to 1no
startnumber, 0 to 86,400, or HH:MM:SS string. Seconds, or HH:MM:SS(.fraction).no
endnumber, 0 to 86,400, or HH:MM:SS string. Seconds, or HH:MM:SS(.fraction).no

text#

Taken by: video, image, gif.

FieldTypeRequired
textstring. The text, 1 to 500 characters and at most 6 lines (\n).yes
fontone of inter, inter-bold, roboto, roboto-bold, open-sans, open-sans-bold, montserrat, montserrat-bold, poppins, poppins-bold, oswald, oswald-bold, bebas-neue, playfair-display, playfair-display-bold, merriweather, merriweather-bold, noto-sans, noto-sans-bold, noto-serif, noto-serif-bold, jetbrains-mono, jetbrains-mono-bold. A font id from GET /v1/fonts (default inter).no
sizeinteger, 8 to 1,000. The font size in pixels (default 48).no
colorone of black, white, or string. The text colour (default #FFFFFF).no
opacitynumber, 0 to 1. The text opacity, 0 to 1 (default 1).no
line_heightnumber, 1 to 3. The distance between lines, times size (default 1.25).no
positionone of top-left, top, top-right, left, center, right, bottom-left, bottom, bottom-right. Where the text sits (default center); lines align to its side.no
margininteger, 0 to 1,000. Pixels from the edges position touches (default 48).no
offset_xinteger, -7,680 to 7,680. Pixels to move right (negative: left).no
offset_yinteger, -7,680 to 7,680. Pixels to move down (negative: up).no
boxobject. A box behind each line: color (default black), opacity (0.5), padding in pixels (16).no
outlineobject. An outline around the letters: width in pixels (default 2), color (default black).no
shadowobject. A drop shadow: x and y in pixels (default 2), color (default black), opacity (0.5).no
startnumber, 0 to 86,400, or HH:MM:SS string. When the text appears, on the output's timeline (a part's own on a part).no
endnumber, 0 to 86,400, or HH:MM:SS string. When the text goes; the end of the output by default.no
fadeobject. Seconds of fade in and fade out; out needs end.no

text.box#

FieldTypeRequired
colorone of black, white, or string. black, white or #RRGGBB.no
opacitynumber, 0 to 1no
paddinginteger, 0 to 200no

text.outline#

FieldTypeRequired
widthinteger, 1 to 20no
colorone of black, white, or string. black, white or #RRGGBB.no

text.shadow#

FieldTypeRequired
xinteger, -50 to 50no
yinteger, -50 to 50no
colorone of black, white, or string. black, white or #RRGGBB.no
opacitynumber, 0 to 1no

text.fade#

FieldTypeRequired
innumber, 0.1 to 60no
outnumber, 0.1 to 60no

subtitles#

Taken by: video.

FieldTypeRequired
inputstring. The input holding an SRT or WebVTT file (UTF-8, at most 1 MB); no other field of the job may read it.yes
fontone of inter, inter-bold, roboto, roboto-bold, open-sans, open-sans-bold, montserrat, montserrat-bold, poppins, poppins-bold, oswald, oswald-bold, bebas-neue, playfair-display, playfair-display-bold, merriweather, merriweather-bold, noto-sans, noto-sans-bold, noto-serif, noto-serif-bold, jetbrains-mono, jetbrains-mono-bold. A font id from GET /v1/fonts (default inter).no
sizeinteger, 8 to 400. The font size in pixels on a 1,080-line frame (default 48), libass's: the height of a line.no
colorone of black, white, or string. The text colour (default #FFFFFF).no
outlineobject. The outline around the letters: width in pixels on a 1,080-line frame (default 2; 0 for none), color (default black).no
positionone of bottom, top. Where the lines sit, centred: bottom (default) or top.no
margininteger, 0 to 1,000. Pixels from the bottom or top edge on a 1,080-line frame (default 48).no

subtitles.outline#

FieldTypeRequired
widthinteger, 0 to 20no
colorone of black, white, or string. black, white or #RRGGBB.no

fade#

Taken by: video, audio, gif.

FieldTypeRequired
innumber, 0.1 to 60no
outnumber, 0.1 to 60no
colorone of black, whiteno

video#

Taken by: video.

FieldTypeRequired
codecone of h264, h265, vp9, av1, copyno
qualityone of low, medium, high, losslessno
crfinteger, 0 to 63no
encoder_presetone of ultrafast, superfast, veryfast, faster, fast, medium, slowno
max_bitratestringno
pixel_formatone of yuv420p, yuv422p, yuv444p, yuv420p10leno
faststartbooleanno

audio#

Taken by: video, audio.

FieldTypeRequired
inputstring. A lowercase letter, then up to 39 lowercase letters, digits or underscores.no
codecone of aac, mp3, opus, vorbis, flac, alac, pcm, copyno
qualityone of low, medium, highno
bitratestringno
sample_rateinteger, 8,000 to 192,000no
channels1, or 2no
volumenumber, 0 to 4no
normalizetrue, or objectno
mixlist of object, 1 to 4no

audio.normalize#

FieldTypeRequired
loudnessnumber, -70 to -5no
true_peaknumber, -9 to 0no
rangenumber, 1 to 20no

audio.mix#

FieldTypeRequired
inputstring. A lowercase letter, then up to 39 lowercase letters, digits or underscores.yes
volumenumber, 0 to 4no

visualize#

Taken by: video.

FieldTypeRequired
styleone of waveform, spectrumyes
widthinteger, 16 to 7,680, evenno
heightinteger, 16 to 7,680, evenno
fpsinteger, 1 to 60no

Quality#

quality is constant quality; crf and bitrate are the advanced form. A mapping keeps its meaning: a change would be a new value, never a new meaning for an old one.

Video codeclowmediumhighlossless
h264CRF 28CRF 23CRF 18CRF 0
h265CRF 32CRF 28CRF 22refused
vp9CRF 40CRF 33CRF 24refused
av1CRF 40CRF 32CRF 24refused
Audio codeclowmediumhigh
aac96k128k192k
mp396k128k192k
vorbis96k128k192k
opus48k96k128k

VP9 and AV1 run in pure constant quality (-b:v 0), or with video.max_bitrate in constrained quality with the cap as the target bitrate; AV1 is several times slower than H.264, which counts against your plan's longest job. H.265 in MP4 and MOV is tagged hvc1 so Safari plays it.

How inputs count#

A job reads each input once and counts its bytes once, however many outputs use it. The processed duration and the output settings set the compute seconds: an output with trim processes only its range, and an image at 50 minutes takes about as long as an image at 3 seconds. A storyboard without exact_times uses the nearest key frames, which is faster than exact times; an image with at: "auto" picks from the first 300 frames after its start.

Concatenation#

An output with concat plays 1 to 16 parts one after another; a part is an input name, { "input", "start", "end", "text" }, a still image, { "image", "duration", "motion", "text" } (Slideshows), or a title card, { "color", "duration", "text" } (Join files). An object part takes transition, which crosses it with the part before (Join files). A part's up to 4 text layers are drawn on that part alone, timed on its own clock. The output's subtitles follow the joined timeline, which starts at 0. A video output needs a resize box, and every part is fitted into it (contain becomes pad) at fps (30 by default). Each input part must contain an audio track unless the output sets "audio": false or names its sound with audio.input, which replaces the parts' sound and ends with the picture. An image or a card is silent next to them; an output of images and cards alone has no sound unless audio.input names one. An audio output joins audio tracks only.

Per-operation endpoints#

For one input and one output, these endpoints take the output's fields and input in a flat body. They create the same job as POST /v1/jobs (same /v1/jobs/{id}, webhooks and usage), with operation set to the endpoint. extra_inputs names more inputs for overlays and mixes.

EndpointMakesOutput name
POST /v1/transcodevideovideo
POST /v1/thumbnailsimagethumbnail
POST /v1/storyboardstoryboardstoryboard
POST /v1/gifgifgif
POST /v1/audioaudioaudio
POST /v1/concatvideo (or audio with "type": "audio")video
POST /v1/slideshowvideovideo
POST /v1/probemetadatametadata
POST /v1/scenesscenesscenes

POST /v1/concat takes inputs, a list of 2 to 16 parts: a source, or { "source", "start", "end" }. POST /v1/slideshow takes images, a list of 1 to 16 pictures: a source, or { "source", "duration", "motion", "text", "transition" }. POST /v1/probe takes input and filename only, and waits for the job.

Errors#

A field that is wrong returns 400 invalid_request with one errors[] entry per problem, each with a pointer into your body (#/outputs/0/resize/fit) and a detail. A per-operation body's pointers point into that body (#/resize/fit).

JSON
{
  "type": "https://videxo.io/docs/errors#invalid_request",
  "title": "The request is not valid. `errors` lists each field that is wrong.",
  "status": 400,
  "detail": "outputs[1].fade.out: fade.out needs the output's length: set trim.duration or trim.end (or an end on every concat part).",
  "code": "invalid_request",
  "reason": "invalid_fields",
  "retryable": false,
  "request_id": "req_7h2m4x9q0c3v8k1n5r6t2b4z",
  "errors": [
    {
      "pointer": "#/outputs/1/fade/out",
      "detail": "fade.out needs the output's length: set trim.duration or trim.end (or an end on every concat part)."
    }
  ]
}

The full list is on Error codes.