# ClipOnIt API — guide for AI agents > ClipOnIt turns a long video into short, subtitled, reframed clips. REST API under `/api/*` (OpenAPI 3.1 at `/openapi.json`, interactive docs at `/docs`) and an MCP server at `POST /mcp` (discovery at `/.well-known/mcp.json`). Base URL: https://cliponit.app ## Auth - API key: `Authorization: Bearer ck_…`. Get one with `POST /api/me/api-key` (returns the key once; Starter plan or above, else 403). The user creates it in the app under Settings → API, or you can log in with `POST /api/auth/login {email,password}` → `{token}` and use that token as Bearer (30 days). - MCP: same header on `POST /mcp` (JSON-RPC 2.0, streamable-http; JSON responses, SSE only for agent-job waits with a progressToken). 401 without key, JSON-RPC `-32003` if the plan has no API access (free plan). 70 tools, each with `annotations` (readOnlyHint / destructiveHint / idempotentHint / openWorldHint): ask the user before calling a destructive one. A tool error comes back as `isError: true` with text `HTTP : [what to do]`; an unknown tool name is JSON-RPC `-32602`. - MCP via OAuth 2.1 (Claude.ai / ChatGPT connectors): no key needed. The 401 carries `WWW-Authenticate: Bearer resource_metadata=…`; discovery at `/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server`, dynamic client registration `POST /oauth/register`, PKCE S256, access tokens `cko_…` (1 h) + refresh (90 d, rotated). Same plan rules as the API key. - Chrome extension: `Authorization: Bearer cke_…`, minted by `POST /api/extension/token` from the signed-in website only (session cookie, not a Bearer). It is a portable session, so it works on **every plan including free** (unlike `ck_…`), slides 180 days forward on each call, and is listed/revoked with `GET /api/me/extensions` and `DELETE /api/me/extensions/{id}`. - Never put the key in a URL. ## Typical flow 1. `GET /api/me` → read `quota` (`videos_used`/`videos_limit`, feature booleans). 2. `POST /api/videos/url {url}` (or multipart `POST /api/videos` with `file`) → video `{id, status:"processing"}`. 3. Poll `GET /api/videos/{id}` every ~10 s until `state` is `ready` (or `failed` → read `error`; `POST /api/videos/{id}/retry`). `state` is `queued` (with `queue_position`) or `running`, `eta_s` is the estimated seconds left (~0.25 × duration). Processing takes ~1-5 min for 20 min of video. Over MCP use `wait_video` instead (server-side long-poll, one call). 4. `clips[]` sorted chronologically; choose by `score` (0-100) and `reason`. Need another moment? `POST /api/videos/{id}/search {query}` creates a clip. 5. Optional edits on a clip (one edit per `?orientation=vertical|horizontal`): - `PATCH /api/clips/{id} {start_s, end_s}` to trim (absolute seconds, ≥ 3 s; pick word boundaries from `GET /api/clips/{id}/transcript`) - `POST /api/clips/{id}/auto-reframe` (faces → layout/keyframes, synchronous, up to 2 min), THEN `POST /api/clips/{id}/auto-zoom` (a later auto-reframe overwrites zoom keyframes) - `POST /api/clips/{id}/silences` → `{cuts}` then `PUT /api/clips/{id}/edit {cuts}` (merge with `edit.cuts` from `GET /api/clips/{id}` if you had some) - `PUT /api/clips/{id}/edit` is a partial patch: `{subtitles:{style:"boxed"}}` changes only that field - `POST /api/videos/{vid}/clips/{id}/description`, B-roll/music/emojis (Starter+) - `DELETE /api/clips/{id}` removes a clip (e.g. one created by search) 6. Subtitle defaults for every new clip edit: `PUT /api/me/subtitle-preset {style, color, size, words_per_line…}` once per account (merged over the built-in defaults, `GET` to read). 7. `POST /api/clips/{id}/export?orientation=vertical` → `{id, status:"queued", eta_s}`, or `POST /api/videos/{vid}/export-all?orientation=vertical` → `{exports:[{clip_id, export_id}]}` (one export per clip). Poll `GET /api/exports/{id}` every ~5 s (`eta_s`, `progress`) until `done` → download `url` (`/files/exports/.mp4`, prefix with the base URL). `GET /api/exports/{id}/frame?t=3` returns a 360 px JPEG to check framing/subtitles. `DELETE /api/exports/{id}` cancels a queued or running export (`status: canceled`). Over MCP use `wait_export`, `export_all_clips`, `get_export_frame`, `cancel_export`. ## Units and conventions - Times are seconds (float). Clip `start_s`/`end_s`, `cuts`, B-roll and emoji ranges: absolute in the source video. Keyframe `t`: relative to the clip start. - Zones `{x,y,w,h}` and emoji `x,y`: fractions 0..1. Layouts: vertical (1 zone), split (2), trio (3), spotlight (1), centered (1), horizontal (0 = full frame). - `PUT /edit` is a patch: every field optional, absent = unchanged; `subtitles` merged field by field; `keyframes`/`cuts`/`broll`/`emojis` replace their list; `music: null` removes it. The edit row (and the account subtitle preset) is created on first access. - Subtitles: `style` karaoke|boxed|outline|glow|minimal, `color` yellow|white|green|cyan|pink|orange|rainbow, `position` bottom|middle|top, `size` 1|2|3, `words_per_line` 3..8, `lang` "original" or ISO code. ## Errors (`{"error": "message"}`) - 400 validation (bounds, enums, zone counts) — the message names the field (`champ subtitles.size invalide : attendu 1..3`); fix the payload, do not retry blindly. - 401 missing/invalid key — ask the user for a key. - 402 quota reached: monthly videos, 5 searches/day on free, file too big/long on free — stop and tell the user to upgrade at https://cliponit.app/billing. - 403 feature not in plan: API/MCP (Starter), translation/caption overrides/B-roll/music (Starter), Premiere XML (Pro), exporting the demo video (free) — tell the user, do not retry. - 404 not found or not owned by this key's user — the message names the resource and id (`clip introuvable…`). - 5xx transient (face model missing, ffmpeg) — retry once after a few seconds. ## Links - OpenAPI: https://cliponit.app/openapi.json - Docs: https://cliponit.app/docs - MCP: https://cliponit.app/.well-known/mcp.json (endpoint https://cliponit.app/mcp) - Plans: https://cliponit.app/pricing - Affiliate program (30% for life): `GET /api/me/affiliate` → `{ref_code, link, signups, paying, pending_cents, paid_cents, payouts}`; share `link`, sign-ups within 60 days are attributed. Public page: https://cliponit.app/affiliates ## Formats & hook (v2026-10) - orientation: vertical 1080x1920 | horizontal 1920x1080 | square 1080x1080 | portrait45 1080x1350. One independent edit per orientation; auto_reframe supports vertical|horizontal only (square/portrait45 start from a centered crop, use update_clip_edit keyframes to move it). - subtitles.hook: {enabled, text (null = clip title, max 120 chars), duration_s 0.5..10 (default 2.5), style card|plain} — title card burned at the top during the first seconds. Set it via update_clip_edit or set_default_subtitles before export. - auto_zoom anchors the punch-in on the mean face box of the clip when faces were detected (run auto_reframe first). ## Explainer videos (v2026-10) Fully generated vertical videos (voice-over, pixel-art or dithered low-poly 3D scenes) from a JSON script that references catalogue elements by name — you never draw anything. Two styles, chosen by `script.style`: `pixel-news` (default, 2D pixel art panel + HUD + subtitles) and `archibald` (full-screen low-poly 3D, moving camera, red highlights, title/number at the top). Contract: docs/explainer.md. - `GET /api/explainer/styles` → `[{id, name, description, theme, scene_fields, video_fields, camera_presets, catalogue, library, safe_area}]`; `safe_area` = usable area in panel coordinates (pixel-news e.g. `{x:[0,180], y:[-18,150]}`: subtitles above, caption/credit below) — keep elements inside. - `GET /api/explainer/library?style=&compact=1` → `{style, text}`: that style's catalogue (~2-3k tokens). Read it once. `?all=1` → merged catalogue (one entry per concept, one variant per style). Animated previews: `/explainer-assets/