# Skill: full/director — "במאי AI" (Complete video)

The top of the range: the takes are cut and joined into one story, then Gemini watches and listens to the result — faces, pauses, what the words cannot say — and directs the whole edit: the hook, three generated clips on the moments the story shows (the one the customer asked for among them), kinetic words, punch-ins, flashes, sound design and an original score whose sections change exactly on the story's turns. One render, a finished video.

**Includes:** takes combined into one video · Gemini watches and directs the edit · 3 AI clips on the story's moments · a dramatic score timed to the story's turns · sound design · kinetic words · punch-ins and flashes · hook · captions corrected by ear · colour grade · optional CTA end card and banner
**Built on:** hook `punch` ("זום פאנץ׳", `/skills/hook-punch.md`) + captions `hormozi` ("הורמוזי", `/skills/captions-hormozi.md`)
**Price:** 2150–2300 credits per finished minute · 30 s ≈ 2050–2150 credits
**Uses:** `/v1/speech/transcripts` · `/v1/studio/trim` · `/v1/studio/merge` · `/v1/ai/text` · `/v1/ai/video` · `/v1/ai/music` · `/v1/ai/sound-effects` · `/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.** This template runs on the newest models: `quality: "best"` on every AI call — Veo 3.1 for clips,
Nano Banana Pro for images, ElevenLabs v3 for Hebrew voice. Each is priced per asset, so a 30-second video
costs almost as much as a 60-second one. `quality: "balanced"` is the cheaper cut of the same recipe.

---

## 1. Combine the takes (skip for a single take)

The customer often sends a story and a re-take of the part that went wrong ("the pitch came out bad, so I shot it
again"). Transcribe every take, find the cut points in the words, trim, then join whole clips.

Retakes INSIDE one recording ("רגע, אני אגיד שוב…", a sentence said twice, "אז אני… אז אני רוצה", "את את") need no
manual cut: add `"remove_retakes": true` to /v1/studio/edit (or short / remove-silence) — the engine finds them in the
words, keeps the last complete take of each (`{ "mode": "best" }`: the cleanest one) and never cuts a word said only
once; the render's `retakes` lists what it cut. Whole takes in separate files: merge them whole, then edit the merged
video with `"remove_retakes": { "window_s": <its length in seconds> }` — or send the files to the video agent and pick
"combine the best takes".

```jsonc
POST /v1/studio/uploads { "filename": "take1.mp4", "content_type": "video/mp4", "size_bytes": <bytes> }   // → output.upload_url (PUT the file) + output.url · the ORIGINAL file as recorded, never a resized copy (a vertical video resized to 1920×1080 renders stretched)
POST /v1/speech/transcripts { "audio_url": "<take url>", "language_code": "he", "speech_models": ["universal-3-5-pro", "universal-2"], "keyterms_prompt": ["<names, brands>"] }
// cut point = the silence between the last word you keep and the first word you drop:
// "ברורה" ends 83.45 s, "אז" starts 84.36 s → cut at 83.9 (half a second of breath)
POST /v1/studio/trim  { "video_url": "<take 1>", "start": 0, "end": 83.9, "output": { "resolution": "source", "quality": "high" } }
POST /v1/studio/merge { "clips": [ { "url": "<trimmed take 1>" }, { "url": "<take 2>" } ], "output": { "resolution": "source", "quality": "high" } }
POST /v1/speech/transcripts { "audio_url": "<merged url>", "language_code": "he", … }   → transcript_id (captions + timings)
```

Trim first and merge **whole** clips (no `start`/`end` inside the merge). Do not remove silences for a
fast talker — the few seconds saved are not worth jump cuts in a story. For two versions (with / without a
pitch) cut once and render twice: the shorter version is the same timeline trimmed.

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

## 2. Let Gemini watch the video and direct the edit

Gemini watches the merged video (picture + sound) — it sees faces, gestures and pauses, hears what the words
cannot say, and corrects the transcript. Give it the word timings too, for frame-accurate effects.

```jsonc
GET /v1/studio/hook-styles · GET /v1/studio/caption-styles · GET /v1/studio/text-styles   // the ids it may use
POST /v1/ai/text
{ "quality": "best",                                   // Gemini 3.1 Pro
  "system": "<the director brief below>",
  "prompt": "Watch and listen to the whole video, then return the edit plan JSON.",
  "media": [ { "url": "<merged url>", "type": "video", "fps": 2 } ],
  "json": true, "max_tokens": 32000, "temperature": 1 }
