Edit JSON
preview and apply take one JSON object with exactly one key: range (cut or restore speech), speed (play parts faster or slower), track (tracks and clips), placement (camera and screen layout), canvas (the size of the video frame), offset (the sync of one recording to the reference audio) or gain (the volume of an inserted video). Unknown keys are refused with invalid_edit.
Always preview first. It uses the same checks as apply and writes nothing.
Range edits: cut and restore
Cut the lunch sentence, using output times from cutzal transcript:
Bring a cut part back. Restores must use source time, because cut parts have no output time:
action:cutorrestore.coordinate:outputorsource(see coordinates.md).ranges: 1 to 1000 items of{ "start", "end" }in seconds,startat least 0 and less thanend.- Put the edges on word boundaries from
transcript(a word'sstartandend). An edge inside a word can keep part of it (partial: trueintranscript). - One apply is one checkpoint. Put all ranges of one decision (for example every filler word) in one edit.
- Optional
rippleTrackIdsoncutorrestorelists the overlay visual tracks that should move with the edit. Use IDs fromcontext.tracks; duplicates, audio/source tracks, and this field on swap are refused. Without it, output-anchored clips keep their existing times.
The participating clips follow the actual change in rendered output, including padding and existing cuts. Internal cuts split video graphics and advance sourceInS so the remaining footage stays synchronized. Content-anchored clips and native camera/audio tracks retain their existing projection rules. Affected locks, missing media, unsupported linked sound-effect trims, or fragments that only hold an exhausted video frame reject the entire cut. Preview shows those conflicts without writing. Undo/rewind restores both the cut and graphics. In the CutZal app, every edit that only shortens or only lengthens the video (keys, timeline, phone script and chat) includes every additional visual track automatically when the project has an output-anchored clip. cutzal commands keep the default above: only the tracks you list move.
A restore with rippleTrackIds pushes later participating clips back by the actual increase in rendered output, and a clip that runs to the end of the video grows to the new end. This restores the speech that the cut above removed and moves the graphics back with it:
When the restored speech lands inside a clip, an image or text clip continues through it, and a video clip splits there and leaves the restored part empty. A video clip is refilled instead where an earlier ripple cut split that same graphic (restoring the same speech with the same tracks puts its footage back), keeps playing when it runs to the end of the video, and simply grows while it is already holding its last frame. A linked sound effect that ends after the restored spot inside its parent rejects the whole restore.
An output-anchored clip that runs to the end of the video (a background, watermark, frame or a header text clip) makes a shortening cut without rippleTrackIds fail with track.outOfRange, because the clip would pass the new end. List its track in rippleTrackIds whenever you cut, so the clip shrinks with the video and later clips move back with the speech.
Speed edits: fast-forward or slow down
speed plays parts of the video faster or slower. The screen, the camera and clips that follow the speech (content anchors) change speed together, the edited video gets shorter (or longer), and later speech and clips move with it. Use it for a long process that should be seen but not heard in full, such as reading a prompt aloud, typing or waiting for a result. Cut what nobody needs to see.
Fast-forward the lunch sentence 4 times, using output times from cutzal transcript. Its sound is muted:
Speed up the last sentence a little and keep its sound:
Bring the last sentence back to its original speed with rate: 1:
Speed up the whole video a little. It multiplies the speed of every part, so the 4x part above now plays at 4.8x:
rate: 0.25 to 16 with up to two decimals. Above 1 is faster, below 1 is slower, and1removes the speed from those ranges.coordinate:outputorsource(see coordinates.md). Output ranges cover only what is in the edited video now. Source ranges also cover parts that are cut, so speech you restore inside them later plays at the same speed.audio:keep(the speech speeds up, pitch preserved) ormute. Without it, 0.5 to 2 keeps the sound and other speeds mute it.keepis allowed from 0.5 to 4. A muted part has no captions. Sound effect clips on audio tracks still play, so add a fast-forward sound effect there if the person wants one.- Setting a speed on a range again replaces it only where the ranges overlap. Several ranges in one edit get the same speed and are one checkpoint.
global: the speed of the whole video, 0.5 to 2 with up to two decimals. It multiplies every part's speed (1.2 on a 4x part plays it at 4.8x), so parts keep their relative speed. A part whose resulting speed is above 4 or below 0.5 plays without sound.{ "speed": { "global": 1 } }brings the whole video back to its original speed. In chat it needs a whole-video request.- Clips pinned to output time (
anchor.kind: "output") move with the speech around the change. Over a part whose speed changes, an image or text clip gets shorter or longer with it, while a video clip or sound effect keeps its length and plays at normal speed. A clip that ran to the end of the video still runs to the new end. An edit that would move a clip on a locked track, or make a clip pass the new end or overlap another, is refused as a whole withblockedand theclipIds. context.speedlists the current speeds:spansin source time andratefor the whole video.context.output.segmentscarriesrate(andmutewhen the sound is off) on those parts.transcriptandcapturealready use the new output times.- A project with a speed is saved as file version 19. Older CutZal versions refuse to open it instead of playing it at normal speed.
Track edits
context returns the track document in result.tracks:
tracks: in drawing order, bottom first. A later track is drawn on top of an earlier one.binding.kind: "source": the recording (screen, camera, microphone, system audio).binding.kind: "transcript": the subtitle track.binding.kind: "overlay": tracks for your clips.kind: "video"holds image and video clips,kind: "audio"holds sound effects.
clips: image and video clips.audioClips: sound effect clips (only when an audio track exists).
The accepted track edits are track.add, track.update, track.remove, track.reorder, clip.put, clip.remove and track.batch. Use rewind to undo; there is no undo edit.
Add a track
A video track for images and B-roll:
An audio track for sound effects. Audio tracks need "muted" and are always "visible": true:
id: your choice, unique, up to 200 characters.name: what the person sees.- Optional
"beforeTrackId": "<id>"inserts the track before that one (so it is drawn below it). Without it the track goes last (on top). - Images can be registered with
asset addbefore adding a track. Registration preserves the existing tracks and clips;clip.putplaces the image on a video track. - Sound files (
asset addof an audio file,sfx use) are refused until the project has an audio track. Add the audio track first.
Put a clip
clip.put adds a clip, or replaces the clip with the same id. To change a clip, copy it from context, change fields, and put it again.
An image over the whole frame from 1 s to 2 s of the output:
B-roll in a box at the top right from 3 s to 4.5 s, starting 2 s into the B-roll file:
A sound effect at 1 s, 9 dB quieter than the file:
Image and video clip fields:
id: your choice, unique across all clips, up to 200 characters.trackId: an overlay video track.assetId: an image or video asset fromcontext.assetsorasset add.sourceInS: where in the file to start, in seconds. Always0for images.anchor:{ "kind": "output", "startS", "endS" }pins the clip to output time for ordinary range edits. Explicit cuts withrippleTrackIds(and cuts made in the CutZal app) update participating clips in that transaction. Opening a project never repositions them. Clips the person placed in CutZal may have{ "kind": "content", ... }anchors that follow the speech; keep those as they are when you put such a clip again.placement:full(fills the frame),screen(where the screen recording sits in the layout) orframe(a box set byframe).frame(needed forplacement: "frame"):posis a 9-grid spot (tl,tc,tr,ml,mc,mr,bl,bc,br) or{ "x", "y" }from 0 to 1 measured from the top left.sizePctis the box width as a fraction of the frame width (0.35 is 35%).radiusPxrounds the corners.aspectismedia(the file's own shape) or1:1,4:3,3:4,16:9.mirrorflips it horizontally.focus(optional) picks which part of the file shows when the box crops it (coordinates.md).endBehavior: what happens when the file ends beforeendS.returngoes back to what was showing before;holdkeeps its last frame untilendS.
Sound effect clip fields: id, trackId (an overlay audio track), assetId (an audio asset), sourceInS, gainDb (from -40 to 12, one decimal place) and anchor as above.
Other track edits
track.updatechangestakes at least one ofname,locked,visible,muted(audio tracks, and the main voice track: the microphone, or the main video when there is no microphone),solo(the same tracks: when any track is soloed, only soloed tracks play, in previews and exports),gainDb(system audio tracks),denoiseanddenoiseMix(the microphone track). Muting the voice or soloing another track exports the video with no speech.track.remove({ "kind": "track.remove", "trackId": "<id>" }) removes an overlay track that has no clips left.track.reorder({ "kind": "track.reorder", "trackIds": [...] }) takes every track id in the new order, bottom first. The recording tracks (binding.kind: "source") keep their order among themselves, so the camera stays above the screen. Overlay tracks can go anywhere, for example between the screen and the camera so the camera stays on top of a B-roll clip.
Background noise removal
Turn on denoise on the microphone recording track (binding.sourceId is the microphone source, usually source:microphone) to remove background noise such as fans and air conditioners from the voice:
- It applies only while the microphone is the master audio.
falseturns it off. Sound effects and system audio are never processed. - The app processes the whole microphone recording on this computer in the background (a long recording can take a few minutes, no cost). Preview plays the original sound until it is ready, and export waits for it. Transcripts and AI cut decisions keep using the original sound.
denoiseMix sets the strength, an integer from 1 to 10 (like Premiere's Mix Amount). 10 is the default and uses only the cleaned voice. Lower values blend in more of the original sound, which keeps a voice natural when the cleaned result sounds over-processed:
- Changing the strength does not process the recording again. Turning
denoiseoff keeps the strength for the next time it is turned on.10removes the field.
Several changes at once
track.batch applies up to 100 track edits together as one checkpoint. Batches cannot be nested.
Layout edits: camera and screen
placement changes how the screen recording and the camera are arranged, like the layout (화면 구성) controls in CutZal. It needs a project with a screen and a camera recording. context.placement names the source tracks that hold the screen and the camera (null when there is none), and the clips on those tracks hold the current layout: placement (full fills the picture, frame uses frame) and frame.
Show only the camera while the person talks from 2 s to 4 s of the edited video:
Show the screen with the camera in the top left corner for the whole video:
Make the camera a little bigger everywhere:
Shrink the screen and round its corners for one part, using source time:
The face sits left of the middle of the camera recording, so a narrow camera frame shows it off center. Keep the face at the center of the frame for the whole video (focus is the face's position in the recording, before mirroring):
- Exactly one of these:
preset:cam-pip(screen with the camera in a corner;cornerpicks it, defaultbr),split(screen left, camera right),cam-full(camera only) orscreen-full(screen only).- Presets look at the shape of each recording and of the frame. A recording whose orientation differs from the frame (a vertical screen recording in a horizontal video, a horizontal camera in a vertical short) is shown whole with
screen-full,cam-fulland the screen part ofcam-pip, with the background around it, instead of being cropped. In a horizontal frame with a vertical screen recording,splitputs the camera on the left and the whole vertical screen on the right. cameraorscreen: only the frame fields to change. The other fields keep their value in each part. The fields are in coordinates.md.
coordinateandrangesgo together, with the same rules as range edits. Leave both out to change the whole video. Parts cut out between your ranges are included, the same as selecting blocks in CutZal.- The layout follows the speech: later cuts and restores keep it on the same words.
camera.mirroralso works where the camera fills the picture (cam-full). Position, size, rounding and aspect changes still skip full-picture camera parts; a full-picture-only range without a mirror change is refused withinvalid_edit. A range with no camera is also refused.- Switching between
cam-pip,splitandcam-fullpreserves each existing clip's mirror setting. Newly created clips without an existing camera segment usemirror: false;screen-fullremoves the camera from the selected range. focusfollows the same rules as the other frame fields and also carries over whencam-piporsplitrebuilds the camera frame. A full-picture camera part has nofocus, so a part that goes throughcam-fullcomes back centered.- When nothing would change,
applyreturnsnoopandpreviewshowschanged: false. - One edit is one checkpoint. To pick a preset and then change the camera size, send two edits.
- A clip that fills the frame can carry
"conform": "fit"(track edits,placement: "full"only): the whole recording is shown inside the frame instead of filling it and cropping the edges. Clips withoutconformfill the frame as before.
Canvas: the size of the video frame
canvas sets the frame size of the whole video: "1920x1080" (horizontal), "1080x1920" (vertical) or "original" (the size of the screen recording, the default). One video has one frame size; vertical and horizontal recordings are then placed inside it with placement.
Make a horizontal video from a project whose screen was recorded on a vertical monitor:
- Layouts that showed a recording whole keep showing it whole: a full-frame clip that would now be cropped gets
conform: "fit", and a box that would now run off the frame shrinks in place. Clips that were already cropped stay as they are, and locked tracks are not changed.applyreturns how many clips it changed asfixed. - Then pick the layout per part with
placementpresets, which follow the new frame shape. previewonly says whether the size changes (changed). Check the result withcaptureafterapply.- One edit is one checkpoint, and rewinding to an earlier checkpoint also restores the frame size. Same size is
noop.
Offset: sync a recording to the reference audio
offset moves one take of one source in time against the reference audio. sourceTime = masterTime + offsetS, so a positive value shows later parts of that recording at the same moment. Read the current values from context.sources (each source has isMaster and its takes with id, offsetS and syncStatus).
The camera shows the mouth about 0.3 seconds before the voice is heard, so move the camera 0.3 seconds back from its current value (here from 0.27):
offsetSis the new value in seconds, not a change (within ±3600). Compute it fromcontext.sources. Sending the same value again isnoop.takeIdcan be left out when the source has one take. With several takes it is required, and a missing or unknown take is refused with the list of takes.- The reference audio source (
isMaster: true) always has offset 0 and is refused. Move the other sources instead. - The take becomes
syncStatus: "manual". The person can undo it in CutZal. It is listed incheckpoints, butrewinddoes not bring offsets back: to go back, send anotheroffsetedit with the previous value. Works before transcription too. previewonly says that the value changes. Check the result withcaptureat a moment where the mouth and the voice are clear.
Inserted videos
cutzal insert adds a video with its own sound to the edited video (see commands.md). Its part of source time comes after the end of the recording, and the edited video plays it at the insert point. Cut and restore it like any other part: a range cut with coordinate: "output" over the inserted block takes it out of the edited video. A content clip can be anchored to the inserted part in source time, and it follows that part when the person moves it.
Gain: the volume of an inserted video
gain sets the volume of one inserted video in dB. Inserted takes are the ones with inserted: true in context.sources, and their gainDb is the current value (0 is the original volume of the file). It starts at the value CutZal matched to the main audio when the video was inserted.
The inserted interview plays a little loud next to the talk, so make it 3 dB quieter than its current 0 dB:
gainDbis the new value, not a change: from −40 to +20 dB in steps of 0.1.0brings back the original volume. Sending the current value again isnoop.- Only inserted videos take a volume. Any other take is refused with
invalid_editand the list of inserted takes. To change the loudness of the whole video, use the person's target loudness setting in CutZal instead. - The same volume is used by playback in CutZal, by exports, and by the loudness measurement for the target loudness. The person can undo it in CutZal. It is listed in
checkpoints.previewonly says that the value changes.
What gets refused
- Clips on the same track that overlap in output time.
- Clip changes on a locked track.
- An asset kind that does not match the track (images and videos go on video tracks, audio on audio tracks).
- A
rewind-like edit (track.restore) or a drag transform (clip.transform): userewind, or put the clip again.
When an edit would change clips in a way the person should decide, preview shows status: "blocked" with conflicts, and apply returns the error blocked without applying. Change the edit or ask the person.
A full example: title card on a new project
cutzal context --project clip.mp4givesrevision: 0on a project that was just opened.- Add a video track (the
track.addabove) withapply --revision 0 --id add-graphics. The result hasrevision: 1. cutzal asset add --project clip.mp4 title.pngregisters assettitle. The revision is now 2 (runcontextto see it).- Put the title card (the first
clip.putabove) withapply --revision 2 --id put-title --label "Title card at 1 s". cutzal capture --project clip.mp4 --at 1.5and look at the PNG.
Captions on video
Use the existing transcript track and a track.update edit. Captions are off until explicitly enabled. Read the track ID from inspect rather than inventing a second track. The full caption object is required:
x and y are fractions of the canvas (0.05–0.95); size is a fraction of canvas height (0.02–0.09). appearance is outline, box, or plain (text only). Locked transcript tracks reject caption changes. Placement is clamped to keep the text in frame. Both preview and the composed video use these settings; source stems and SRT/VTT files remain separate. Burning captions re-encodes the composed video. No transcription or model call is started by this edit.
The caption look uses the same words as title text. These fields are optional; omit them to keep the default look (white text, #111111 outline or a black box at 80%):
weight:semiboldorblack. The outline is thicker withblack.color: text color,#RRGGBB.edgeColor: outline color, or box color withbox,#RRGGBB.boxOpacity: box opacity withbox, 0.2–1.shadow:trueadds a soft drop shadow.
The app offers four looks and names them by appearance, weight, and shadow only (colors and opacity can change without changing the look). To match one, send its values:
Lines wrap between words. A word longer than the line, or text without spaces, wraps by character.
Title text on the video
On-screen titles (for example the two-line header of a vertical short) are text clips, not images, so the person can edit the words in CutZal. Put one with clip.put on a video overlay track, with title instead of assetId: { "kind": "clip.put", "clip": { "id": "header", "trackId": "<overlay track>", "placement": "full", "anchor": { "kind": "output", "startS": 0, "endS": <end> }, "title": { "text": "first line\nsecond line", "style": { "x": 0.5, "y": 0.24, "size": 0.058, "colors": ["#F3EFE8", "#EC8A62"], "weight": "black", "shadow": true } } } }.
text: up to 3 lines (\n), 40 characters per line.colorsis one#RRGGBBper line; extra lines reuse the last color.weightisblackorsemibold.xandyare the center of the text block as fractions of the canvas (0.02–0.98);sizeis the letter height as a fraction of canvas height (0.02–0.12). A line wider than 88% of the canvas shrinks on its own.- Text clips take
placement: "full"only, and noassetId,sourceInS,endBehavior,frameormirror. To change the words, send the same clip again with a newtitle. Preview, capture and export draw the same image. - A project with a text clip is saved as file version 16. Older CutZal versions refuse to open it instead of showing the video without the title.
- Optional style fields:
sizessets the size of each line (same range assize; lines without a value usesize). Wrap words in asterisks, like*word*, to draw them inaccent(#RRGGBB, default#FF3B30); the asterisks are not drawn and do not count toward the 40 characters, and they must come in pairs.progress: { "y": <top of the bar as a fraction of canvas height>, "color": "#RRGGBB", "track": "#RRGGBB" }adds a bar that fills from left to right while the text clip is on screen. CutZal's Properties panel setstrackto the bar color darkened (each channel x 0.48). - Example: a two-line short header (small top line, big bottom line, key words in the accent color, progress bar under the text) on a 1080x1920 canvas is
{ "x": 0.5, "y": 0.2318, "size": 0.074, "sizes": [0.0344, 0.074], "colors": ["#F3F3EF"], "accent": "#FF3B30", "weight": "black", "shadow": false, "progress": { "y": 0.2943, "color": "#FF3B30", "track": "#7A1C17" } }with text likefirst line\n*key words* rest.
Subtitle term fixes
Transcripts spell product names the way they sound (for example "클라우드 코드"). To show the standard spelling in subtitles, set captionTerms on the transcript track with track.update: { "kind": "track.update", "trackId": "transcript", "changes": { "captionTerms": [{ "from": "클라우드 코드", "to": "Claude Code" }, { "from": "깃허브", "to": "GitHub" }] } }.
- Both sides need the same number of words. The last word keeps what follows it, so "클라우드 코드에서" becomes "Claude Code에서". Longer terms match first, up to 100 terms.
- Preview, burned-in captions and SRT/VTT use the same list. Word timings do not change.
"captionTerms": nullremoves the list.