# Skill: full/kinetic — "עריכה קינטית בסגנון CapCut" (Complete video)

The trending CapCut Reel, modelled frame by frame on two reference videos: no subtitle line — every word lands on the frame it is said, in small cards that build up and clear (small connector words, one big key word in a rotating display font, acid lime on every other card), a big hook, punch zooms on a jump-cut rhythm, black title cards with a white flash on the thesis, emoji stickers, a small reused sound kit and a beat under the voice. On the cheapest models — two Universal-2 transcripts, one Gemini Flash-Lite call, four sound effects, one track — and one 720p render.

**Includes:** every word on screen the moment it is said · big key words in rotating display type, lime accents · a punch-zoom rhythm · black title cards with a flash · emoji stickers · sound design · music under the voice · pauses cut
**Built on:** kinetic `texts[]` (effects `words`, `punch`, `blur_in`, `rise`, `glitch`) laid out by `POST /v1/studio/kinetic` — no caption line; the nearest single looks are hook `words` and captions `volt`
**Price:** 110–120 credits per finished minute · 30 s ≈ 60–68 credits
**Uses:** `/v1/speech/transcripts` · `/v1/studio/remove-silence` · `/v1/studio/kinetic` · `/v1/ai/text` · `/v1/ai/sound-effects` · `/v1/stock/icon` · `/v1/ai/music` · `/v1/studio/edit`

Base URL: `https://apihub.co.il` · Auth: `Authorization: Bearer <API key>` · Docs: `/docs`
Send the same `X-Tag: <job name>` header on every call of one video; `GET /v1/usage?tag=<job name>` then
returns exactly what it cost. Times in request bodies are **seconds on the edited (output) timeline**.
Unknown fields are rejected with the name of the right field — read the error instead of guessing.
**Files are kept 24 hours** (renders, uploads, generated clips/images/music) — download the finished video.

**Models — the cheapest that do the job.** AssemblyAI Universal-2 transcribes twice, Gemini 3.1
Flash-Lite (`quality: "fast"`) picks the moments, ElevenLabs makes four short sounds (each reused) and a music bed exactly as long as
the video, the stickers are free emoji; no AI video or images. Everything is priced by the video's length.

---

## 1. Transcribe the video (once)

```jsonc
POST /v1/studio/uploads { "filename": "clip.mp4", "content_type": "video/mp4", "size_bytes": <bytes> }   // a local file → output.url (skip for a public link) · the original file, never a resized copy
POST /v1/speech/transcripts { "audio_url": "<video url>", "language_code": "he", "speech_models": ["universal-2"] }
// → T0, the video's words · Universal-2 · 64 s ≈ 1 credit
// optional — the music level: a WAV of the voice for the planner to measure (`cut_audio_url`); without it the music sits at 0.3
POST /v1/studio/convert { "video_url": "<video url>", "output": { "format": "wav" } }                      // x-wait-ms: 90000 · ≈ 0.15 credits a second
```

**One transcript, on the video's own clock.** Every element the planner writes is anchored to a word — `at` a word's
id (w0001…) or `during` a stretch of them — and the render does the rest: it finds the pauses in the sound itself
(`remove_silence`, the same numbers give the same cut every time), cuts them, and puts each text, zoom, sticker and
sound on the exact output frame its word lands on. Nothing is timed on a guessed cut clock, so the cut audio is never
transcribed a second time and there is nothing to put back from a reference transcript. The render's answer reports
where every element landed (`elements`: exact / moved / lost — a word that was cut moves its element to the next
kept word of the phrase; one with nothing left to hold on to is dropped and named).

**Why a WAV (optional).** The planner measures the voice's loudness in it (`cut_audio_url`) and sets the music
≈ 10 dB under the voice — a loud car recording (−12 LUFS) needs twice the music volume of a quiet one (−18).

**Everything below is timed on the cut video's clock.**

## 2. The first plan — free: the numbered words and the length

```jsonc
POST /v1/studio/kinetic
{ "video_url": "<video url>", "transcript_id": "<T0>", "cut": { "from": "audio", "min_pause_ms": 200, "padding_ms": 70 },
  "cut_audio_url": "<the wav's url>" }                                   // cut_audio_url optional (the music level)
// → output.words    [ { "i": 0, "id": "w0001", "text": "אין", "start": 0.17, "end": 0.76 }, … ]   the numbered words (the video's clock)
//   output.summary  { "source_seconds": 62.1, "output_seconds": 62.1, "cards": 51, "texts": 79, … }   the render shortens it by the pauses
//   output.timeline [ { "t": 0.17, "what": "hook \"אין כזה דבר\"" }, … ]
```