// → output.json = the plan · ≈ 38 credits for 100 s of video · ~70 s
```

The director brief — send it as `system`, with the video length, the customer's must-have shots and, at the end,
the word timings (`GET /v1/speech/transcripts/{id}` → `words`, ms ÷ 1000, as `word@seconds`):

```text
You are the creative director and senior editor of a top short-form studio (Reels / TikTok / Shorts). You WATCH and LISTEN to the attached vertical video and design its edit. Be bold and creative, but every choice must serve the story — restraint is part of the craft. Return JSON only.

THE VIDEO: <L> s. Times below are seconds on this video's timeline.

RETURN
{
 "beats": [ { "start": s, "end": s, "beat": "…", "emotion": "…", "intensity": 1-10 } ],
 "corrections": [ { "at": s, "wrong": "word as transcribed", "right": "the word actually said" } ],
 "hook": { "text": "…", "style": one of ["box_red","yellow","black","outline","clean","type","punch","glitch","marker","words","box","counter","shake","rise","strike","blur","flash"], "duration": 2.5-3.5 },
 "captions_style": one of ["classic","hormozi","karaoke","one-word","marker","news","neon","quiet","pop","typewriter","bounce","underline","elegant","handwritten","comic","sticker","drama","cinema","shadow","lime","retro","scale","bubble","sport","minimal","slide","volt","neon-glow","marker-tape","keyword-pop","keyword-box","editorial","poster","broadcast","soft","cards","cards-bold","cards-neon","kinetic-pop","fire","gold","glass","vhs","candy"],
 "grade": one of ["none","warm","cool","vivid","cinematic","teal-orange","bw","vintage","matte"],
 "clips": [ { "start": s, "duration": 4 | 6 | 8, "mode": "full" | "split", "prompt": "…", "why": "…" } ],
 "texts": [ { "start": s, "duration": 1.2-2.4, "text": "…", "effect": one of ["fade","pop","blur_in","slide_up","typewriter","highlight","punch","glitch","marker","words","box","counter","shake","rise","strike","flash"], "preset": one of ["hero","mega","accent","pill","pill-accent","marker","outline","small","script","type"], "y": 22-45 } ],
 "zooms": [ { "start": s, "end": s, "to": 1.05-1.3, "ease": "punch" | "smooth" } ],
 "flashes": [ { "start": s } ],
 "sounds": [ { "start": s, "prompt": "English, max 8 words", "duration": 0.5-3, "volume": 0.3-0.7 } ],
 "music": { "style": "overall sound, always instrumental, sits UNDER the voice", "sections": [ { "start": s, "end": s, "name": "…", "styles": ["…"], "avoid": ["…"] } ] },
 "notes": "one paragraph: the idea of the edit"
}

RULES
- Land every effect on the frame its word is said — take times from the word timings and from what you see.
- beats: the emotional map from what you SEE and HEAR (voice, face, gestures, pauses).
- corrections: speech-recognition mistakes you hear; keep slang and curses.
- HOOK (0–3 s): the strongest line, 3–7 words in the video's language, never inventing facts; wrap ONE payoff word in *asterisks*. Prefer an effect style (punch, rise, words, glitch…) — the classic boxes cannot colour a word.
- CLIPS: exactly 3, AI-generated, photoreal, cinematic, vertical 9:16:
  1. MUST show: <the shot the customer asked for, e.g. the below-average guy walks up to the most attractive woman at the bar and she turns him down>. Place it while it is being told, mode "full".
  The rest: the most visual moments of the story. Never over a punchline or the last line; at least 8 s apart; none before 3 s.
  prompt: English, 40–80 words: subject, action, setting, camera move, lens and light, mood; no real people, never the speaker; tasteful; end with "no text, no logos, no watermark".
- TEXTS: 5–9 kinetic words, 1–4 words each taken from what is said at that moment, at most one per 5 s, never during the hook; counter for money and numbers; accent one word with *asterisks*. y = % from the top (captions live at 65–90).
- ZOOMS: 4–8 — punch on surprises and contrasts, smooth on emotional lines; never during a "full" clip.
- FLASHES: at most 3 (before a full clip or on a hard contrast).
- SOUNDS: at most 12 — a whoosh 0.3 s before each clip, an impact on the hardest line, a hit on the turn, a riser into the payoff, soft pops on text effects.
- MUSIC: consecutive sections covering 0 → <L> exactly, each ≥ 3 s; borders land exactly on the story's turns (quiet tension in the set-up, a hit or drop on the conflict, a lift on the turn, a build to the payoff, a real ending). English styles: instruments, dynamics, tempo, energy.

