Skip to main content

transcode

Re-encode a video to a different codec, container, or quality.

trim

Cut a clip by start offset and duration (lossless). Audio inputs keep their format (mp3, wav, m4a); FLAC comes back in a video container because trim copies the stream without re-encoding.

segment

Split a video into fixed-length segments (multi-output).

thumbnails

Extract frames at a fixed interval (multi-output).

scale

Resize a video (aspect-preserving when height is -2/-1).

extract_audio

Extract the audio track to mp3/wav/m4a. Optional sample_rate + mono make the output speech-to-text ready (a fixed-rate mono track an ASR model can ingest).

gif

Make an animated GIF from the clip.

watermark

Overlay an image/video (input_urls = [base, overlay]).

captions

Burn subtitles (SRT/VTT/ASS) into the video (input_urls = [video, subtitle]).

subtitle_compose

Author + burn animated TikTok-style captions — a timed top hook + a word-styled, animated bottom transcript — from word timings. input_urls = [video]; the styled .ass is server-generated from words/hook/style, so no subtitle file is supplied. Pass concrete style fields directly (no preset names).

concat

Concatenate 2-10 clips (input_urls = clips, normalized).

concat_copy

Losslessly rejoin 2-50 already-encoded, SAME-codec chunks via the concat demuxer with -c copy — no re-encode (input_urls = chunks in order). The chunks MUST share identical video+audio codecs/params; the engine probe-gate rejects a mismatch (a -c copy across divergent codecs can exit 0 yet play corrupt). For clips that differ, use concat (filter-concat re-encode) instead.

speed

Change playback speed (0.25x-4x).

volume

Adjust audio volume (0-10x). Container mp4/mkv only.

loudnorm

Normalize audio loudness to a target (EBU R128). Container mp4/mkv only.

reverse

Reverse video + audio.

poster

Produce an mp4 + a jpg poster frame (named outputs).

rotate

Rotate 90/180/270 degrees.

crop

Crop a region (omit x/y to center).

pad

Pad onto a canvas with a background color (omit x/y to center).

reframe

Reframe to a social aspect ratio (9:16/1:1/4:5/16:9) by crop (fill) or pad (letterbox).

reframe_track

Pan a fixed-size crop window across the frame to follow a subject, driven over time by a crop-track sidecar (input_urls = [video, crop_track]). input1 is a server-staged ffmpeg sendcmd script of timestamped crop commands. NOTE: the crop-track is emitted upstream by a saliency/face-tracking model that is NOT yet in the catalog, so this capability is INERT until that model is onboarded — it is not generally usable yet.

fade

Fade the video (and matching audio afade) in and/or out. type out/both REQUIRE fade_out_start (static argv cannot derive the clip duration).

color

Grade a clip via ffmpeg’s eq filter: brightness/contrast/saturation/gamma. A bare color (all defaults) is the identity grade — it re-encodes but leaves the picture unchanged.

lut3d

Color-grade a clip through a caller-supplied .cube 3D-LUT via ffmpeg’s lut3d filter (input_urls = [video, cube_lut]).

stills

Extract a single frame as a jpg/png image. position first/last/at (default first); position at REQUIRES timestamp (static argv cannot derive the clip duration). quality applies to jpg only (png is lossless).

overlay

Composite a second video over a base at a corner/center anchor (input_urls = [base, overlay]).

audio_replace

Replace or mix a video’s audio track with a second input (input_urls = [video, audio]). Container mp4/mkv only.

package_abr

Package a video into an adaptive-bitrate streaming bundle: CMAF HLS (master.m3u8) + DASH (manifest.mpd) over shared fragmented-mp4 segments. Clear (no DRM); stream-copies (no re-encode). Input should carry both a video and an audio stream. Routed to the Shaka packager.

package_abr_ladder

Package a video into a multi-rendition adaptive-bitrate ladder: CMAF HLS (master.m3u8) + DASH (manifest.mpd) over N renditions (clear, no DRM). Re-encodes each rung, so any source codec is accepted; the input must carry both a video and an audio stream. Give an explicit renditions list OR a ladder preset (not both); omit both for a default ladder clipped to the source height. Widths are derived from the source aspect (never supplied); a rung taller than the source is rejected (no upscaling). Unlike package_abr (single-rendition stream-copy via Shaka) this builds the full ladder; unlike hls_ladder (HLS only) it also emits DASH.

hls_ladder