It answers at once and costs a read request (1 credit). Optional `"speed": 1.1`: the references talk 3.5–4.5 words a
second; a much slower speaker reads tighter 10 % faster (the pitch is kept) — off by default. In `output.edit` every
text is `"during": { "from": "w0012", "to": "w0015", "pad_start_ms": 30, … }`, every sticker and sound `"at": { "word": …, "edge": "start", "offset_ms": -60 }`
— the same `at` / `during` any `POST /v1/studio/edit` element accepts, next to the video's `words` with their ids.

## 3. Gemini Flash-Lite picks the moments

```jsonc
POST /v1/ai/text                           // Gemini 3.1 Flash-Lite ≈ 1 credit, ~4 s
{ "quality": "fast", "json": true, "max_tokens": 3000, "temperature": 0.4,
  "system": "<the picker brief below, verbatim>",
  "prompt": "The video is <summary.source_seconds> s (its pauses will be cut). Numbered words:\n0:אין 1:כזה 2:דבר, 3:אני …",
  "media": [ { "url": "<video url>", "type": "video", "fps": 0.5 } ] }
```

```text
You pick the moments for a fast CapCut-style kinetic-typography edit of a vertical talking-head video. Every spoken word will appear on screen as it is said; you choose which words get the big type, where the emoji stickers go and which line becomes a full black title card. You WATCH and HEAR the attached video and get its transcript as numbered words (number:word). Return JSON only:
{
 "corrections": [ { "word": n, "text": "the word actually said" } ],
 "emphasis": [ n, … ],
 "scenes": [ { "word": n } ],
 "stickers": [ { "word": n, "icon": "…" } ],
 "music": "…"
}
RULES
- corrections: only a word that is clearly wrong — you hear something else, or it cannot be right in its sentence (e.g. the verb's person does not match its subject: "אני … יתמיד" → "אתמיד"). Never "fix" the speaker's own style, slang or prefixes (colloquial forms like "בלהתמיד", "בלמכור" are what he says). The text is what is said there, in the video's language; "" removes a word that was not said (one word heard as two: "וידאו עם" → "וידאוים", ""). When unsure, leave it.
- emphasis: the words that carry the meaning — a strong noun or verb, a number, a contrast, an emotion — about one in every four words, never two neighbours, never small function words.
- scenes: 1 for a video under 40 s, else 2: the thesis or the punchline the video builds to (a short line, 2–6 words) — never in the first 5 words, never the last 3 words.
- stickers: 5–8 concrete words a small emoji can illustrate (money, pain, a goal, laziness, time…), spread over the video — at least 10 words apart, not in the first 6 words, not inside a scene line. icon: one of ["fire","rocket","money-bag","dollar-banknote","chart-increasing","chart-decreasing","brain","flexed-biceps","bullseye","light-bulb","hourglass-not-done","alarm-clock","stopwatch","warning","cross-mark","check-mark-button","red-heart","broken-heart","eyes","thinking-face","face-screaming-in-fear","face-with-steam-from-nose","face-with-rolling-eyes","pensive-face","crying-face","sleeping-face","party-popper","trophy","1st-place-medal","gem-stone","key","locked","puzzle-piece","magnifying-glass-tilted-left","megaphone","mobile-phone","laptop","person-running","person-lifting-weights","handshake","hundred-points","sparkles","high-voltage","collision","skull","roasted-sweet-potato","couch-and-lamp","bed","hamburger","shopping-cart","gear","mountain","snail","turtle"].
- music: one sentence in English for an instrumental bed under the voice that fits the mood: genre, tempo in BPM, 3–4 instruments, energy; a clear beat (drums, 808 or claps) and a simple hook, never pads-only; "no vocals, stays under the voice".
```

What it returned for the example:

```json
{
 "corrections": [
  {
   "word": 146,
   "text": "בקיצור"
  }
 ],
 "emphasis": [
  1,
  7,
  17,
  20,
  27,
  33,
  44,
  52,
  70,
  81,
  91,
  111,
  116,
  134,
  151
 ],
 "scenes": [
  {
   "word": 44
  },
  {
   "word": 126
  }
 ],
 "stickers": [
  {
   "word": 17,
   "icon": "brain"
  },
  {
   "word": 33,
   "icon": "rocket"
  },
  {
   "word": 52,
   "icon": "gear"
  },
  {
   "word": 81,
   "icon": "laptop"
  },
  {
   "word": 111,
   "icon": "chart-increasing"
  },
  {
   "word": 134,
   "icon": "sparkles"
  },
  {
   "word": 151,
   "icon": "key"
  }
 ],
 "music": "Upbeat lo-fi hip hop, 90 BPM, clean kick drum, crisp snare, warm synth chords, no vocals, stays under the voice."
}
```