WORD TIMINGS (word@start seconds): <הם@0.00 יושבים@0.22 בפאב@0.48 …>
```

A real plan it returned for the example (84 s, trimmed):

```json
{
 "hook": {
  "text": "הבחור שניגש לבחורה הכי *סקסית* בפאב",
  "style": "punch",
  "duration": 3
 },
 "captions_style": "hormozi",
 "grade": "cinematic",
 "corrections": [
  {
   "at": 14.94,
   "wrong": "דפוסה",
   "right": "תפוסה"
  },
  {
   "at": 70.71,
   "wrong": "מאמצים",
   "right": "אמיצים"
  }
 ],
 "clips": [
  {
   "start": 7.6,
   "duration": 8,
   "mode": "full",
   "prompt": "An ordinary, slightly awkward guy approaches a stunningly attractive woman at a lively, softly lit pub. She politely turns him down, body language slightly closed off. Cinematic lighting, intimate atmosphere, shallow depth of field, natural colors. no text, no logos, no watermark.",
   "why": "ניגש לבחורה הכי סקסית בפאב … מצטערת, אני תפוסה"
  },
  {
   "start": 77.2,
   "duration": 4,
   "mode": "split",
   "prompt": "A dull, monotonous office scene where uninspired workers sit passively at their desks in gray, dim lighting… no text, no logos, no watermark.",
   "why": "אנשים שיושבים בצד שקטים"
  }
 ],
 "texts": [
  {
   "start": 32.61,
   "duration": 2,
   "text": "האומץ זה הניצחון",
   "effect": "blur_in",
   "preset": "hero",
   "y": 25
  },
  {
   "start": 52.31,
   "duration": 2.2,
   "text": "התגובה של *עצמנו*",
   "effect": "highlight",
   "preset": "marker",
   "y": 30
  }
 ],
 "sounds": [
  {
   "start": 14.8,
   "prompt": "deep bass impact on rejection",
   "duration": 1.5,
   "volume": 0.5
  },
  {
   "start": 22.7,
   "prompt": "vinyl record scratch wow moment",
   "duration": 0.8,
   "volume": 0.5
  }
 ],
 "music": {
  "style": "Cinematic instrumental drama with rhythmic tension, evolving from subtle curiosity to a powerful emotional climax, always sitting under the voice.",
  "sections": [
   {
    "start": 0,
    "end": 14.8,
    "name": "Pub Setup",
    "styles": [
     "subtle tension",
     "curiosity",
     "minimal"
    ]
   },
   {
    "start": 14.8,
    "end": 22.7,
    "name": "The Question",
    "styles": [
     "suspense",
     "pause effect",
     "low drone"
    ]
   },
   {
    "start": 22.7,
    "end": 42.2,
    "name": "The Twist",
    "styles": [
     "uplifting",
     "driving rhythm",
     "hopeful"
    ]
   },
   {
    "start": 42.2,
    "end": 60.1,
    "name": "Self Reflection",
    "styles": [
     "serious",
     "introspective",
     "slow cello"
    ]
   },
   {
    "start": 60.1,
    "end": 72.1,
    "name": "The Haters",
    "styles": [
     "dark pulse",
     "edgy",
     "steady bass"
    ]
   },
   {
    "start": 72.1,
    "end": 83.9,
    "name": "The Contrast Climax",
    "styles": [
     "epic build",
     "staccato strings",
     "triumphant finish"
    ]
   }
  ]
 }
}
```

**Your pass as the editor** before generating anything: keep clip 1 on the told moment (start it on the verb —
"ניגש" — and let it run past the punch line it illustrates), make the most cinematic clip "full", drop any zoom
that overlaps a full clip, reuse one generation for repeated sounds (three whooshes = one call). Veo renders
1080p only at 8 s — 4 s and 6 s clips are 720p.

## 3. Generate everything in parallel

```jsonc
POST /v1/ai/video { "quality": "best", "prompt": "<clip prompt>", "aspect_ratio": "9:16", "resolution": "1080p", "duration": "8",
                    "negative_prompt": "text, captions, subtitles, watermark, logo" }        // Veo 3.1 8 s ≈ 960 credits · ~110 s
POST /v1/ai/video { …, "resolution": "720p", "duration": "4" }                                             // 4 s ≈ 480 credits · ~50 s
POST /v1/ai/music { "prompt": "<music.style> Hybrid orchestral drama; instrumental.", "instrumental": true,
                    "sections": [ { "name": "Pub Setup", "duration": 14.8, "styles": ["subtle tension", "curiosity"] }, … ],
                    "avoid": ["vocals", "lyrics", "EDM drops"] }   // durations = end − start; the track lasts exactly their sum · ≈ 0.75 credits/s · ~18 s