Encode a TRUE multi-rendition adaptive-bitrate HLS ladder (master.m3u8 + N variant playlists/segments) in one ffmpeg pass. Re-encodes every rung, so any source codec is accepted; the input must carry an audio stream. Give an explicit renditions list OR a ladder preset (not both); omit both for a default 3-rung 720p ladder. Unlike package_abr (single-rendition stream-copy via Shaka), this builds the full ladder over the ffmpeg baseline. Upscaling above the source is NOT rejected, so choose rungs at or below the source height.

scene_detect

Detect hard cuts and return a structured analysis ({scenes:[{time,score}]}) as the result — a metadata RESULT, not a media URL (egress 0).

silence_detect

Detect silent spans in the audio and return a structured analysis ({silences:[{start,end,duration}]}) as the result — a metadata RESULT, not a media URL (egress 0); an input with no silence is a valid empty result.

probe

Probe a media file with ffprobe and return its container format + streams as a structured JSON RESULT (codecs, duration, resolution, bitrate, …) — a metadata RESULT, not a media URL (egress 0). Use it to inspect a source before choosing an operation.

silence_remove

Keep only the given time ranges and concatenate them into one continuous output (gaps closed) in a single ffmpeg pass — the ‘act’ half of silence-skip and long->short editing. Chain silence_detect first and invert its silent spans into the kept ranges. Ranges must be sorted ascending and non-overlapping; everything outside them is dropped. Re-encodes.

storyboard_sprites

Sample frames at a fixed interval and pack them into one tiled sprite sheet image PLUS a WebVTT thumbnail index (named outputs: sheet + vtt).

slideshow

Turn 2-50 still images into one silent video (input_urls = images): each image is shown for image_duration, normalized to a common canvas, optionally Ken Burns-zoomed, and concatenated.

crossfade

Crossfade two clips into one (input_urls = [first, second]): the video xfade starts offset seconds into the first clip and blends over duration seconds; the audio always acrossfades at the junction of the two tracks. Both inputs must carry a video AND an audio stream and share a frame rate. Re-encodes.

silence_split

Detect silences and split the file at them in one job (multi-output: seg_000, seg_001, … in order): each cut lands at the END of a detected silence, so each new segment starts where sound resumes. max_segment_seconds force-cuts when no usable silence arrives in time. The input must carry an audio stream. Default stream-copy snaps cuts to keyframes; reencode=true gives exact boundaries (H.264, invalid in webm).

sticker_overlay

Composite a sticker/logo image (or video) over a base video inside a timed window (input_urls = [base, sticker]): visible from start until end (omit end to run to the source end). Same anchors as overlay; the sticker’s own alpha is honored (no opacity control). Only the base’s audio is kept. Re-encodes.

title_card

Draw a text title over the video with drawtext (rendered verbatim — no ffmpeg expansion; newlines break lines; max 200 chars after normalization). Show window: omit start and end for the whole video, start alone runs to the end, end alone runs from the beginning. Re-encodes.

audio_duck

Duck a music/program bed under a voice track (input_urls = [music, voice]): the voice drives a sidechain compressor that lowers the music while the voice is above threshold, then the same voice is mixed on top. Audio-only output (a video input contributes only its audio) running exactly as long as the music; levels are never auto-normalized. Both inputs must carry an audio stream.

audio_master

Master the audio track to a distribution loudness target: a fixed gentle compressor, then single-pass EBU R128 normalization, then a true-peak limiter, written audio-only (always resampled to 44100 Hz). Give target_lufs/ceiling_dbtp OR a named preset — NOT both (a call supplying preset alongside target_lufs/ceiling_dbtp is rejected before submission); omit all three for the engine defaults (-14 LUFS / -1 dBTP). For normalization without compression use loudnorm instead. The input must carry an audio stream.

audio_analysis

Measure the audio track — integrated loudness (LUFS), loudness range (LU), true peak (dBTP), sample peak (dBFS), and the astats clipping indicators flat_factor/peak_count — via astats+ebur128, returned as a structured JSON RESULT, not a media URL (egress 0). The input MUST carry an audio stream. A field that cannot be measured is omitted (e.g. digital silence has no finite dB peak) — treat a missing field as not-measurable, never as 0.

run_ffmpeg

Run an arbitrary ffmpeg command (full power). Put the ffmpeg arguments in args (the leading ffmpeg is added for you). Use {input} for a single input_url, or {input0}/{input1}/… for multiple input_urls; use {output} for the output. Subject to server-side allowlist.

