---
title: "Plan a drawn motion-graphics explainer on a voice-over"
method: POST
path: "/v1/studio/explainer"
tags: ["Edit"]
---

# Plan a drawn motion-graphics explainer on a voice-over

`POST /v1/studio/explainer`

## Request

- Base URL: `https://apihub.co.il`
- URL: `https://apihub.co.il/v1/studio/explainer`
- Auth: HTTP bearer

Turns a script of beats and ONE voice-over into ONE ready body for POST /v1/studio/edit: a fully drawn 9:16 explainer with no camera — the look of the "how it works" reels made in After Effects. Each beat is one layout of the scene explainer-scene (a big title whose *marked* words turn into an italic serif in the accent, a caption with its key word on an accent pill, and a layout: `logo`, `versus` (the old way crossed out), `cards` lit one by one, a `counter` rolling, a `chat` typing a request and ticking its steps, an `app` screen with stat tiles and task cards, `devices`, a `graph` around a centre, a `site`, an `editor` with a playhead, the `end` card with a countdown ring) on a dark, cream, accent or lavender plate with soft glows. Make the voice-over from the beats' `say` lines joined by spaces (POST /v1/ai/voice with `timestamps: true`) and send its url and words: every beat starts on the first word of its line (matched by the letters, so a name read as one word does not shift them), a soft whoosh on every turn, a music bed when you give one. Nothing is rendered here. Recipe: /skills/full-motion-explainer.md

**Billing:** Read request — the plan is free; the render is POST /v1/studio/edit (the beats are effect seconds of explainer-scene) · **Rate limit:** 60/min

## Headers

- `Idempotency-Key` string
- `X-Tag` string

## Request body

- object
  - `font` string: The sans family (GET /v1/studio/fonts; default Heebo)
  - `beats` object[], required: The beats in order: one idea each — a layout (`kind`), its title / caption / items, and the line said over it (`say`)
    - `say` string, required: What the voice says during this beat — the beats' says joined by spaces are the voice-over's text
    - `done` string: end: the button's word once pressed
    - `from` number: counter: where it starts
    - `kind` 'title' | 'logo' | 'versus' | 'cards' | 'counter' | 'chat' | 'app' | 'devices' | 'graph' | 'site' | 'editor' | 'end': The layout of this beat
    - `name` string: app: the name in the greeting
    - `ring` string: end: the text in the countdown ring (1:00)
    - `rows` object[]: app: the task cards (label, tag chip, value = % done, note)
      - `tag` string
      - `note` string
      - `label` string, required
      - `value` string
    - `text` string: app: the greeting · site: the small tag · end: the card's line (*marks*)
    - `items` object[]: The layout's items: cards (label + icon), the chat's steps (label + value), the app's three stat tiles (label + value, tag = unit), devices, the network's nodes, the site's products (label + value = price), versus [the old way, yours]
      - `tag` string
      - `icon` 'video' | 'mic' | 'captions' | 'globe' | 'phone' | 'laptop' | 'chat' | 'sparkles' | 'chart' | 'music' | 'image' | 'megaphone' | 'check' | 'bolt' | 'clock' | 'user' | 'mail' | 'play' | 'wand' | 'terminal' | 'heart' | 'cart' | 'bell' | 'scissors' | 'layers' | 'send' | 'translate' | 'star' | 'calendar' | 'agent'
      - `label` string, required
      - `value` string
    - `label` string: The layout's own word: the logo's name, the network's centre, the site's brand, the editor's preview, the counter's small label, the app's section, the ending's small label, the chat window's name
    - `theme` 'dark' | 'cream' | 'accent' | 'lavender': The plate; omit it and the plan picks one for the layout, never the same twice in a row when it can help it
    - `title` string: The big title; *a phrase* = the accent in italic serif; \n = a new line
    - `typed` boolean: chat: the request is already in the box (the beat after the one that typed it)
    - `value` number: counter: where the number lands
    - `button` string: site / end: the button's word
    - `prefix` string: counter: before the number (₪, $)
    - `prompt` string: chat: the request typed into the box
    - `suffix` string: counter: after it (%, דקות)
    - `address` string: site: the address shown in the bar (only shown, never opened)
    - `caption` string: A short line lower down; *a word* = on an accent pill
    - `decimals` integer: counter: decimals
  - `music` object: A bed under the voice (POST /v1/ai/music, instrumental) — about 18 dB under it at the default
    - `url` string, uri, required
    - `volume` number
  - `serif` string: The accents' serif (default Frank Ruhl Libre)
  - `accent` string: The accent of the whole video: marked words, pills, highlights
  - `sounds` boolean: A soft whoosh on every turn of the picture (the CC0 kit)
  - `logo_url` string, uri: Your logo (PNG / JPG): on the logo and versus beats
  - `voiceover` object, required: ONE voice-over that reads every beat's `say`, in order (the text = the says joined by spaces)
    - `url` string, uri, required: The voice-over: POST /v1/ai/voice → output.url
    - `words` object[], required: Its words with times in seconds: POST /v1/ai/voice with `timestamps: true` → output.words
      - `end` number, required
      - `text` string, required
      - `start` number, required
    - `duration_seconds` number: Its length (output.duration_seconds)
  - `resolution` '720' | '1080'

## Response `200`

Completed

- object
  - `id` string: Execution id — stable across internal retries; use it with GET /v1/executions/{id}
  - `status` 'pending' | 'processing' | 'completed' | 'partial' | 'failed' | 'cancelled' | 'timeout'
  - `operation` string
  - `tag` string, nullable: Your X-Tag, if sent
  - `created_at` string, date-time
  - `completed_at` string, date-time, nullable
  - `output` object: Operation result (see each operation). While a Studio render is processing: `stage` (queued · transcribing · downloading · preparing · cleaning_voice · mixing_audio · rendering · finishing), `phase` (the engine's: download · prepare · voice · audio · encode · qa · upload, or null), `progress` (a percent, 0–100) and `progress_fraction` (0–1), `eta_seconds` (seconds left, never below 0) and `estimate_seconds`
  - `error` string, nullable
  - `usage` object
    - `credits_charged` number, nullable
    - `credits_reserved` number
    - `estimated_credits` number
    - `cost_source` 'provider_reported' | 'calculated' | 'estimated' | 'null', nullable
    - `provider_usage` object, nullable: Normalised consumption, e.g. { VIDEO_SECOND: 22.4 }
  - `balance` object
    - `available` number
    - `reserved` number

## Other responses

- `400`: Invalid input
- `402`: Insufficient credits / project budget
- `404`: Resource not found (or not yours)
- `429`: Rate limited
- `503`: Service temporarily paused

## Changes

- **2026-10-19** `a8caa42835ce`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/apihub/apis/apihub-one-api-for-chat-image-video-ai/changes/v1/studio/explainer/post.md)

---

[API](https://skmtc.dev/apihub/apis/apihub-one-api-for-chat-image-video-ai.md) · [All operations](https://skmtc.dev/apihub/apis/apihub-one-api-for-chat-image-video-ai/llms.txt) · [OpenAPI document](https://skmtc.dev/apihub/apis/apihub-one-api-for-chat-image-video-ai/revisions/a8caa42835ce?raw)