**Your pass before using it:** keep the corrections that repair a real mistake and drop any that change the speaker's
own style (on another video the cheap model "fixed" a woman's quoted "מצליחה" to "מצליח"). Where a word looks wrong and
the picker left it, listen once — `POST /v1/ai/text` with `"media": [{ "url": …, "type": "video", "start": s, "end": s }]`,
"write exactly what is said", ≈ 1 credit — and add your own correction: on the example "לגרוגבוט" was "לגרוק בוט" and
"וידאו עם" was one word, "וידאוים" (the second word removed with `"text": ""`). The picks themselves were right: "יש כלי
כזה!" and "בשבילכם" as the black cards, a rocket on "אוטומטי", a brain on "כתוביות".

## 4. The assets — in parallel, each generated once

```jsonc
POST /v1/ai/sound-effects { "text": "fast short airy whoosh swipe transition, clean, no reverb tail", "duration": 0.6, "prompt_influence": 0.5 }   // ≈ 1 credit
POST /v1/ai/sound-effects { "text": "short soft bubble pop click for a sticker appearing, clean", "duration": 0.5, "prompt_influence": 0.5 }
POST /v1/ai/sound-effects { "text": "short digital glitch stutter burst, electronic, clean", "duration": 0.5, "prompt_influence": 0.5 }
POST /v1/ai/sound-effects { "text": "one second tension riser swell that stops abruptly, no hit", "duration": 1.0, "prompt_influence": 0.5 }
POST /v1/stock/icon { "icon": "fluent-emoji-flat:<sticker icon>", "size": 512, "padding": 4 }   // one per sticker · free (Fluent Emoji, MIT — no credit line)
POST /v1/ai/music { "prompt": "<picks.music>", "duration": <ceil(summary.output_seconds + 1)>, "instrumental": true }   // ≈ 0.77 credits a second
```

The four sounds are the whole kit: the plan reuses each file for every moment of its kind (≈ 20 hits from 4 files).

## 5. The final plan and ONE render

```jsonc
POST /v1/studio/kinetic
{ …the body of step 2…,
  "corrections": <picks.corrections you kept>, "emphasis": <picks.emphasis>, "scenes": <picks.scenes>,
  "stickers": [ { "word": 17, "url": "<icon png>" }, … ],
  "sounds": { "whoosh": "<mp3>", "pop": "<mp3>", "glitch": "<mp3>", "riser": "<mp3>" },
  "music": { "url": "<music mp3>" } }                  // the volume comes from the measured voice
// → output.edit — the complete body of the render, output.timeline — what happens when (check it)
POST /v1/studio/edit <output.edit>          // x-wait-ms: 90000, then poll GET /v1/executions/{id} · 720p ≈ 0.75 credits a second
```

The same inputs always give the same plan (`seed` changes the rotation of fonts, places and effects).

**The music level, measured.** The track is levelled to −18 LUFS, then `volume`. The references keep their beat 8–10 dB
under the voice, so the planner sets `volume` = 10^((voice LUFS + 8) / 20) from the WAV: −18 LUFS → 0.32, −15.9 → 0.40,
−12.2 → 0.62 (0.3 when it cannot measure; a `volume` you send wins). A fixed 0.3 left the example's music 16.8 dB under
its loud voice (measured in the render) — hardly there. Never `duck` a talking head — the bed disappears.

## What the plan puts in the render (the style, measured on the two reference Reels)

| The look | How the plan does it |
|---|---|
| No subtitle line: each word on the frame it is said, cards of 1–3 words (≈ one card a second) | `texts[]`: the phrase's small words (Heebo 800, 4.2 %, effect `words`) + ONE key word (≈ 60–75 % of the width) + an optional small tail, all clearing together; a card beyond the render's 80 texts becomes one line with the key word in the accent colour |
| Key words in rotating display type, lime `#D0F808` on every other card | heavy (Heebo 900 · Montserrat 900), condensed (Karantina · Oswald), script (Amatic SC · Caveat — only in lime); entrances `punch`, `blur_in` (the tracking-in), `rise`, `glitch`, `slide_up`; exits `smear` / `fade` |
| Words under the face, never over it | the band from under the (zoomed) chin to 84 % of the height; `face` moves it |
| The hook is the first spoken line, big | the first phrase in the condensed face, word by word, over the head, on a 1.22× push-in that eases out |
| A framing change every ≈ 2.2 s | `zoom`: punch levels 1.0 / 1.14 / 1.2 / 1.08 / 1.17 on the face, one slow push per ≈ 15 s |
| Flash / glitch cuts | a 0.07 s white flash (55 %) on every fourth punch, `glitch` key words with a glitch sound |
| Black title cards (20–30 % of ref 1) | `scenes`: a `#080808` panel over the thesis line (1.2–2.5 s, the voice goes on), a white flash into it, a riser before it |
| Emoji stickers next to the words (1 per ≈ 5 s) | `overlays` pip 22 % wide beside the face, tilted ±8°, floating; a pop each; never on a black card |
| Sound design ≈ 2–4 hits per 10 s | whoosh on the open, the cards and every other punch; pop on stickers; glitch; riser — ≤ 26 hits |
| A beat ≈ 10 dB under the voice, no pumping | `music.volume` from the voice measured in the WAV, `duck: false`; `audio.normalize` → −16 LUFS |
| No pauses (the references: none over 0.45 s) | `remove_silence` 200/70 from the audio (+ `speed` for a slow talker); every element rides on its word through the cut |
| Readable on a light shirt | a 4 % dark edge + drop shadow on every word; a slightly darker, contrastier grade with a vignette |

## Tune it

`seed` (another rotation) · `face` { `x`, `top`, `bottom` } — where the face is in the 9:16 frame (default a phone selfie:
10–55 %) · `speed` 1–1.25 · `accent` (default `#D0F808`; the second reference used `#F0F830`) · `hook: false` ·
`resolution: "1080"` (25 % more for the render) · `cut: false` to keep every pause · `cut.from: "words"` for a transcript
whose words have real gaps (the plan then cuts itself and sends `keep` spans; the anchors resolve through those spans the
same way). Stickers can be any transparent PNG.

## What the engine cannot do yet (so the plan does not try)

The speaker shrinking into a circle or a rounded card on a background; glitch / blur / pixel transitions on the picture
itself; a glow on kinetic texts; letter-by-letter typing; text behind the speaker; textured backgrounds. The plan uses
black cards, flashes, glitch words and punch zooms instead.

## What the example cost and how long it took

The example on /skills: the owner talking in his car — 62 s, Hebrew, one take, 1080×1920 HEVC, a loud −12.2 LUFS
recording — → 57.3 s, 720×1280, −15.8 LUFS, the beat 10.4 dB under the voice (music volume 0.63 from the measured voice).
Made with the recipe of 27.9.2026 on the dev server (`GET /v1/usage?tag=…`); since 28.9 the plan is anchored to the
video's own transcript, so the two rows marked † are no longer needed — ≈ 10 credits less per minute (the cut
transcript, and the sound-only pass unless you want the music level measured from it):