upload_file

Upload a local file (max 100MB) to eachlabs storage and get a public URL usable as input_url for the other tools. The presign call and the upload PUT do not create a prediction and are not billed as one; storage egress may bill per your plan.

get_job

Fetch the status and result of a prediction. With wait: true, waits up to the hold for it to finish; call it again while the result says running. Call it again with the same id to show a finished result again. For the predictions of one run_model call with count above 1, pass all their ids as prediction_ids to get them back together as one result. A running job can take several minutes, video often 5 to 10; keep checking with get_job. Do not start another run, on this model or a different one, as a fallback unless the person asks for it: each run is billed. If a job fails or shows no progress for far longer than usual, you may suggest another model, but ask the person first.

search_models

Find each::labs models by name. Returns slug, name, type and pricing for each match. Use the slug with get_model_schema and run_model.

get_model_schema

Return a model’s input schema (OpenAPI document) by slug, so a run_model input can be built from it.

run_model

Run any each::labs model by slug with an input that matches its schema. Creates a billed prediction and returns the result, or, for a job that takes longer than about 20 s, its prediction id with status running: then check it with get_job and wait: true until it finishes. If a call ends without a result or an id, use list_recent_jobs before starting the run again. With count above 1, starts that many predictions of the same input and returns them together as one result, so several variants show on one card; while they are running, check them with get_job and all their ids as prediction_ids. A running job can take several minutes, video often 5 to 10; keep checking with get_job. Do not start another run, on this model or a different one, as a fallback unless the person asks for it: each run is billed. If a job fails or shows no progress for far longer than usual, you may suggest another model, but ask the person first.

list_workflows

List the organization’s workflows: id, slug, name, status and counts.

get_workflow

Return one workflow: its definition, versions and input schema.

trigger_workflow

Trigger a workflow version with its inputs. Creates a billed execution and returns its outputs, or, for a run that takes longer than about 20 s, its execution id with status running: then check it with get_workflow_execution and wait: true until it finishes. If a call ends without a result or an id, use list_recent_jobs before starting the run again. A running job can take several minutes, video often 5 to 10; keep checking with get_workflow_execution. Do not start another run, on this model or a different one, as a fallback unless the person asks for it: each run is billed. If a job fails or shows no progress for far longer than usual, you may suggest another model, but ask the person first.

get_workflow_execution

Fetch the status and outputs of a workflow execution. With wait: true, waits up to the hold for it to finish; call it again while the result says running. Call it again with the same id to show a finished result again. A running job can take several minutes, video often 5 to 10; keep checking with get_workflow_execution. Do not start another run, on this model or a different one, as a fallback unless the person asks for it: each run is billed. If a job fails or shows no progress for far longer than usual, you may suggest another model, but ask the person first.

list_recent_jobs

Lists the organization’s most recent runs: id, model, status, time and output link. Use it when a call that starts a run (run_model, trigger_workflow or a video tool) ended without a clear answer and you have no job id: it timed out, the connection dropped, or the reply was cut off. The run may still be going and billed, so find it here and check it with get_job instead of starting it again. Don’t use it after a clear answer: a result, “still running” with an id, or an error such as rate limited (429), insufficient balance or invalid input; those runs either finished, gave you their id, or never started.

request_file

Show the person an upload card inside the conversation so they can pick files from their device (drop zone or file picker; the gallery on a phone). The card uploads them from their browser to eachlabs storage. One card takes up to 50 files of up to 100 MB each. Set count to the number of files the person mentioned, and max to the most files the target accepts for that input when you know it (the array’s maxItems in get_model_schema, or the video tool’s input limit, e.g. slideshow takes up to 50); a card for more than one file lets the person add up to max, or up to 50 without it. Show one card for the whole set (for example all the images of a slideshow); use separate cards only for files with different roles (a face and a target). Returns a receipt_url: call wait_for_upload with it immediately and continue with the files it returns. In an app that shows cards, do not ask the person to paste a link or to attach the file to the chat. If your app does not show cards (for example a terminal), do not call wait_for_upload; ask the person in the chat for a public file URL instead. Renders only in hosts that support MCP Apps.

wait_for_upload

Call right after request_file with the receipt_url it returned. Waits until the person has finished uploading on the card and returns the files, one line each with name, kind and public URL; continue with those URLs. If it returns status “waiting”, call it again with the same receipt_url.

