# Skill: full/chapters — "נקודות ממוספרות" (Complete video)

A CapCut creator's listicle, measured on his Reels: every point the speaker makes opens with a white flash, its number on a yellow starburst and its name in big yellow serif at the top, held through the point; the captions sit on small yellow labels at the chest; a yellow serif title opens, a soft beat and his outro close. Gemini 3.5 Flash finds the points; one 720p render.

**Includes:** a flash into every point · the number on a yellow starburst · the point's name held at the top · captions on small yellow labels · a yellow serif title · his outro: the call to action word by word and a message card · soft music under the voice · pauses cut
**Built on:** kinetic `texts[]`, `panels`, `overlays` laid out by `POST /v1/studio/reel-style` (`"style": "chapters"`) — no caption line; the nearest single looks are hook `rise` and captions `marker-tape`
**Price:** 115–130 credits per finished minute · 30 s ≈ 64–74 credits
**Uses:** `/v1/speech/transcripts` · `/v1/studio/remove-silence` · `/v1/studio/reel-style` · `/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.5
Flash (`quality: "balanced"`) watches the video once and picks the moments (Flash-Lite was tried and could not place the face),
ElevenLabs makes two short sounds (each reused) and a music bed exactly as long as the video, the icons are free; 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 bed sits at 0.25 (0.2 in the review)
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 — the title, each subtitle, beat, pill,
chapter card, sticker, zoom and sound — is anchored to a word (`at` a word's id, or `during` a stretch of them).
The render finds the pauses in the sound itself (`remove_silence`, the same numbers every time), cuts them, and puts
each element on the exact output frame its word lands on; its answer reports where every one landed (`elements`:
exact / moved / lost). No second transcription of the cut audio, no reference transcript to put words back from. The
WAV is only where the planner measures the voice, to put the music ≈ 13 dB under it (16 dB in the review).

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

His listicle ("фишки для UGC", 73 s, 4 points): a flash into every point, the number on a yellow starburst and the point's name
held at the top through the point, the captions on small yellow labels at the chest.

## 2. The first plan — the numbered words and the picker call

```jsonc
POST /v1/studio/reel-style
{ "style": "chapters", "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, "text": "איזה", "start": 0.12, "end": 0.41 }, … ]   the numbered words (the output clock)
//   output.picker   { "system": "<the brief for this style>", "prompt": "The video is 57.24 s after the edit. Numbered words: …" }
//   output.summary / timeline / warnings   (every element of output.edit carries `at` / `during` on the words' ids)
```

It answers at once and costs a read request (1 credit).

## 3. Gemini watches the video and picks the moments

```jsonc
POST /v1/ai/text                           // Gemini 3.5 Flash ≈ 8–11 credits, ~35 s
{ "quality": "balanced", "json": true, "max_tokens": 8000, "temperature": 0.4,
  "system": "<output.picker.system>", "prompt": "<output.picker.prompt>",
  "media": [ { "url": "<video url>", "type": "video", "fps": 0.5 } ] }
```

It returns word numbers and short texts only: `corrections`, `face`, `title`, `chapters`, `stickers` (icon names), `cta` and `music_brief`.
**Why Flash and not Flash-Lite:** on the example Flash-Lite (1 credit) copied the example face box instead of measuring it
(the layout is placed around the face), merged two list items into one and wrote a word the speaker never said in the title;
Gemini 3.5 Flash measured the face within ≈ 3 % (the chin at 53–56 % against 58 % measured), kept the title to his words
and fixed a misheard brand ("לגרוגבוט" → "לגרוק בוט"). Give it `max_tokens` 8000 — it thinks first, and 3000 cut one answer short.

**Your pass before using it:** every on-screen text must be the speaker's own words (drop a title word he never said);
keep only corrections that repair a real mistake; the `face` box must hold the face (top = the top of the head, bottom = the
chin, % of the 9:16 frame) — if a thumbnail (`POST /v1/studio/thumbnail`) shows otherwise, fix it; drop a sticker whose
icon is not in the lists below.

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

```jsonc
POST /v1/ai/sound-effects { "text": "soft airy whoosh swipe transition, clean, short, no reverb tail", "duration": 0.7, "prompt_influence": 0.5 }   // ≈ 1 credit
POST /v1/ai/sound-effects { "text": "short soft UI pop click for a label appearing, clean", "duration": 0.5, "prompt_influence": 0.5 }              // ≈ 1 credit
POST /v1/stock/icon { "icon": "<a sticker's icon>", "size": 512, "padding": 4 }   // one per sticker · free (Fluent Emoji MIT, SVG Logos CC0, Skill Icons MIT)
POST /v1/stock/icon { "icon": "mdi:starburst", "color": "#FDD03E", "size": 512, "padding": 2 }   // the starburst behind the numbers
POST /v1/ai/music { "prompt": "<picks.music_brief>", "duration": <ceil(summary.output_seconds + 1)>, "instrumental": true }   // ≈ 0.77 credits a second
```

## 5. The final plan and ONE render

```jsonc
POST /v1/studio/reel-style
{ …the body of step 2…,
  …the picks as they came (without "music_brief"), each sticker with its "url" added…,
  "icons": { "burst": "<starburst png>" },
  "sounds": { "whoosh": "<mp3>", "pop": "<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) · output.warnings
POST /v1/studio/edit <output.edit>          // x-wait-ms: 90000, then poll GET /v1/executions/{id} · 720p ≈ 0.77 credits a second
```

## What the plan puts in the render

Measured on the creator's Reels (1 fps sheets of every video, full frames around every change, colours sampled from the
pixels, ebur128 and spectrograms of the sound):

| The look (measured) | How the plan does it |
|---|---|
| Title in big yellow **#FDFD62** condensed serif at the top, 0–5 s | `title` → Frank Ruhl Libre 900 / Playfair Display 900 in yellow with a dark edge, until the first point |
| A white flash into every point | a 0.08 s white panel + a whoosh |
| The number: black on a yellow **#FDD03E** starburst (≈ 28 % wide) | `icons.burst` (`mdi:starburst` in #FDD03E) 24 % wide on the reading side of the top band, the number in black serif on it, a pop |
| The point's name in big yellow serif at the top, held through the point | `chapters[].title` → Frank Ruhl Libre 900 in yellow, fitted to 62 % of the width (5–9.5 %), effect `rise`, until the next point; the framing alternates 1.08 / 1.0 per point |
| Captions: small black text on a yellow label | phrases of ≤ 3 words on #FDD03E labels, Karantina / Oswald 4.1 %, cut in |
| Every video of his ends the same way: a warm light-leak flash, the call to action ONE word at a time (heavy white, h 2.9 %, ≈ 0.7 s a word) and a white "message" card | `cta` (the first word of the call to action, only when he says one) → a #FFF3D6 flash (0.34 s), Heebo 900 / Montserrat 900 at 5 %, word by word, and the whole line in dark type on a white card for the last ≈ 2.6 s, a 1.1× punch, a whoosh |
| A soft beat under the voice that never stops: in the gaps the mix drops only 12–19 dB (a bed ≈ 13 dB under the voice), few effects | `music.volume` = 10^((voice LUFS + 5) / 20) from the WAV (−12 LUFS → 0.45, −18 → 0.22; 0.25 when it cannot measure), `duck: false`; a whoosh into the title / board / point / outro, pops on stickers and beats (≤ 26 hits from 2 files); `audio.normalize` → −16 LUFS |
| No pauses | `remove_silence` 200/70 from the audio |
| A warm, slightly contrasty grade | `color` contrast 1.05, saturation 1.05, temperature +0.12, vignette 0.2; 9:16, 720×1280 |

## Tune it

`chapters` (3–5, ≥ 4 s apart) · `icons.burst` (any transparent PNG) · `title` · `resolution: "1080"` · `speed` 1–1.25 · `cut: false` to keep every pause · `cut.from: "words"` for a transcript whose words
have real gaps. Every field of the returned `edit` can still be changed before the render.

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

Rounded label and pill corners (text boxes are square); a box behind ONE word of a line (his purple key word — the plan
boxes the whole phrase instead); glow on text; emoji inside text (✅ ❌ are stickers beside it); the speaker cut out of the
background or in a circle on a card; a screen recording in a phone mockup on a coloured card (overlays are drawn below
panels — his tutorial parts); hand-drawn marks (the red ring around a link, arrows); a real light leak (a warm flash 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, −12.2 LUFS — → 57.2 s,
720×1280. Five points — "API Hub", "בקרוב תהיה אפליקציה", "הסוכן מקבל וידאו", "תכין 30 רילסים", "כולל חיבור AI" — each with a flash, its number on the yellow starburst and its name at the top; 52 yellow-label captions, the Instagram logo and a brain emoji, and his outro; −15.8 LUFS. The pass changed "עשיתי עם API" (not quite his words) to "API Hub". Made with these steps on the dev server (tag `insta-chapters-car`, `GET /v1/usage?tag=…`):

| Step | Credits | Time |
|---|---|---|
| Upload (124 MB) | 1 | 1.2 min |
| Transcribe the original (Universal-2, 63 s) | 1 | 12–16 s |
| Pauses cut, sound only (WAV, 58 s) | 9 | 4–7 s |
| Transcribe the cut (Universal-2, 58 s) | 1 | 6–9 s |
| Plan ×2 (POST /v1/studio/reel-style) | 2 | 2–3 s |
| Gemini 3.5 Flash picks | 10 | 39 s |
| 2 sound effects + 5 icons | 2 | 5 s (parallel) |
| Music, 59 s | 45 | 10 s |
| Render (720p, 57.2 s) | 44 | 3.4 min |
| **Total** | **115** | **≈ 6 min** |

The music and the render are ≈ 75 % of it; without music a minute is ≈ 80 credits. A finished minute ≈ 118–126 credits,
a 30-second video ≈ 64–74. Until the render engine's join fix is deployed to the Lambda, each cut adds a few
milliseconds of silence to the voice, so near the end of a 55-second edit the words appear up to ≈ 0.15 s before they are said.

Stickers: brand logos `skill-icons:instagram`, `logos:facebook`, `logos:tiktok-icon`, `logos:youtube-icon`, `logos:whatsapp-icon`, `logos:telegram`, `logos:google-gemini`, `logos:openai-icon`, `logos:grok`, `logos:x`, `skill-icons:linkedin`, `logos:apple`, `logos:android-icon` and 54 Fluent emoji (`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.