POST /v1/ai/sound-effects { "text": "deep bass impact on rejection", "duration": 1.5 }                     // ≈ 1–2 credits each
GET  /v1/speech/transcripts/{id}/captions   → the caption lines (ms); apply the plan's corrections to line and word text
```

## 4. One render

```jsonc
POST /v1/studio/edit     // x-wait-ms: 110000, then poll GET /v1/executions/{id}
{ "video_url": "<merged url>",
  "hook": { "text": plan.hook.text, "start": 0, "duration": plan.hook.duration, "style": plan.hook.style },
  "captions": { "style": plan.captions_style, "captions": <corrected caption lines> },
  "overlays": [ { "url": "<clip>", "type": "video", "start": 7.6, "duration": 8, "mode": "full", "fade_ms": 180 },
                { "url": "<clip>", "type": "video", "start": 77.2, "duration": 4, "mode": "split", "fade_ms": 180,
                  "split_side": "top", "split_ratio": 0.55, "speaker_focus": 0.3 } ],
  "texts":  [ { "text": "התגובה של *עצמנו*", "start": 52.31, "duration": 2.2, "effect": "highlight", "preset": "marker", "style": { "y": 30 } } ],
  "zoom":   [ { "start": 22.7, "end": 28.4, "to": 1.25, "ease": "punch", "y": 0.35 } ],
  "panels": [ { "start": 22.74, "duration": 0.12, "color": "#FFFFFF", "fade_ms": 30 } ],                   // the flashes
  "sounds": [ { "url": "<sfx>", "start": 14.8, "volume": 0.5 } ],
  "music":  { "url": "<score>", "volume": 0.26, "duck": false, "loop": false, "fade_in_ms": 400, "fade_out_ms": 250 },
  "color":  { "preset": plan.grade },
  "audio":  { "normalize": true },
  "output": { "aspect_ratio": "9:16", "resolution": "1080", "fit": "crop", "quality": "high" } }
```

**Music level:** a talking head speaks almost all the time — with `duck: true` the score would be pushed under
the voice for the whole video. `duck: false` at 0.26 keeps it ≈ 14 dB under the voice: heard, never in the way.
The score was written to the story's turns, so its hits land by themselves.

**Optional — a closing call to action and a banner** (the second version of the example):
- End card: `POST /v1/ai/voice` (the pitch, ElevenLabs v3), a Veo 8 s background, `POST /v1/studio/edit`
  on it with `"speed": 0.88` (stretches 8 s to 9.1 s), `"audio": { "mute": true }` and the voice as
  `voiceover` → merge it after the story → in the final edit add its captions (transcribe the voice, offset the
  words), texts on it, and a dark panel (`"color": "#000000", "opacity": 0.55`) behind the last line so the URL reads.
- Banner: one small text at the bottom, once in the middle —
  `{ "text": "נערך אוטומטית עם *API* *Hub*\napihub.co.il", "preset": "small", "effect": "slide_up", "exit": "fade",
  "style": { "y": 84, "size": 3.9, "weight": 800, "background": "#111111E6", "accent_color": "#FFD600", "max_chars_per_line": 40 } }`.

## 5. Check before you deliver

Grab frames at the hook, inside every clip and on every text (`POST /v1/studio/thumbnail { "video_url", "at" }`,
≈ 1 credit each) and look at them: the accent word must be visible on its box, the captions clear of the texts,
the clips on the right words. Re-render only what is wrong — a render of 84 s takes ~7 minutes.

## What the example cost and how long it took

84 s, two takes, three Veo 3.1 clips (8 s + 4 s + 4 s), a 6-section score, 8 sounds, one 1080p render:

| Step | Credits | Time |
|---|---|---|
| Upload + transcribe 2 takes | 6 | 10 s |
| Trim + merge | 186 | ~2 min |
| Transcribe the merged video | 3 | 8 s |
| Gemini watches and plans | 38 | 70 s |
| 3 Veo 3.1 clips | 1,920 | ~2 min (parallel) |
| Score (84 s, 6 sections) + 8 sounds | 72 | ~20 s (parallel) |
| Final render (1080p) | 84 | ~7 min |
| **Total** | **2,309** | **~13 min** |

The clips are 83% of the price. The same recipe with Veo 3.1 Fast (`/skills/full-director-fast.md`) made the
example for 749 credits.

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.