| Step | Credits | Time |
|---|---|---|
| Upload (124 MB) | 1 | 1.5 min |
| Transcribe the original (Universal-2, 63 s) | 1 | 11 s |
| Pauses cut, sound only (WAV, 58 s) † | 9 | 4 s |
| Transcribe the cut (Universal-2, 58 s) † | 1 | 9 s |
| Plan ×2 (POST /v1/studio/kinetic) | 2 | 2–3 s |
| Gemini 3.1 Flash-Lite picks | 1 | 28 s |
| 4 sound effects + 7 emoji stickers | 4 | 4 s (parallel) |
| Music, 59 s | 45 | 9 s |
| Render (720p, 57.3 s) | 44 | 2.7 min |
| **Total** | **108** | **≈ 5 min** |

A finished minute ≈ 113 credits: the music ≈ 47, the render ≈ 46, the silence pass ≈ 9, the rest ≈ 11. A
30-second video ≈ 64. The music is the biggest single item; without it (`music` omitted) a minute is ≈ 66 credits.
(The first test, a 64-second WhatsApp video at 1.1×, came to 102 credits.)

**Until the render engine update is deployed to the Lambda** (`deploy-render.sh`, the join fix of 24.9), each cut of a
render adds a few milliseconds of silence to the voice, so on a 50-second edit the words land up to ≈ 0.15 s after their
text appears (measured on the example). After the deploy the lead is the planned 0.03 s.

Stickers from the list: 54 Fluent emoji (`GET` any of them as `fluent-emoji-flat:<name>`).

Send `x-wait-ms: 110000` to wait for the render in the same call; if the response is still
`processing`, poll `GET /v1/executions/{id}` until `status` is `completed` and read `output.url`.

## Check the bill

`GET /v1/usage?tag=<job name>` — per call, per operation and the total, in credits. Quote that to the
customer, not the estimate above.

## Rules that keep captions readable on every platform

- **Safe zone.** Keep text inside the middle 80% of the frame: `side_margin` ≥ 6 handles the sides; `margin`
  ≥ 12 keeps the bottom clear of the platform UI (username, buttons, progress bar).
- **A line never leaves the frame** — a line too wide for the side margins is set smaller automatically. Do
  not shrink the size yourself, wrap instead (`max_chars_per_line`).
- **Hebrew has no capitals** — keep `uppercase: false`; it only breaks Latin words mixed into a sentence.
- **Fonts.** `GET /v1/studio/fonts` lists the bundled families and which scripts they draw. A font that
  cannot draw the caption's language is swapped and reported in `warnings`.
- The customer's transcript wins: to fix a word, `GET /v1/speech/transcripts/{id}/captions`, edit the text,
  and send the list back as `captions.captions` — timing is kept when the word count is unchanged.