presign_upload

Called by the upload card for a file the person picked: returns a signed storage URL for that file’s type. Not for the model; the card calls it.

file_uploaded

Called by the upload card once a file is in eachlabs storage; records the public URL. The model gets the files from wait_for_upload, not from here. Only URLs on eachlabs storage hosts are accepted.

wait_for_choice

Call with the choice_receipt_url from a result that showed several artifacts, when the person should pick one. Waits until they select one and press Use this variant on the card and returns the chosen artifact’s number and URL; continue with that URL. If it returns status “waiting”, call it again, or ask the person which one. If your app does not show cards (for example a terminal), do not call wait_for_choice; ask the person in the chat which one they want instead.

show_files

Show files the person already has as a card in the conversation: a picked variant, an earlier result, an uploaded input. Give the public URLs; do not run a model or a video tool just to display a file. With pick: true the card becomes a picker and the choice reaches you through wait_for_choice. If your app does not show cards (for example a terminal), do not call wait_for_choice; ask the person in the chat which one they want instead.

request_parameters

Show the person a settings card for a model before running it: the model’s options from its schema as visible controls (size, count, format, guidance, seed and so on), prefilled with the input you have, and a Run button. File inputs you already have are kept and not shown. Returns a receipt_url: call wait_for_parameters with it immediately; it returns the model, version and input to pass to run_model. Use it when a model has options worth choosing; skip it when the person already said exactly what they want. If your app does not show cards (for example a terminal), do not call wait_for_parameters; ask the person in the chat for the values instead. Renders only in hosts that support MCP Apps.

wait_for_parameters

Call right after request_parameters with the receipt_url it returned. Waits until the person pressed Run on the settings card and returns {model, version, input}: pass them to run_model as they are. If it returns status “waiting”, call it again with the same receipt_url.

request_workflow_inputs

Show the person a card to set up a workflow run: the inputs of the version to run as visible controls (text, choices, numbers, switches), prefilled with what you have, with a drop zone inside the form for each image, video or audio input, and a Run button. Returns a receipt_url: call wait_for_workflow_inputs with it immediately; it returns the workflow_id, version_id and inputs to pass to trigger_workflow. Use it when a workflow needs a file or has inputs worth choosing; skip it when the person already gave every input. If your app does not show cards (for example a terminal), do not call wait_for_workflow_inputs; ask the person in the chat for the inputs instead. Renders only in hosts that support MCP Apps.

wait_for_workflow_inputs

Call right after request_workflow_inputs with the receipt_url it returned. Waits until the person pressed Run on the workflow card and returns {workflow_id, version_id, inputs}: pass them to trigger_workflow as they are. If it returns status “waiting”, call it again with the same receipt_url.

request_trim

Show the person a timeline to choose a range of a video or audio file: the player, two draggable handles for the start and the end, the times and the length, and a Preview button. Use it when the person wants to cut, trim or keep part of a clip and has not given exact times. Returns a receipt_url: call wait_for_edit with it immediately; it returns {edit: “trim”, url, start, duration} to pass to trim as input_url, start and duration. If your app does not show cards (for example a terminal), do not call wait_for_edit; ask the person in the chat for the start and end times instead. Renders only in hosts that support MCP Apps.

request_crop

Show the person the first frame of a video, or an image, with a draggable, resizable rectangle to choose a crop region, aspect presets (free, 1:1, 16:9, 9:16, 4:5) and the region’s x, y, width and height in source pixels. Use it when the person wants to crop or reframe and has not given exact pixels. Returns a receipt_url: call wait_for_edit with it immediately; it returns {edit: “crop”, url, x, y, width, height}. Today the values feed the video crop tool (input_url, x, y, width, height); an image URL gives the same rectangle for an image crop. If your app does not show cards (for example a terminal), do not call wait_for_edit; ask the person in the chat for the crop region in pixels instead. Renders only in hosts that support MCP Apps.

wait_for_edit

Call right after request_trim or request_crop with the receipt_url it returned. Waits until the person pressed the button on the card and returns what they chose. For {edit: “trim”, url, start, duration}: call trim with input_url = url and start and duration as they are. For {edit: “crop”, url, x, y, width, height}: call crop with input_url = url and x, y, width and height as they are. If it returns status “waiting”, call it again with the same receipt_url.
Last modified on October 12, 2026