# Clippie API and MCP docs URL: https://clippie.ai/developers/docs The Clippie API makes AI Story videos, Clippie Recast videos, AI images and video, voiceovers and transcripts from your own code, at `https://api.clippie.ai/v1`. The MCP server at `https://api.clippie.ai/mcp` gives Claude, ChatGPT, Cursor and other AI clients the same tools. Both spend the same credits and minutes as the app, at the same prices. ## Quickstart 1. Create an API key in [Settings, API](https://clippie.ai/user/settings?tab=api) (paid plans). It starts with `clp_pat_` and is shown once. Put it in an environment variable, `CLIPPIE_API_KEY`, never in code. 2. Make your first call: `GET /v1/me` shows your plan, credits and minutes. 3. Check the cost: send the create call with `"dryRun": true`. Nothing is spent. 4. Create: the same call without `dryRun`, with an `Idempotency-Key` header. It answers `202 { job }` straight away. 5. Wait: `GET /v1/jobs/:id?wait=30` until the job is `completed`, or pass a `webhookUrl` and get `job.completed` posted to you. First call: ```bash curl https://api.clippie.ai/v1/me \ -H "Authorization: Bearer $CLIPPIE_API_KEY" # => { "user": { ... }, "plan": { ... }, "credits": { "balance": ... }, "minutesLeft": { ... }, "watermark": { ... } } ``` ai-story.sh: ```bash # 1. Create the video. Send the same Idempotency-Key if you retry, # so a retry returns the first job instead of charging twice. # Add "dryRun": true to see the exact cost without spending anything. curl https://api.clippie.ai/v1/videos/ai-story \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 5d1c0e2a-7c4f-4a51-9b1e-2f0c6f1d9a77" \ -d '{ "prompt": "The lost library of Alexandria", "aspectRatio": "9:16", "durationSeconds": 60, "webhookUrl": "https://example.com/clippie-webhook" }' # => 202 { "job": { "id": "job_...", "status": "queued", ... } } # 2. Wait for it. ?wait= holds the request open for up to 50 seconds. curl "https://api.clippie.ai/v1/jobs/job_...?wait=30" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" # => { "job": { "status": "completed", "output": { "videoUrl": "...", "editorUrl": "..." } } } ``` ai-story.mjs: ```js import { randomUUID } from 'node:crypto'; const API = 'https://api.clippie.ai/v1'; const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const video = { prompt: 'The lost library of Alexandria', aspectRatio: '9:16', durationSeconds: 60 }; // Check the cost first. A dry run spends nothing. const dry = await fetch(`${API}/videos/ai-story`, { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json' }, body: JSON.stringify({ ...video, dryRun: true }), }).then((res) => res.json()); console.log(dry.estimate); // { credits, minutes, wouldSucceed, reason } // Make one key per video and reuse it on retries, so you are never charged twice. const idempotencyKey = randomUUID(); let { job } = await fetch(`${API}/videos/ai-story`, { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json', 'Idempotency-Key': idempotencyKey }, body: JSON.stringify(video), }).then((res) => res.json()); // Long-poll: each call waits up to 30 seconds for the job to change. while (!['completed', 'failed', 'canceled'].includes(job.status)) { ({ job } = await fetch(`${API}/jobs/${job.id}?wait=30`, { headers: auth }).then((res) => res.json())); } console.log(job.status, job.output?.videoUrl, job.output?.editorUrl); ``` ai_story.py: ```python import os, uuid, requests API = "https://api.clippie.ai/v1" auth = {"Authorization": f"Bearer {os.environ['CLIPPIE_API_KEY']}"} video = {"prompt": "The lost library of Alexandria", "aspectRatio": "9:16", "durationSeconds": 60} # Check the cost first. A dry run spends nothing. dry = requests.post(f"{API}/videos/ai-story", json={**video, "dryRun": True}, headers=auth).json() print(dry["estimate"]) # Make one key per video and reuse it on retries, so you are never charged twice. idempotency_key = str(uuid.uuid4()) job = requests.post( f"{API}/videos/ai-story", json=video, headers={**auth, "Idempotency-Key": idempotency_key}, ).json()["job"] # Long-poll: each call waits up to 30 seconds for the job to change. while job["status"] not in ("completed", "failed", "canceled"): job = requests.get(f"{API}/jobs/{job['id']}", params={"wait": 30}, headers=auth, timeout=60).json()["job"] print(job["status"], (job.get("output") or {}).get("videoUrl")) ``` ## Authentication Send a bearer token on every call: `Authorization: Bearer `. - API keys (`clp_pat_…`) are for your code and for MCP clients that send a fixed header. Create them in [Settings, API](https://clippie.ai/user/settings?tab=api). A key has the `read` scope, the `create` scope or both, and expires after 30 days, 90 days (the default) or never. You can have 20. Only a hash is stored, so a lost key cannot be shown again; revoke it and make a new one. - OAuth access tokens (`clp_at_…`) are what Claude, ChatGPT, Cursor and VS Code get when you connect them to the MCP server and sign in. They use authorization code with PKCE and dynamic client registration; a `401` carries a `WWW-Authenticate` header that points clients at the sign-in. - The API is part of every paid plan. Without an active subscription, calls answer `403 subscription_required`. A suspended account gets `403 suspended`. > Keep keys in an environment variable or a secret manager. Anyone with a key can spend your credits within its scopes. ## Requests and responses - Base URL: `https://api.clippie.ai/v1`. Breaking changes go to `/v2`; new fields can appear at any time. - POST bodies are JSON (`Content-Type: application/json`). GET calls take query parameters. - Lists take `?limit=` (up to 100, default 25) and `?cursor=`, and answer `{ data, nextCursor }`. Pass `nextCursor` back to get the next page; it is `null` on the last one. - Every response has an `X-Request-Id`. Include it when you contact support. - The OpenAPI 3.1 spec is at https://api.clippie.ai/v1/openapi.json. ## Errors Errors are `{ "error": { "type", "message", "param"?, "requestId" } }`. Branch on `type`; the message is written for people. | Status | type | Meaning | | --- | --- | --- | | 400 | `invalid_request` | A field is missing or wrong. `param` names it. | | 401 | `unauthorized` | No key, or the key is wrong, expired or revoked. MCP clients get a `WWW-Authenticate` hint to sign in. | | 402 | `insufficient_credits` | Not enough credits for this call. Top up or wait for the next period. | | 402 | `insufficient_minutes` | Not enough voiceover, caption or export minutes. | | 403 | `subscription_required` | The API needs an active paid plan. | | 403 | `forbidden_scope` | The key is read-only and the call creates something. | | 403 | `suspended` | The account is suspended. | | 403 | `forbidden` | This account cannot use the API (yet). | | 404 | `not_found` | No such job, file or project on your account. | | 409 | `idempotency_conflict` | The Idempotency-Key was used with a different body. | | 409 | `too_many_active_jobs` | Too many jobs are running. Wait for one to finish. | | 422 | `generation_rejected` | The model refused the input, for example a safety filter. | | 429 | `rate_limited` | Too many calls. Wait for `Retry-After` seconds. | | 500 | `internal` | Something broke on our side. Retry with the same Idempotency-Key. | A generation that fails after the `202` is a job failure, not an HTTP error: the job ends `failed` with an `error`, and whatever was charged is refunded. ## Rate limits - 120 calls a minute per account, REST and MCP together. Responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`. - Over the limit, calls answer `429 rate_limited` with a `Retry-After` header. Wait that many seconds. - Create calls also count against the same per-feature limits as the app (below). - At most 5 jobs run at once per account (3 for AI Story videos). Another one answers `409 too_many_active_jobs`. | Call | Limit bucket | | --- | --- | | [generate_image](https://clippie.ai/developers/docs/generate-image.md) | `ai_image` | | [generate_video](https://clippie.ai/developers/docs/generate-video.md) | `ai_video` | | [recast_video](https://clippie.ai/developers/docs/recast-video.md) | `ai_video` | | [upscale](https://clippie.ai/developers/docs/upscale.md) | `upscale` | | [text_to_speech](https://clippie.ai/developers/docs/text-to-speech.md) | `voiceover` | | [transcribe](https://clippie.ai/developers/docs/transcribe.md) | `transcribe` | | [write_script](https://clippie.ai/developers/docs/write-script.md) | `llm` | | [create_ai_story_video](https://clippie.ai/developers/docs/create-ai-story-video.md) | `render` | | [render_video](https://clippie.ai/developers/docs/render-video.md) | `render` | ## Idempotency, dry runs and estimates - Send `Idempotency-Key: ` on every create call (MCP: the `idempotencyKey` argument). A retry with the same key and body within 24 hours returns the first job and charges nothing; the same key with a different body is `409 idempotency_conflict`. Make one key per thing you want made, for example a UUID, and keep it until the job exists. - Add `"dryRun": true` to any create call to run every check and get `{ dryRun: true, estimate }` back: the credits and minutes it would take, `wouldSucceed`, and the `reason` when it would be refused. Nothing is spent and no job is made. - [estimate_cost](https://clippie.ai/developers/docs/estimate-cost.md) (`POST /v1/estimate`) does the same for any create tool by name, which is handy for agents and for adding up a batch. ## Jobs and waiting Every create call answers `202 { job }`. A job goes `queued` → `processing` → `rendering` → `completed`, `failed` or `canceled`. - `job.output` holds what was made: `videoUrl`, `imageUrls`, `audioUrl`, `captions`, `editorUrl` (open it in the Clippie editor) and `libraryId`. - `job.cost` is what was actually charged, after any refund: `{ credits, minutes: { voiceover, captions, export } }`. - [Get job](https://clippie.ai/developers/docs/get-job.md) with `?wait=30` holds the request (up to 50 seconds) until the job changes, so a simple loop never hammers the API. MCP: `get_job` with `waitSeconds`. - [Cancel job](https://clippie.ai/developers/docs/cancel-job.md) stops a job that has not started. [List jobs](https://clippie.ai/developers/docs/list-jobs.md) shows your recent ones. A job: ```json { "job": { "id": "job_abc123", "object": "job", "kind": "ai_story_video", "status": "queued", "step": null, "progress": null, "input": { "prompt": "The lost library of Alexandria" }, "output": null, "cost": { "credits": 0, "minutes": { "voiceover": 1, "captions": 1, "export": 1 } }, "error": null, "source": "api", "webhookUrl": null, "createdAt": "2026-10-24T12:00:00.000Z", "startedAt": null, "completedAt": null } } ``` ## Webhooks Pass `webhookUrl` (https, public address) on a create call and Clippie POSTs `{ event, job }` to it when the job finishes. - Events: `job.completed` and `job.failed`. - Headers: `Clippie-Event`, `Clippie-Job-Id` and `Clippie-Signature: t=,v1=`, an HMAC-SHA256 of `"."` with your `whsec_…` signing secret from [Settings, API](https://clippie.ai/user/settings?tab=api). - Answer with any 2xx within 10 seconds. Failed deliveries are retried after 30 s, 2 m, 10 m, 30 m, 1 h, 2 h, 4 h and 8 h. Redirects are not followed. - Delivery is at least once: dedupe on `Clippie-Job-Id` plus the event. Check the signature against the raw body and reject timestamps older than five minutes. verify.mjs: ```js import { createHmac, timingSafeEqual } from 'node:crypto'; // header: the Clippie-Signature header, "t=,v1=" // rawBody: the request body exactly as received, before JSON parsing // secret: your whsec_... signing secret from Settings > API export function verifyClippieWebhook(rawBody, header, secret, toleranceSeconds = 300) { const parts = Object.fromEntries(header.split(',').map((part) => part.trim().split('='))); const timestamp = Number(parts.t); if (!Number.isInteger(timestamp) || !parts.v1) return false; if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false; const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest(); const given = Buffer.from(parts.v1, 'hex'); return given.length === expected.length && timingSafeEqual(given, expected); } ``` verify.py: ```python import hashlib, hmac, time # header: the Clippie-Signature header, "t=,v1=" # raw_body: the request body bytes exactly as received # secret: your whsec_... signing secret from Settings > API def verify_clippie_webhook(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool: parts = dict(part.strip().split("=", 1) for part in header.split(",") if "=" in part) try: timestamp = int(parts.get("t", "")) except ValueError: return False if abs(time.time() - timestamp) > tolerance: return False signed = f"{timestamp}.".encode() + raw_body expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, parts.get("v1", "")) ``` ## MCP server The MCP server is at `https://api.clippie.ai/mcp` (Streamable HTTP). It offers every endpoint below as a tool, with the same scopes, prices and errors. Claude, ChatGPT, Cursor and VS Code sign in with OAuth when you add the URL; clients that send a fixed header can use an API key instead. ### Install Claude: Customize → Connectors → + → Add custom connector, paste the URL, then Connect: ```text https://api.clippie.ai/mcp ``` Claude Code (then /mcp to sign in): ```bash claude mcp add --transport http clippie https://api.clippie.ai/mcp ``` Cursor: ~/.cursor/mcp.json: ```json { "mcpServers": { "clippie": { "url": "https://api.clippie.ai/mcp" } } } ``` VS Code: .vscode/mcp.json: ```json { "servers": { "clippie": { "type": "http", "url": "https://api.clippie.ai/mcp" } } } ``` ChatGPT: Settings → Apps → Advanced settings → Developer mode on, then Create app with this URL and OAuth: ```text https://api.clippie.ai/mcp ``` Clients that send a fixed header: ```bash claude mcp add --transport http clippie https://api.clippie.ai/mcp \ --header "Authorization: Bearer clp_pat_..." # Cursor mcp.json { "mcpServers": { "clippie": { "url": "https://api.clippie.ai/mcp", "headers": { "Authorization": "Bearer clp_pat_..." } } } } # VS Code .vscode/mcp.json { "inputs": [ { "type": "promptString", "id": "clippie-key", "description": "Clippie API key", "password": true } ], "servers": { "clippie": { "type": "http", "url": "https://api.clippie.ai/mcp", "headers": { "Authorization": "Bearer ${input:clippie-key}" } } } } # Any client curl https://api.clippie.ai/mcp \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' ``` ### Server instructions What the server tells your AI when it connects: > Clippie makes short-form videos, images and audio for the signed-in user, from their own credits and minutes. Call get_account first. Before any tool that spends (its description says so), run estimate_cost or the tool with dryRun: true and confirm the cost with the user. Give every create call an idempotencyKey and reuse it if you retry, so a retry never charges twice. Create tools return a job: use get_job with waitSeconds (up to 50) instead of polling in a tight loop, and never report a job as finished before its status is completed. Finished jobs include links to the media and to the Clippie editor. Lists take limit and cursor; catalogs are also resources under clippie://catalog/. ### Resources Catalogs your client can attach to the conversation without a tool call. | URI | Title | What it is | | --- | --- | --- | | `clippie://account` | Account | Your plan, credit balance and minutes left. | | `clippie://catalog/voices` | Voices | Narration voices (first 100; list_voices searches them all). | | `clippie://catalog/caption-styles` | Caption styles | Burned-in caption styles. | | `clippie://catalog/art-styles` | Art styles | Looks for AI Story videos. | | `clippie://catalog/story-templates` | Story templates | Ready-made AI Story formats. | | `clippie://catalog/motion-templates` | Motion templates | Trending motion clips for Clippie Recast (first 100). | | `clippie://catalog/models` | AI models | AI Studio video and image models with credit prices. | | `clippie://catalog/music` | Music | Background music tracks (first 100). | | `clippie://jobs/{id}` | Job | One API job: status, output and cost. | ### Prompts Guided workflows you can pick in your client. Each one checks the cost and asks you before it spends. | Prompt | What it does | Arguments | | --- | --- | --- | | `make_short_video` | Topic → script → voice and style → cost check → create → wait for the finished video. | `topic`, `style` (optional), `length` (optional) | | `recast_me` | Put a photo of you into a trending motion clip with Clippie Recast. | `photoUrl` (optional), `template` (optional) | | `batch_shorts` | Several short videos from a list of topics, with one cost check for the whole batch. | `topics`, `style` (optional) | ## Endpoints ### Templates Finished, narrated videos built and rendered on the server. | Endpoint | REST | MCP tool | Cost | | --- | --- | --- | --- | | [Create AI Story video](https://clippie.ai/developers/docs/create-ai-story-video.md) | `POST /v1/videos/ai-story` | `create_ai_story_video` | Spends | | [Render video](https://clippie.ai/developers/docs/render-video.md) | `POST /v1/videos/:projectId/render` | `render_video` | Spends | ### AI tools Single generations: images, AI video, Recast, upscales, speech, transcripts and scripts. | Endpoint | REST | MCP tool | Cost | | --- | --- | --- | --- | | [Generate images](https://clippie.ai/developers/docs/generate-image.md) | `POST /v1/ai/images` | `generate_image` | Spends | | [Generate a video clip](https://clippie.ai/developers/docs/generate-video.md) | `POST /v1/ai/videos` | `generate_video` | Spends | | [Recast a trending video](https://clippie.ai/developers/docs/recast-video.md) | `POST /v1/ai/recast` | `recast_video` | Spends | | [Upscale an image or video](https://clippie.ai/developers/docs/upscale.md) | `POST /v1/ai/upscale` | `upscale` | Spends | | [Text to speech](https://clippie.ai/developers/docs/text-to-speech.md) | `POST /v1/ai/speech` | `text_to_speech` | Spends | | [Transcribe audio or video](https://clippie.ai/developers/docs/transcribe.md) | `POST /v1/ai/transcribe` | `transcribe` | Spends | | [Write a script](https://clippie.ai/developers/docs/write-script.md) | `POST /v1/ai/scripts` | `write_script` | Free | ### Resources Catalogs to pick from: voices, styles, templates, models, music and footage. | Endpoint | REST | MCP tool | Cost | | --- | --- | --- | --- | | [List voices](https://clippie.ai/developers/docs/list-voices.md) | `GET /v1/voices` | `list_voices` | Free | | [List caption styles](https://clippie.ai/developers/docs/list-caption-styles.md) | `GET /v1/caption-styles` | `list_caption_styles` | Free | | [List art styles](https://clippie.ai/developers/docs/list-art-styles.md) | `GET /v1/art-styles` | `list_art_styles` | Free | | [List story templates](https://clippie.ai/developers/docs/list-story-templates.md) | `GET /v1/story-templates` | `list_story_templates` | Free | | [List motion templates](https://clippie.ai/developers/docs/list-motion-templates.md) | `GET /v1/motion-templates` | `list_motion_templates` | Free | | [List AI models](https://clippie.ai/developers/docs/list-models.md) | `GET /v1/models` | `list_models` | Free | | [List background videos](https://clippie.ai/developers/docs/list-background-videos.md) | `GET /v1/background-videos` | `list_background_videos` | Free | | [List music](https://clippie.ai/developers/docs/list-music.md) | `GET /v1/music` | `list_music` | Free | | [List sound effects](https://clippie.ai/developers/docs/list-sound-effects.md) | `GET /v1/sound-effects` | `list_sound_effects` | Free | | [List languages](https://clippie.ai/developers/docs/list-languages.md) | `GET /v1/languages` | `list_languages` | Free | ### Jobs & files Wait for and cancel jobs, check costs, and upload the files other calls use. | Endpoint | REST | MCP tool | Cost | | --- | --- | --- | --- | | [List jobs](https://clippie.ai/developers/docs/list-jobs.md) | `GET /v1/jobs` | `list_jobs` | Free | | [Get job](https://clippie.ai/developers/docs/get-job.md) | `GET /v1/jobs/:id` | `get_job` | Free | | [Cancel job](https://clippie.ai/developers/docs/cancel-job.md) | `POST /v1/jobs/:id/cancel` | `cancel_job` | Free | | [Estimate cost](https://clippie.ai/developers/docs/estimate-cost.md) | `POST /v1/estimate` | `estimate_cost` | Free | | [Upload file](https://clippie.ai/developers/docs/upload-file.md) | `POST /v1/files` | `upload_file` | Free | | [Start a large file upload](https://clippie.ai/developers/docs/create-file-upload.md) | `POST /v1/files/uploads` | REST only | Free | | [Finish a large file upload](https://clippie.ai/developers/docs/complete-file-upload.md) | `POST /v1/files/uploads/complete` | REST only | Free | | [Get file](https://clippie.ai/developers/docs/get-file.md) | `GET /v1/files/:id` | `get_file` | Free | ### Account Your plan, balances and where they went. | Endpoint | REST | MCP tool | Cost | | --- | --- | --- | --- | | [Get account](https://clippie.ai/developers/docs/get-account.md) | `GET /v1/me` | `get_account` | Free | | [List credit history](https://clippie.ai/developers/docs/list-credit-history.md) | `GET /v1/credits/history` | `list_credit_history` | Free | --- ## Get account URL: https://clippie.ai/developers/docs/get-account Shows the user's Clippie plan, AI credit balance (plan and top-up), the voiceover, caption and export minutes left, when the period ends and whether videos are watermarked. Call this first, and before anything that spends. ### At a glance | Property | Value | | --- | --- | | REST | `GET https://api.clippie.ai/v1/me` | | MCP tool | `get_account` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters No parameters. ### Example curl: ```bash curl "https://api.clippie.ai/v1/me" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" ``` get-account.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/me', { headers: auth }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200` with JSON. The full response schema is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `get_account` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "get_account", "arguments": {} } ``` --- ## List credit history URL: https://clippie.ai/developers/docs/list-credit-history Lists credit movements (top-ups, plan credits, generations, refunds) and minute spends, newest first, each linked to its API job when there is one. ### At a glance | Property | Value | | --- | --- | | REST | `GET https://api.clippie.ai/v1/credits/history` | | MCP tool | `list_credit_history` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `limit` | query | integer | No | | How many to return, 1-100. Default 25. 1-100. | | `cursor` | query | string | No | | `nextCursor` from the previous page. Up to 500 chars. | ### Example curl: ```bash curl "https://api.clippie.ai/v1/credits/history?limit=10" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" ``` list-credit-history.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/credits/history?limit=10', { headers: auth }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200 { data, nextCursor }`. Pass `nextCursor` as `cursor` for the next page. The item shape is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `list_credit_history` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "list_credit_history", "arguments": { "limit": 10 } } ``` --- ## List voices URL: https://clippie.ai/developers/docs/list-voices Lists narration voices: the user's own cloned voices first, then the popular voices, then the rest. Filter with search (name, accent, style), gender, language or mine. Pass a voice's id as voiceId. ### At a glance | Property | Value | | --- | --- | | REST | `GET https://api.clippie.ai/v1/voices` | | MCP tool | `list_voices` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `search` | query | string | No | | Matches name, accent or description, e.g. "british" or "deep". Up to 60 chars. | | `gender` | query | string | No | | One of `male`, `female`. | | `language` | query | string | No | | Language code, e.g. "en", "es", "jp". Up to 10 chars. | | `mine` | query | boolean | No | | Only the user's own cloned voices. | | `limit` | query | integer | No | | How many to return, 1-100. Default 25. 1-100. | | `cursor` | query | string | No | | `nextCursor` from the previous page. Up to 500 chars. | ### Example curl: ```bash curl "https://api.clippie.ai/v1/voices?search=british&limit=10" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" ``` list-voices.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/voices?search=british&limit=10', { headers: auth }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200 { data, nextCursor }`. Pass `nextCursor` as `cursor` for the next page. The item shape is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `list_voices` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "list_voices", "arguments": { "search": "british", "limit": 10 } } ``` --- ## List caption styles URL: https://clippie.ai/developers/docs/list-caption-styles Lists the burned-in caption styles (font, colours, one word at a time or lines). Pass an id as captionStyleId. ### At a glance | Property | Value | | --- | --- | | REST | `GET https://api.clippie.ai/v1/caption-styles` | | MCP tool | `list_caption_styles` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `limit` | query | integer | No | | How many to return, 1-100. Default 25. 1-100. | | `cursor` | query | string | No | | `nextCursor` from the previous page. Up to 500 chars. | ### Example curl: ```bash curl "https://api.clippie.ai/v1/caption-styles" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" ``` list-caption-styles.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/caption-styles', { headers: auth }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200 { data, nextCursor }`. Pass `nextCursor` as `cursor` for the next page. The item shape is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `list_caption_styles` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "list_caption_styles", "arguments": {} } ``` --- ## List art styles URL: https://clippie.ai/developers/docs/list-art-styles Lists the looks an AI Story video can have. Either pass a type "style" id as artStyleId, or pass one of imageModels as imageModel together with a type "stylePrompt" id as stylePromptId. Direct-model scene images are included on paid plans. ### At a glance | Property | Value | | --- | --- | | REST | `GET https://api.clippie.ai/v1/art-styles` | | MCP tool | `list_art_styles` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `type` | query | string | No | | One of `style`, `stylePrompt`. | | `limit` | query | integer | No | | How many to return, 1-100. Default 25. 1-100. | | `cursor` | query | string | No | | `nextCursor` from the previous page. Up to 500 chars. | ### Example curl: ```bash curl "https://api.clippie.ai/v1/art-styles?limit=10" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" ``` list-art-styles.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/art-styles?limit=10', { headers: auth }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200 { data, nextCursor }`. Pass `nextCursor` as `cursor` for the next page. The item shape is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `list_art_styles` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "list_art_styles", "arguments": { "limit": 10 } } ``` --- ## List story templates URL: https://clippie.ai/developers/docs/list-story-templates Lists ready-made AI Story formats (an art style plus a frame shape, e.g. "History documentary" 16:9). Pass an id as storyTemplateId to set the look and aspect ratio in one go. ### At a glance | Property | Value | | --- | --- | | REST | `GET https://api.clippie.ai/v1/story-templates` | | MCP tool | `list_story_templates` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `aspectRatio` | query | string | No | | One of `16:9`, `9:16`. | | `limit` | query | integer | No | | How many to return, 1-100. Default 25. 1-100. | | `cursor` | query | string | No | | `nextCursor` from the previous page. Up to 500 chars. | ### Example curl: ```bash curl "https://api.clippie.ai/v1/story-templates?aspectRatio=9%3A16" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" ``` list-story-templates.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/story-templates?aspectRatio=9%3A16', { headers: auth }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200 { data, nextCursor }`. Pass `nextCursor` as `cursor` for the next page. The item shape is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `list_story_templates` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "list_story_templates", "arguments": { "aspectRatio": "9:16" } } ``` --- ## List motion templates URL: https://clippie.ai/developers/docs/list-motion-templates Lists the trending motion clips used by Clippie Recast and motion-control video models, in the same order as AI Studio (newest hand-picked uploads first, then by trend). Pass an id as motionTemplateId. Each template says its length and whether Recast accepts it (recastCompatible); offer only those for recast_video. ### At a glance | Property | Value | | --- | --- | | REST | `GET https://api.clippie.ai/v1/motion-templates` | | MCP tool | `list_motion_templates` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `limit` | query | integer | No | | How many to return, 1-100. Default 25. 1-100. | | `cursor` | query | string | No | | `nextCursor` from the previous page. Up to 500 chars. | ### Example curl: ```bash curl "https://api.clippie.ai/v1/motion-templates" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" ``` list-motion-templates.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/motion-templates', { headers: auth }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200 { data, nextCursor }`. Pass `nextCursor` as `cursor` for the next page. The item shape is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `list_motion_templates` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "list_motion_templates", "arguments": {} } ``` --- ## List AI models URL: https://clippie.ai/developers/docs/list-models Lists the AI Studio video and image models with their resolutions, aspect ratios, durations and credit price per generation (images: per image). Use an id as model for generate_video / generate_image. ### At a glance | Property | Value | | --- | --- | | REST | `GET https://api.clippie.ai/v1/models` | | MCP tool | `list_models` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `type` | query | string | No | | One of `video`, `image`. | | `limit` | query | integer | No | | How many to return, 1-100. Default 25. 1-100. | | `cursor` | query | string | No | | `nextCursor` from the previous page. Up to 500 chars. | ### Example curl: ```bash curl "https://api.clippie.ai/v1/models?type=video" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" ``` list-models.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/models?type=video', { headers: auth }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200 { data, nextCursor }`. Pass `nextCursor` as `cursor` for the next page. The item shape is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `list_models` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "list_models", "arguments": { "type": "video" } } ``` --- ## List background videos URL: https://clippie.ai/developers/docs/list-background-videos Lists the gameplay and satisfying background clips (Minecraft parkour, Roblox, ...) used behind story videos. ### At a glance | Property | Value | | --- | --- | | REST | `GET https://api.clippie.ai/v1/background-videos` | | MCP tool | `list_background_videos` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `category` | query | string | No | | Up to 60 chars. | | `limit` | query | integer | No | | How many to return, 1-100. Default 25. 1-100. | | `cursor` | query | string | No | | `nextCursor` from the previous page. Up to 500 chars. | ### Example curl: ```bash curl "https://api.clippie.ai/v1/background-videos" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" ``` list-background-videos.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/background-videos', { headers: auth }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200 { data, nextCursor }`. Pass `nextCursor` as `cursor` for the next page. The item shape is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `list_background_videos` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "list_background_videos", "arguments": {} } ``` --- ## List music URL: https://clippie.ai/developers/docs/list-music Lists background music tracks, popular first. Tracks with hasCopyrightWarning may be muted on some platforms. Pass an id as musicId. ### At a glance | Property | Value | | --- | --- | | REST | `GET https://api.clippie.ai/v1/music` | | MCP tool | `list_music` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `search` | query | string | No | | Up to 60 chars. | | `limit` | query | integer | No | | How many to return, 1-100. Default 25. 1-100. | | `cursor` | query | string | No | | `nextCursor` from the previous page. Up to 500 chars. | ### Example curl: ```bash curl "https://api.clippie.ai/v1/music?search=chill&limit=10" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" ``` list-music.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/music?search=chill&limit=10', { headers: auth }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200 { data, nextCursor }`. Pass `nextCursor` as `cursor` for the next page. The item shape is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `list_music` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "list_music", "arguments": { "search": "chill", "limit": 10 } } ``` --- ## List sound effects URL: https://clippie.ai/developers/docs/list-sound-effects Lists the short sound effects (whoosh, bell, punch, ...) that can be placed on a video timeline. ### At a glance | Property | Value | | --- | --- | | REST | `GET https://api.clippie.ai/v1/sound-effects` | | MCP tool | `list_sound_effects` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `search` | query | string | No | | Up to 60 chars. | | `limit` | query | integer | No | | How many to return, 1-100. Default 25. 1-100. | | `cursor` | query | string | No | | `nextCursor` from the previous page. Up to 500 chars. | ### Example curl: ```bash curl "https://api.clippie.ai/v1/sound-effects" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" ``` list-sound-effects.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/sound-effects', { headers: auth }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200 { data, nextCursor }`. Pass `nextCursor` as `cursor` for the next page. The item shape is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `list_sound_effects` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "list_sound_effects", "arguments": {} } ``` --- ## List languages URL: https://clippie.ai/developers/docs/list-languages Lists the languages for captions, transcription and narration. Pass a code as language. ### At a glance | Property | Value | | --- | --- | | REST | `GET https://api.clippie.ai/v1/languages` | | MCP tool | `list_languages` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `limit` | query | integer | No | | How many to return, 1-100. Default 25. 1-100. | | `cursor` | query | string | No | | `nextCursor` from the previous page. Up to 500 chars. | ### Example curl: ```bash curl "https://api.clippie.ai/v1/languages" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" ``` list-languages.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/languages', { headers: auth }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200 { data, nextCursor }`. Pass `nextCursor` as `cursor` for the next page. The item shape is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `list_languages` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "list_languages", "arguments": {} } ``` --- ## List jobs URL: https://clippie.ai/developers/docs/list-jobs Lists the jobs started through the API or MCP, newest first, with their status, output and what they cost. Filter by status or kind. ### At a glance | Property | Value | | --- | --- | | REST | `GET https://api.clippie.ai/v1/jobs` | | MCP tool | `list_jobs` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `status` | query | string | No | | One of `queued`, `processing`, `rendering`, `completed`, `failed`, `canceled`. | | `kind` | query | string | No | | Up to 40 chars. | | `limit` | query | integer | No | | How many to return, 1-100. Default 25. 1-100. | | `cursor` | query | string | No | | `nextCursor` from the previous page. Up to 500 chars. | ### Example curl: ```bash curl "https://api.clippie.ai/v1/jobs?status=completed&limit=10" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" ``` list-jobs.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/jobs?status=completed&limit=10', { headers: auth }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200 { data, nextCursor }`. Pass `nextCursor` as `cursor` for the next page. The item shape is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `list_jobs` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "list_jobs", "arguments": { "status": "completed", "limit": 10 } } ``` --- ## Get job URL: https://clippie.ai/developers/docs/get-job Gets one job: status (queued, processing, rendering, completed, failed, canceled), step, progress, output (videoUrl, imageUrls, audioUrl, captions, editorUrl) and cost. Pass waitSeconds (up to 50) to wait for it to finish instead of polling in a loop. ### At a glance | Property | Value | | --- | --- | | REST | `GET https://api.clippie.ai/v1/jobs/:id` | | MCP tool | `get_job` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `id` | path | string | Yes | | The job id, job_… 1-80 chars. | | `wait` | query | integer | No | | Wait up to this many seconds (max 50) for the job to finish. REST: ?wait=. 0-50. MCP: `waitSeconds`. | ### Example curl: ```bash curl "https://api.clippie.ai/v1/jobs/job_abc123?wait=30" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" ``` get-job.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/jobs/job_abc123?wait=30', { headers: auth }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200` with JSON. The full response schema is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `get_job` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "get_job", "arguments": { "id": "job_abc123", "waitSeconds": 30 } } ``` --- ## Cancel job URL: https://clippie.ai/developers/docs/cancel-job Cancels a job that is still queued and refunds what it was charged. Jobs that have started cannot be canceled. ### At a glance | Property | Value | | --- | --- | | REST | `POST https://api.clippie.ai/v1/jobs/:id/cancel` | | MCP tool | `cancel_job` | | Scope | `create` | | Cost | Spends nothing. Needs the `create` scope. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `id` | path | string | Yes | | The job id, job_… 1-80 chars. | ### Example curl: ```bash curl https://api.clippie.ai/v1/jobs/job_abc123/cancel \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` cancel-job.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/jobs/job_abc123/cancel', { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json' }, body: JSON.stringify({}), }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200` with JSON. The full response schema is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `cancel_job` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "cancel_job", "arguments": { "id": "job_abc123" } } ``` --- ## Estimate cost URL: https://clippie.ai/developers/docs/estimate-cost Shows what a create tool would charge (credits and voiceover, caption and export minutes) and whether it would go through, without spending anything. Pass the tool name and the same input you would send it. Confirm the cost with the user before creating. ### At a glance | Property | Value | | --- | --- | | REST | `POST https://api.clippie.ai/v1/estimate` | | MCP tool | `estimate_cost` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `tool` | body | string | Yes | | A create tool name, e.g. generate_image. 1-80 chars. | | `input` | body | object | No | `{}` | The tool's input. | ### Example curl: ```bash curl https://api.clippie.ai/v1/estimate \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tool": "create_ai_story_video", "input": { "prompt": "The lost library of Alexandria", "aspectRatio": "9:16", "durationSeconds": 60 } }' ``` estimate-cost.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/estimate', { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json' }, body: JSON.stringify({ "tool": "create_ai_story_video", "input": { "prompt": "The lost library of Alexandria", "aspectRatio": "9:16", "durationSeconds": 60 } }), }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200` with JSON. The full response schema is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `estimate_cost` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "estimate_cost", "arguments": { "tool": "create_ai_story_video", "input": { "prompt": "The lost library of Alexandria", "aspectRatio": "9:16", "durationSeconds": 60 } } } ``` --- ## Upload file URL: https://clippie.ai/developers/docs/upload-file Imports an image, video or audio file from a public https URL (up to 100 MB) so other tools can use it as fileId. REST callers can also send the file itself as multipart/form-data (field "file"). ### At a glance | Property | Value | | --- | --- | | REST | `POST https://api.clippie.ai/v1/files` | | MCP tool | `upload_file` | | Scope | `create` | | Cost | Spends nothing. Needs the `create` scope. | | Upload | Also takes `multipart/form-data` with the file in `file`, up to 100 MB. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `url` | body | string (URL) | No | | Public https URL of the file to import. Up to 2048 chars. | | `name` | body | string | No | | File name to keep, e.g. "intro.mp4". Up to 120 chars. | ### Example curl: ```bash curl https://api.clippie.ai/v1/files \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/photo.jpg", "name": "photo.jpg" }' ``` upload-file.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/files', { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json' }, body: JSON.stringify({ "url": "https://example.com/photo.jpg", "name": "photo.jpg" }), }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200` with JSON. The full response schema is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `upload_file` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "upload_file", "arguments": { "url": "https://example.com/photo.jpg", "name": "photo.jpg" } } ``` --- ## Start a large file upload URL: https://clippie.ai/developers/docs/create-file-upload Starts a multipart upload for files over 100 MB (up to 5 GB): returns presigned URLs to PUT each part to, valid for an hour, then call POST /v1/files/uploads/complete with each part's ETag. ### At a glance | Property | Value | | --- | --- | | REST | `POST https://api.clippie.ai/v1/files/uploads` | | MCP tool | REST only (not an MCP tool) | | Scope | `create` | | Cost | Spends nothing. Needs the `create` scope. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `fileName` | body | string | Yes | | 1-120 chars. | | `contentType` | body | string | Yes | | image/*, video/* or audio/* 3-100 chars. | | `sizeBytes` | body | integer | Yes | | 1-5368709120. | ### Example curl: ```bash curl https://api.clippie.ai/v1/files/uploads \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "fileName": "long-video.mp4", "contentType": "video/mp4", "sizeBytes": 524288000 }' ``` create-file-upload.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/files/uploads', { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json' }, body: JSON.stringify({ "fileName": "long-video.mp4", "contentType": "video/mp4", "sizeBytes": 524288000 }), }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200` with JSON. The full response schema is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). --- ## Finish a large file upload URL: https://clippie.ai/developers/docs/complete-file-upload Finishes a multipart upload started with POST /v1/files/uploads and returns the file. ### At a glance | Property | Value | | --- | --- | | REST | `POST https://api.clippie.ai/v1/files/uploads/complete` | | MCP tool | REST only (not an MCP tool) | | Scope | `create` | | Cost | Spends nothing. Needs the `create` scope. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `key` | body | string | Yes | | 1-400 chars. | | `uploadId` | body | string | Yes | | 1-1000 chars. | | `parts` | body | array of object | Yes | | 1-10000 items. | ### Example curl: ```bash curl https://api.clippie.ai/v1/files/uploads/complete \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "key": "KEY_FROM_CREATE_FILE_UPLOAD", "uploadId": "UPLOAD_ID_FROM_CREATE_FILE_UPLOAD", "parts": [ { "partNumber": 1, "etag": "\"ETAG_FROM_THE_PART_UPLOAD\"" } ] }' ``` complete-file-upload.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/files/uploads/complete', { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json' }, body: JSON.stringify({ "key": "KEY_FROM_CREATE_FILE_UPLOAD", "uploadId": "UPLOAD_ID_FROM_CREATE_FILE_UPLOAD", "parts": [ { "partNumber": 1, "etag": "\"ETAG_FROM_THE_PART_UPLOAD\"" } ] }), }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200` with JSON. The full response schema is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). --- ## Get file URL: https://clippie.ai/developers/docs/get-file Gets a file uploaded with upload_file: its type and URL. ### At a glance | Property | Value | | --- | --- | | REST | `GET https://api.clippie.ai/v1/files/:id` | | MCP tool | `get_file` | | Scope | `read` | | Cost | Free. Works with a read-only key. | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `id` | path | string | Yes | | file_… 1-80 chars. | ### Example curl: ```bash curl "https://api.clippie.ai/v1/files/file_abc123" \ -H "Authorization: Bearer $CLIPPIE_API_KEY" ``` get-file.mjs: ```js const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const res = await fetch('https://api.clippie.ai/v1/files/file_abc123', { headers: auth }); if (!res.ok) throw new Error((await res.json()).error.message); console.log(await res.json()); ``` ### Response `200` with JSON. The full response schema is in the [OpenAPI spec](https://api.clippie.ai/v1/openapi.json). ### From an MCP client Your AI calls `get_file` with the same fields as JSON arguments. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "get_file", "arguments": { "id": "file_abc123" } } ``` --- ## Generate images URL: https://clippie.ai/developers/docs/generate-image Generates 1-4 images with an AI Studio model (Nano Banana, Seedream, Flux, Imagen). Spends credits: about 6 credits for one 1-megapixel Nano Banana image; bigger sizes, more images and premium models cost more (list_models has every price). Run with dryRun: true (or estimate_cost) first and confirm the cost. Returns a job; get_job with waitSeconds returns the image URLs, usually within a minute. ### At a glance | Property | Value | | --- | --- | | REST | `POST https://api.clippie.ai/v1/ai/images` | | MCP tool | `generate_image` | | Scope | `create` | | Cost | Spends credits or minutes, at the same prices as the app. Send `"dryRun": true` (or call [estimate_cost](https://clippie.ai/developers/docs/estimate-cost.md)) first to see the exact cost without spending anything. | | Limit bucket | `ai_image` (see [Rate limits](https://clippie.ai/developers/docs.md#rate-limits)) | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `model` | body | string | No | `"nano-banana"` | Image model id from list_models (type image). One of `flux-dev`, `flux-2-dev`, `flux-2-pro`, `flux-2-flex`, `flux-2-max`, `imagen-4`, `imagen-4-fast`, `imagen-4-ultra`, `seedream-4.0`, `seedream-4.5`, `seedream-5.0-lite`, `seedream-5.0-flash`, `nano-banana`, `nano-banana-2.1`, `nano-banana-pro`. | | `prompt` | body | string | Yes | | What to draw. 1-4000 chars. | | `aspectRatio` | body | string | No | `"1:1"` | Image shape. Not every model takes every ratio (list_models). One of `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `21:9`, `9:21`, `3:2`, `2:3`. | | `resolution` | body | string | No | `"1"` | Megapixels. Not every model takes every size. One of `0.5`, `1`, `2`, `4`. | | `count` | body | integer | No | `1` | How many images, 1-4. Each is charged. 1-4. | | `referenceImages` | body | array of string | No | | Images to work from, for the models that take them (maxReferenceImages in list_models). Up to 14 items. | | `dryRun` | body | boolean | No | | Check the request and return { estimate } (credits and minutes) without spending anything. | | `webhookUrl` | body | string (URL) | No | | https URL that receives job.completed / job.failed, signed with your webhook secret. Up to 2048 chars. | | `Idempotency-Key` | header | string | No | | Retry-safe key: a retry with the same key and body within 24 hours returns the first job instead of starting (and charging) again. The same key with a different body is a 409. MCP clients pass it as the idempotencyKey argument. 1-255 chars. MCP: `idempotencyKey`. | ### Example curl: ```bash # 1. Dry run: the exact cost, nothing is spent. curl https://api.clippie.ai/v1/ai/images \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "nano-banana", "prompt": "A red fox in a snowy forest at golden hour", "aspectRatio": "9:16", "dryRun": true }' # => { "dryRun": true, "estimate": { "credits": ..., "minutes": { ... }, "wouldSucceed": true } } # 2. The real call. Keep the key and send the same one if you retry, so it is never charged twice. KEY=$(uuidgen) curl https://api.clippie.ai/v1/ai/images \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $KEY" \ -d '{ "model": "nano-banana", "prompt": "A red fox in a snowy forest at golden hour", "aspectRatio": "9:16" }' # => 202 { "job": { "id": "job_...", "status": "queued", ... } } ``` generate-image.mjs: ```js import { randomUUID } from 'node:crypto'; const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const input = { "model": "nano-banana", "prompt": "A red fox in a snowy forest at golden hour", "aspectRatio": "9:16" }; async function call(body, headers = {}) { const res = await fetch('https://api.clippie.ai/v1/ai/images', { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json', ...headers }, body: JSON.stringify(body), }); const json = await res.json(); if (!res.ok) throw new Error(`${json.error.type}: ${json.error.message}`); return json; } // 1. Dry run: the exact cost, nothing is spent. Confirm it before going on. const { estimate } = await call({ ...input, dryRun: true }); console.log(estimate); // { credits, minutes, wouldSucceed, reason } // 2. Create. Keep the key and reuse it on retries, so a retry never charges twice. let { job } = await call(input, { 'Idempotency-Key': randomUUID() }); // 3. Wait. Each call holds for up to 30 seconds until the job changes. while (!['completed', 'failed', 'canceled'].includes(job.status)) { const res = await fetch(`https://api.clippie.ai/v1/jobs/${job.id}?wait=30`, { headers: auth }); ({ job } = await res.json()); } console.log(job.status, job.output); ``` ### Response `202 { job }`: the job, straight away. Wait for it with [Get job](https://clippie.ai/developers/docs/get-job.md) (`?wait=30`) or a `webhookUrl`; see [Jobs and waiting](https://clippie.ai/developers/docs.md#jobs). With `"dryRun": true` the answer is `200 { dryRun: true, estimate }` and nothing is started. 202 Accepted: ```json { "job": { "id": "job_abc123", "object": "job", "kind": "generate_image", "status": "queued", "step": null, "progress": null, "input": { "prompt": "The lost library of Alexandria" }, "output": null, "cost": { "credits": 0, "minutes": { "voiceover": 1, "captions": 1, "export": 1 } }, "error": null, "source": "api", "webhookUrl": null, "createdAt": "2026-10-24T12:00:00.000Z", "startedAt": null, "completedAt": null } } ``` Dry run (200), shape only: ```json { "dryRun": true, "estimate": { "credits": 0, "minutes": { "voiceover": 0, "captions": 0, "export": 0 }, "wouldSucceed": true } } ``` ### From an MCP client Your AI calls `generate_image` with the same fields as JSON arguments, plus `idempotencyKey` instead of the header. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "generate_image", "arguments": { "model": "nano-banana", "prompt": "A red fox in a snowy forest at golden hour", "aspectRatio": "9:16", "idempotencyKey": "a-unique-key-per-thing-you-make" } } ``` --- ## Generate a video clip URL: https://clippie.ai/developers/docs/generate-video Generates a short AI video clip with an AI Studio model: Seedance and Veo from text or images, Clippie Recast and Genjutsu from a source video (referenceVideoUrl or motionTemplateId) plus photos. Spends credits, from tens to hundreds a clip depending on model, length and resolution (list_models has the prices). Run with dryRun: true (or estimate_cost) first and confirm the cost. Returns a job; clips take 1-10 minutes, so use get_job with waitSeconds. ### At a glance | Property | Value | | --- | --- | | REST | `POST https://api.clippie.ai/v1/ai/videos` | | MCP tool | `generate_video` | | Scope | `create` | | Cost | Spends credits or minutes, at the same prices as the app. Send `"dryRun": true` (or call [estimate_cost](https://clippie.ai/developers/docs/estimate-cost.md)) first to see the exact cost without spending anything. | | Limit bucket | `ai_video` (see [Rate limits](https://clippie.ai/developers/docs.md#rate-limits)) | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `model` | body | string | Yes | | Video model id from list_models (type video). One of `seedance-2-5`, `seedance-2-0`, `seedance-2-0-fast`, `seedance-2-0-mini`, `seedance-1-5-pro-251215`, `seedance-1-0-pro-250528`, `seedance-1-0-pro-fast-251015`, `veo-3.1-generate-preview`, `veo-3.1-fast-generate-preview`, `veo-3.0-generate-001`, `veo-3.0-fast-generate-001`, `clippie-recast`, `genjutsu-motion-transfer`, `genjutsu-object-swap`, `genjutsu-restyle`. | | `prompt` | body | string | No | `""` | What happens in the clip. Optional for the source-video models. Up to 5000 chars. | | `imageUrl` | body | string | No | | First frame, or the character photo for Recast and Genjutsu: a file id from POST /v1/files (file_…) or a public https URL (copied into Clippie first, up to 100 MB). 1-2048 chars. | | `lastFrameUrl` | body | string | No | | Last frame (needs imageUrl): a file id from POST /v1/files (file_…) or a public https URL (copied into Clippie first, up to 100 MB). 1-2048 chars. | | `referenceImages` | body | array of string | No | | Reference images (Seedance 2, Veo 3.1), or more people and objects for Recast and Genjutsu. Up to 30 items. | | `referenceVideos` | body | array of string | No | | Seedance 2 reference videos. Up to 10 items. | | `referenceAudios` | body | array of string | No | | Seedance 2 reference audio. Up to 10 items. | | `referenceVideoUrl` | body | string | No | | Source video for Recast and Genjutsu: a file id from POST /v1/files (file_…) or a public https URL (copied into Clippie first, up to 100 MB). 1-2048 chars. | | `motionTemplateId` | body | string | No | | A motion template id from list_motion_templates, used as the source video instead of referenceVideoUrl. 1-120 chars. | | `duration` | body | integer | No | | Seconds, within the model's range (list_models). Source-video models take the source's length. 1-30. | | `resolution` | body | string | No | | Default: 720p when the model has it, else its first. One of `480p`, `720p`, `1080p`. | | `aspectRatio` | body | string | No | | Default 9:16 when the model has it. One of `auto`, `21:9`, `16:9`, `4:3`, `1:1`, `3:4`, `9:16`. | | `withAudio` | body | boolean | No | | Generate sound, for the models that can (Veo always does). | | `presetId` | body | string (UUID) | No | | Genjutsu Restyle look. | | `seed` | body | integer | No | | | | `cameraFixed` | body | boolean | No | | Seedance 1.x: keep the camera still. | | `dryRun` | body | boolean | No | | Check the request and return { estimate } (credits and minutes) without spending anything. | | `webhookUrl` | body | string (URL) | No | | https URL that receives job.completed / job.failed, signed with your webhook secret. Up to 2048 chars. | | `Idempotency-Key` | header | string | No | | Retry-safe key: a retry with the same key and body within 24 hours returns the first job instead of starting (and charging) again. The same key with a different body is a 409. MCP clients pass it as the idempotencyKey argument. 1-255 chars. MCP: `idempotencyKey`. | ### Example curl: ```bash # 1. Dry run: the exact cost, nothing is spent. curl https://api.clippie.ai/v1/ai/videos \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "seedance-2-0-fast", "prompt": "A slow dolly shot down a neon-lit Tokyo street at night, light rain", "duration": 5, "aspectRatio": "9:16", "dryRun": true }' # => { "dryRun": true, "estimate": { "credits": ..., "minutes": { ... }, "wouldSucceed": true } } # 2. The real call. Keep the key and send the same one if you retry, so it is never charged twice. KEY=$(uuidgen) curl https://api.clippie.ai/v1/ai/videos \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $KEY" \ -d '{ "model": "seedance-2-0-fast", "prompt": "A slow dolly shot down a neon-lit Tokyo street at night, light rain", "duration": 5, "aspectRatio": "9:16" }' # => 202 { "job": { "id": "job_...", "status": "queued", ... } } ``` generate-video.mjs: ```js import { randomUUID } from 'node:crypto'; const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const input = { "model": "seedance-2-0-fast", "prompt": "A slow dolly shot down a neon-lit Tokyo street at night, light rain", "duration": 5, "aspectRatio": "9:16" }; async function call(body, headers = {}) { const res = await fetch('https://api.clippie.ai/v1/ai/videos', { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json', ...headers }, body: JSON.stringify(body), }); const json = await res.json(); if (!res.ok) throw new Error(`${json.error.type}: ${json.error.message}`); return json; } // 1. Dry run: the exact cost, nothing is spent. Confirm it before going on. const { estimate } = await call({ ...input, dryRun: true }); console.log(estimate); // { credits, minutes, wouldSucceed, reason } // 2. Create. Keep the key and reuse it on retries, so a retry never charges twice. let { job } = await call(input, { 'Idempotency-Key': randomUUID() }); // 3. Wait. Each call holds for up to 30 seconds until the job changes. while (!['completed', 'failed', 'canceled'].includes(job.status)) { const res = await fetch(`https://api.clippie.ai/v1/jobs/${job.id}?wait=30`, { headers: auth }); ({ job } = await res.json()); } console.log(job.status, job.output); ``` ### Response `202 { job }`: the job, straight away. Wait for it with [Get job](https://clippie.ai/developers/docs/get-job.md) (`?wait=30`) or a `webhookUrl`; see [Jobs and waiting](https://clippie.ai/developers/docs.md#jobs). With `"dryRun": true` the answer is `200 { dryRun: true, estimate }` and nothing is started. 202 Accepted: ```json { "job": { "id": "job_abc123", "object": "job", "kind": "generate_video", "status": "queued", "step": null, "progress": null, "input": { "prompt": "The lost library of Alexandria" }, "output": null, "cost": { "credits": 0, "minutes": { "voiceover": 1, "captions": 1, "export": 1 } }, "error": null, "source": "api", "webhookUrl": null, "createdAt": "2026-10-24T12:00:00.000Z", "startedAt": null, "completedAt": null } } ``` Dry run (200), shape only: ```json { "dryRun": true, "estimate": { "credits": 0, "minutes": { "voiceover": 0, "captions": 0, "export": 0 }, "wouldSucceed": true } } ``` ### From an MCP client Your AI calls `generate_video` with the same fields as JSON arguments, plus `idempotencyKey` instead of the header. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "generate_video", "arguments": { "model": "seedance-2-0-fast", "prompt": "A slow dolly shot down a neon-lit Tokyo street at night, light rain", "duration": 5, "aspectRatio": "9:16", "idempotencyKey": "a-unique-key-per-thing-you-make" } } ``` --- ## Recast a trending video URL: https://clippie.ai/developers/docs/recast-video Clippie Recast: puts the people in your photos into a trending motion template (list_motion_templates), keeping its moves, camera and sound. Send one photo per person in the template. Spends credits by the template's length: about 413 credits for a 10-second clip at 720p; 1080p costs more. Run with dryRun: true (or estimate_cost) first and confirm the cost. Returns a job; recasts take a few minutes, so use get_job with waitSeconds. ### At a glance | Property | Value | | --- | --- | | REST | `POST https://api.clippie.ai/v1/ai/recast` | | MCP tool | `recast_video` | | Scope | `create` | | Cost | Spends credits or minutes, at the same prices as the app. Send `"dryRun": true` (or call [estimate_cost](https://clippie.ai/developers/docs/estimate-cost.md)) first to see the exact cost without spending anything. | | Limit bucket | `ai_video` (see [Rate limits](https://clippie.ai/developers/docs.md#rate-limits)) | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `motionTemplateId` | body | string | Yes | | The motion template to recast, from list_motion_templates. 1-120 chars. | | `photo` | body | string | No | | Photo of the person to put in the video: a file id from POST /v1/files (file_…) or a public https URL (copied into Clippie first, up to 100 MB). 1-2048 chars. | | `photos` | body | array of string | No | | One photo per person, matched to the template's people from left to right (its characterCount). 1-4 items. | | `resolution` | body | string | No | `"720p"` | One of `720p`, `1080p`. | | `prompt` | body | string | No | | Optional direction for the recast. Up to 2000 chars. | | `dryRun` | body | boolean | No | | Check the request and return { estimate } (credits and minutes) without spending anything. | | `webhookUrl` | body | string (URL) | No | | https URL that receives job.completed / job.failed, signed with your webhook secret. Up to 2048 chars. | | `Idempotency-Key` | header | string | No | | Retry-safe key: a retry with the same key and body within 24 hours returns the first job instead of starting (and charging) again. The same key with a different body is a 409. MCP clients pass it as the idempotencyKey argument. 1-255 chars. MCP: `idempotencyKey`. | ### Example curl: ```bash # 1. Dry run: the exact cost, nothing is spent. curl https://api.clippie.ai/v1/ai/recast \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "motionTemplateId": "MOTION_TEMPLATE_ID", "photo": "https://example.com/me.jpg", "dryRun": true }' # => { "dryRun": true, "estimate": { "credits": ..., "minutes": { ... }, "wouldSucceed": true } } # 2. The real call. Keep the key and send the same one if you retry, so it is never charged twice. KEY=$(uuidgen) curl https://api.clippie.ai/v1/ai/recast \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $KEY" \ -d '{ "motionTemplateId": "MOTION_TEMPLATE_ID", "photo": "https://example.com/me.jpg" }' # => 202 { "job": { "id": "job_...", "status": "queued", ... } } ``` recast-video.mjs: ```js import { randomUUID } from 'node:crypto'; const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const input = { "motionTemplateId": "MOTION_TEMPLATE_ID", "photo": "https://example.com/me.jpg" }; async function call(body, headers = {}) { const res = await fetch('https://api.clippie.ai/v1/ai/recast', { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json', ...headers }, body: JSON.stringify(body), }); const json = await res.json(); if (!res.ok) throw new Error(`${json.error.type}: ${json.error.message}`); return json; } // 1. Dry run: the exact cost, nothing is spent. Confirm it before going on. const { estimate } = await call({ ...input, dryRun: true }); console.log(estimate); // { credits, minutes, wouldSucceed, reason } // 2. Create. Keep the key and reuse it on retries, so a retry never charges twice. let { job } = await call(input, { 'Idempotency-Key': randomUUID() }); // 3. Wait. Each call holds for up to 30 seconds until the job changes. while (!['completed', 'failed', 'canceled'].includes(job.status)) { const res = await fetch(`https://api.clippie.ai/v1/jobs/${job.id}?wait=30`, { headers: auth }); ({ job } = await res.json()); } console.log(job.status, job.output); ``` ### Response `202 { job }`: the job, straight away. Wait for it with [Get job](https://clippie.ai/developers/docs/get-job.md) (`?wait=30`) or a `webhookUrl`; see [Jobs and waiting](https://clippie.ai/developers/docs.md#jobs). With `"dryRun": true` the answer is `200 { dryRun: true, estimate }` and nothing is started. 202 Accepted: ```json { "job": { "id": "job_abc123", "object": "job", "kind": "recast_video", "status": "queued", "step": null, "progress": null, "input": { "prompt": "The lost library of Alexandria" }, "output": null, "cost": { "credits": 0, "minutes": { "voiceover": 1, "captions": 1, "export": 1 } }, "error": null, "source": "api", "webhookUrl": null, "createdAt": "2026-10-24T12:00:00.000Z", "startedAt": null, "completedAt": null } } ``` Dry run (200), shape only: ```json { "dryRun": true, "estimate": { "credits": 0, "minutes": { "voiceover": 0, "captions": 0, "export": 0 }, "wouldSucceed": true } } ``` ### From an MCP client Your AI calls `recast_video` with the same fields as JSON arguments, plus `idempotencyKey` instead of the header. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "recast_video", "arguments": { "motionTemplateId": "MOTION_TEMPLATE_ID", "photo": "https://example.com/me.jpg", "idempotencyKey": "a-unique-key-per-thing-you-make" } } ``` --- ## Upscale an image or video URL: https://clippie.ai/developers/docs/upscale Upscales an image (2x-6x) or a video (to 720p, 1080p or 4K) with Topaz. Spends credits: about 7 for a 2x image (more at 4x and 6x) and 155 for a video of up to 30 seconds to 1080p at 60 fps. The price follows the real file: bigger images, longer videos (up to 10 minutes) and 4K cost more. Run with dryRun: true (or estimate_cost) first and confirm the cost. Images finish within the call; videos return a running job, so use get_job with waitSeconds. ### At a glance | Property | Value | | --- | --- | | REST | `POST https://api.clippie.ai/v1/ai/upscale` | | MCP tool | `upscale` | | Scope | `create` | | Cost | Spends credits or minutes, at the same prices as the app. Send `"dryRun": true` (or call [estimate_cost](https://clippie.ai/developers/docs/estimate-cost.md)) first to see the exact cost without spending anything. | | Limit bucket | `upscale` (see [Rate limits](https://clippie.ai/developers/docs.md#rate-limits)) | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `fileId` | body | string | No | | The image or video to upscale: a file id from POST /v1/files. 1-80 chars. | | `url` | body | string | No | | The image or video to upscale: a file id from POST /v1/files (file_…) or a public https URL (copied into Clippie first, up to 100 MB). 1-2048 chars. | | `mediaType` | body | string | No | | Optional check: refuse the call unless the file is this type. One of `image`, `video`. | | `upscaleFactor` | body | string | No | `"2x"` | Images: how much bigger. One of `2x`, `4x`, `6x`. | | `enhanceModel` | body | string | No | `"Standard V2"` | One of `Standard V2`, `Low Resolution V2`, `CGI`, `High Fidelity V2`, `Text Refine`. | | `outputFormat` | body | string | No | `"jpg"` | One of `jpg`, `png`, `webp`. | | `faceEnhancement` | body | boolean | No | `false` | | | `targetResolution` | body | string | No | `"1080p"` | Videos: output resolution. One of `720p`, `1080p`, `4k`. | | `targetFps` | body | integer | No | `60` | Videos: output frame rate. 15-60. | | `dryRun` | body | boolean | No | | Check the request and return { estimate } (credits and minutes) without spending anything. | | `webhookUrl` | body | string (URL) | No | | https URL that receives job.completed / job.failed, signed with your webhook secret. Up to 2048 chars. | | `Idempotency-Key` | header | string | No | | Retry-safe key: a retry with the same key and body within 24 hours returns the first job instead of starting (and charging) again. The same key with a different body is a 409. MCP clients pass it as the idempotencyKey argument. 1-255 chars. MCP: `idempotencyKey`. | ### Example curl: ```bash # 1. Dry run: the exact cost, nothing is spent. curl https://api.clippie.ai/v1/ai/upscale \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/photo.jpg", "upscaleFactor": "2x", "dryRun": true }' # => { "dryRun": true, "estimate": { "credits": ..., "minutes": { ... }, "wouldSucceed": true } } # 2. The real call. Keep the key and send the same one if you retry, so it is never charged twice. KEY=$(uuidgen) curl https://api.clippie.ai/v1/ai/upscale \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $KEY" \ -d '{ "url": "https://example.com/photo.jpg", "upscaleFactor": "2x" }' # => 202 { "job": { "id": "job_...", "status": "queued", ... } } ``` upscale.mjs: ```js import { randomUUID } from 'node:crypto'; const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const input = { "url": "https://example.com/photo.jpg", "upscaleFactor": "2x" }; async function call(body, headers = {}) { const res = await fetch('https://api.clippie.ai/v1/ai/upscale', { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json', ...headers }, body: JSON.stringify(body), }); const json = await res.json(); if (!res.ok) throw new Error(`${json.error.type}: ${json.error.message}`); return json; } // 1. Dry run: the exact cost, nothing is spent. Confirm it before going on. const { estimate } = await call({ ...input, dryRun: true }); console.log(estimate); // { credits, minutes, wouldSucceed, reason } // 2. Create. Keep the key and reuse it on retries, so a retry never charges twice. let { job } = await call(input, { 'Idempotency-Key': randomUUID() }); // 3. Wait. Each call holds for up to 30 seconds until the job changes. while (!['completed', 'failed', 'canceled'].includes(job.status)) { const res = await fetch(`https://api.clippie.ai/v1/jobs/${job.id}?wait=30`, { headers: auth }); ({ job } = await res.json()); } console.log(job.status, job.output); ``` ### Response `202 { job }`: the job, straight away. Wait for it with [Get job](https://clippie.ai/developers/docs/get-job.md) (`?wait=30`) or a `webhookUrl`; see [Jobs and waiting](https://clippie.ai/developers/docs.md#jobs). With `"dryRun": true` the answer is `200 { dryRun: true, estimate }` and nothing is started. 202 Accepted: ```json { "job": { "id": "job_abc123", "object": "job", "kind": "upscale", "status": "queued", "step": null, "progress": null, "input": { "prompt": "The lost library of Alexandria" }, "output": null, "cost": { "credits": 0, "minutes": { "voiceover": 1, "captions": 1, "export": 1 } }, "error": null, "source": "api", "webhookUrl": null, "createdAt": "2026-10-24T12:00:00.000Z", "startedAt": null, "completedAt": null } } ``` Dry run (200), shape only: ```json { "dryRun": true, "estimate": { "credits": 0, "minutes": { "voiceover": 0, "captions": 0, "export": 0 }, "wouldSucceed": true } } ``` ### From an MCP client Your AI calls `upscale` with the same fields as JSON arguments, plus `idempotencyKey` instead of the header. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "upscale", "arguments": { "url": "https://example.com/photo.jpg", "upscaleFactor": "2x", "idempotencyKey": "a-unique-key-per-thing-you-make" } } ``` --- ## Text to speech URL: https://clippie.ai/developers/docs/text-to-speech Turns text into an mp3 narration with a Clippie voice or one of your cloned voices, with word timings. Spends voiceover minutes for the audio's length (about 150 words a minute), and the same in caption minutes for the word timings. Finishes within the call: the job comes back completed with audioUrl and captions.words. ### At a glance | Property | Value | | --- | --- | | REST | `POST https://api.clippie.ai/v1/ai/speech` | | MCP tool | `text_to_speech` | | Scope | `create` | | Cost | Spends credits or minutes, at the same prices as the app. Send `"dryRun": true` (or call [estimate_cost](https://clippie.ai/developers/docs/estimate-cost.md)) first to see the exact cost without spending anything. | | Limit bucket | `voiceover` (see [Rate limits](https://clippie.ai/developers/docs.md#rate-limits)) | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `text` | body | string | Yes | | What to say. 1-5000 chars. | | `voiceId` | body | string | Yes | | A voice id from list_voices (Clippie's or your own). 1-64 chars. | | `stability` | body | number | No | `0.5` | 0.01-1. | | `similarity` | body | number | No | `0.75` | 0.01-1. | | `language` | body | string | No | | Language code, e.g. "en". Up to 10 chars. | | `wordTimestamps` | body | boolean | No | `true` | Also return word timings (transcribes the audio, which uses caption minutes too). | | `dryRun` | body | boolean | No | | Check the request and return { estimate } (credits and minutes) without spending anything. | | `webhookUrl` | body | string (URL) | No | | https URL that receives job.completed / job.failed, signed with your webhook secret. Up to 2048 chars. | | `Idempotency-Key` | header | string | No | | Retry-safe key: a retry with the same key and body within 24 hours returns the first job instead of starting (and charging) again. The same key with a different body is a 409. MCP clients pass it as the idempotencyKey argument. 1-255 chars. MCP: `idempotencyKey`. | ### Example curl: ```bash # 1. Dry run: the exact cost, nothing is spent. curl https://api.clippie.ai/v1/ai/speech \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "text": "Clippie turns your ideas into finished videos.", "voiceId": "VOICE_ID", "dryRun": true }' # => { "dryRun": true, "estimate": { "credits": ..., "minutes": { ... }, "wouldSucceed": true } } # 2. The real call. Keep the key and send the same one if you retry, so it is never charged twice. KEY=$(uuidgen) curl https://api.clippie.ai/v1/ai/speech \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $KEY" \ -d '{ "text": "Clippie turns your ideas into finished videos.", "voiceId": "VOICE_ID" }' # => 202 { "job": { "id": "job_...", "status": "queued", ... } } ``` text-to-speech.mjs: ```js import { randomUUID } from 'node:crypto'; const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const input = { "text": "Clippie turns your ideas into finished videos.", "voiceId": "VOICE_ID" }; async function call(body, headers = {}) { const res = await fetch('https://api.clippie.ai/v1/ai/speech', { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json', ...headers }, body: JSON.stringify(body), }); const json = await res.json(); if (!res.ok) throw new Error(`${json.error.type}: ${json.error.message}`); return json; } // 1. Dry run: the exact cost, nothing is spent. Confirm it before going on. const { estimate } = await call({ ...input, dryRun: true }); console.log(estimate); // { credits, minutes, wouldSucceed, reason } // 2. Create. Keep the key and reuse it on retries, so a retry never charges twice. let { job } = await call(input, { 'Idempotency-Key': randomUUID() }); // 3. Wait. Each call holds for up to 30 seconds until the job changes. while (!['completed', 'failed', 'canceled'].includes(job.status)) { const res = await fetch(`https://api.clippie.ai/v1/jobs/${job.id}?wait=30`, { headers: auth }); ({ job } = await res.json()); } console.log(job.status, job.output); ``` ### Response `202 { job }`: the job, straight away. Wait for it with [Get job](https://clippie.ai/developers/docs/get-job.md) (`?wait=30`) or a `webhookUrl`; see [Jobs and waiting](https://clippie.ai/developers/docs.md#jobs). With `"dryRun": true` the answer is `200 { dryRun: true, estimate }` and nothing is started. 202 Accepted: ```json { "job": { "id": "job_abc123", "object": "job", "kind": "text_to_speech", "status": "queued", "step": null, "progress": null, "input": { "prompt": "The lost library of Alexandria" }, "output": null, "cost": { "credits": 0, "minutes": { "voiceover": 1, "captions": 1, "export": 1 } }, "error": null, "source": "api", "webhookUrl": null, "createdAt": "2026-10-24T12:00:00.000Z", "startedAt": null, "completedAt": null } } ``` Dry run (200), shape only: ```json { "dryRun": true, "estimate": { "credits": 0, "minutes": { "voiceover": 0, "captions": 0, "export": 0 }, "wouldSucceed": true } } ``` ### From an MCP client Your AI calls `text_to_speech` with the same fields as JSON arguments, plus `idempotencyKey` instead of the header. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "text_to_speech", "arguments": { "text": "Clippie turns your ideas into finished videos.", "voiceId": "VOICE_ID", "idempotencyKey": "a-unique-key-per-thing-you-make" } } ``` --- ## Transcribe audio or video URL: https://clippie.ai/developers/docs/transcribe Transcribes an audio or video file (up to 30 minutes) into punctuated text, word timings, and SRT and WebVTT captions split at sentence ends. Spends caption minutes for the file's length. Finishes within the call: the job comes back completed with captions.words, captions.srt and captions.vtt. ### At a glance | Property | Value | | --- | --- | | REST | `POST https://api.clippie.ai/v1/ai/transcribe` | | MCP tool | `transcribe` | | Scope | `create` | | Cost | Spends credits or minutes, at the same prices as the app. Send `"dryRun": true` (or call [estimate_cost](https://clippie.ai/developers/docs/estimate-cost.md)) first to see the exact cost without spending anything. | | Limit bucket | `transcribe` (see [Rate limits](https://clippie.ai/developers/docs.md#rate-limits)) | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `fileId` | body | string | No | | An audio or video file id from POST /v1/files. 1-80 chars. | | `url` | body | string | No | | The audio or video to transcribe: a file id from POST /v1/files (file_…) or a public https URL (copied into Clippie first, up to 100 MB). 1-2048 chars. | | `language` | body | string | No | | Language code, e.g. "en". Detected when left out. 2-10 chars. | | `dryRun` | body | boolean | No | | Check the request and return { estimate } (credits and minutes) without spending anything. | | `webhookUrl` | body | string (URL) | No | | https URL that receives job.completed / job.failed, signed with your webhook secret. Up to 2048 chars. | | `Idempotency-Key` | header | string | No | | Retry-safe key: a retry with the same key and body within 24 hours returns the first job instead of starting (and charging) again. The same key with a different body is a 409. MCP clients pass it as the idempotencyKey argument. 1-255 chars. MCP: `idempotencyKey`. | ### Example curl: ```bash # 1. Dry run: the exact cost, nothing is spent. curl https://api.clippie.ai/v1/ai/transcribe \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/clip.mp4", "language": "en", "dryRun": true }' # => { "dryRun": true, "estimate": { "credits": ..., "minutes": { ... }, "wouldSucceed": true } } # 2. The real call. Keep the key and send the same one if you retry, so it is never charged twice. KEY=$(uuidgen) curl https://api.clippie.ai/v1/ai/transcribe \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $KEY" \ -d '{ "url": "https://example.com/clip.mp4", "language": "en" }' # => 202 { "job": { "id": "job_...", "status": "queued", ... } } ``` transcribe.mjs: ```js import { randomUUID } from 'node:crypto'; const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const input = { "url": "https://example.com/clip.mp4", "language": "en" }; async function call(body, headers = {}) { const res = await fetch('https://api.clippie.ai/v1/ai/transcribe', { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json', ...headers }, body: JSON.stringify(body), }); const json = await res.json(); if (!res.ok) throw new Error(`${json.error.type}: ${json.error.message}`); return json; } // 1. Dry run: the exact cost, nothing is spent. Confirm it before going on. const { estimate } = await call({ ...input, dryRun: true }); console.log(estimate); // { credits, minutes, wouldSucceed, reason } // 2. Create. Keep the key and reuse it on retries, so a retry never charges twice. let { job } = await call(input, { 'Idempotency-Key': randomUUID() }); // 3. Wait. Each call holds for up to 30 seconds until the job changes. while (!['completed', 'failed', 'canceled'].includes(job.status)) { const res = await fetch(`https://api.clippie.ai/v1/jobs/${job.id}?wait=30`, { headers: auth }); ({ job } = await res.json()); } console.log(job.status, job.output); ``` ### Response `202 { job }`: the job, straight away. Wait for it with [Get job](https://clippie.ai/developers/docs/get-job.md) (`?wait=30`) or a `webhookUrl`; see [Jobs and waiting](https://clippie.ai/developers/docs.md#jobs). With `"dryRun": true` the answer is `200 { dryRun: true, estimate }` and nothing is started. 202 Accepted: ```json { "job": { "id": "job_abc123", "object": "job", "kind": "transcribe", "status": "queued", "step": null, "progress": null, "input": { "prompt": "The lost library of Alexandria" }, "output": null, "cost": { "credits": 0, "minutes": { "voiceover": 1, "captions": 1, "export": 1 } }, "error": null, "source": "api", "webhookUrl": null, "createdAt": "2026-10-24T12:00:00.000Z", "startedAt": null, "completedAt": null } } ``` Dry run (200), shape only: ```json { "dryRun": true, "estimate": { "credits": 0, "minutes": { "voiceover": 0, "captions": 0, "export": 0 }, "wouldSucceed": true } } ``` ### From an MCP client Your AI calls `transcribe` with the same fields as JSON arguments, plus `idempotencyKey` instead of the header. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "transcribe", "arguments": { "url": "https://example.com/clip.mp4", "language": "en", "idempotencyKey": "a-unique-key-per-thing-you-make" } } ``` --- ## Write a script URL: https://clippie.ai/developers/docs/write-script Writes a narration script for a short video from a topic, with the AI Story editor's writer, a theme, a tone and a target length. Free: spends no credits or minutes. Finishes within the call: the job comes back completed with output.script. Pass the script on to text_to_speech or create_ai_story_video. ### At a glance | Property | Value | | --- | --- | | REST | `POST https://api.clippie.ai/v1/ai/scripts` | | MCP tool | `write_script` | | Scope | `create` | | Cost | Spends nothing, but starts a job like the other create calls and needs the `create` scope. | | Limit bucket | `llm` (see [Rate limits](https://clippie.ai/developers/docs.md#rate-limits)) | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `prompt` | body | string | Yes | | The topic or idea, e.g. "the fall of the Roman Empire". 1-2000 chars. | | `durationSeconds` | body | integer | No | `60` | Target narration length, 30-300 seconds. 30-300. | | `theme` | body | string | No | `"any"` | One of `any`, `historical`, `scientific`, `geography`, `politics`, `nature`, `religion`, `technology`, `psychology`, `mystery`, `space`, `mythology`, `war`. | | `tone` | body | string | No | `"cinematic"` | One of `cinematic`, `suspenseful`, `educational`, `dark`, `inspirational`, `humorous`, `dramatic`, `casual`, `horror`, `scary`, `thriller`, `creepy`. | | `dryRun` | body | boolean | No | | Check the request and return { estimate } (credits and minutes) without spending anything. | | `webhookUrl` | body | string (URL) | No | | https URL that receives job.completed / job.failed, signed with your webhook secret. Up to 2048 chars. | | `Idempotency-Key` | header | string | No | | Retry-safe key: a retry with the same key and body within 24 hours returns the first job instead of starting (and charging) again. The same key with a different body is a 409. MCP clients pass it as the idempotencyKey argument. 1-255 chars. MCP: `idempotencyKey`. | ### Example curl: ```bash # 1. Dry run: the exact cost, nothing is spent. curl https://api.clippie.ai/v1/ai/scripts \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "prompt": "Why octopuses have three hearts", "durationSeconds": 60, "tone": "educational", "dryRun": true }' # => { "dryRun": true, "estimate": { "credits": ..., "minutes": { ... }, "wouldSucceed": true } } # 2. The real call. Keep the key and send the same one if you retry, so it is never charged twice. KEY=$(uuidgen) curl https://api.clippie.ai/v1/ai/scripts \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $KEY" \ -d '{ "prompt": "Why octopuses have three hearts", "durationSeconds": 60, "tone": "educational" }' # => 202 { "job": { "id": "job_...", "status": "queued", ... } } ``` write-script.mjs: ```js import { randomUUID } from 'node:crypto'; const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const input = { "prompt": "Why octopuses have three hearts", "durationSeconds": 60, "tone": "educational" }; async function call(body, headers = {}) { const res = await fetch('https://api.clippie.ai/v1/ai/scripts', { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json', ...headers }, body: JSON.stringify(body), }); const json = await res.json(); if (!res.ok) throw new Error(`${json.error.type}: ${json.error.message}`); return json; } // 1. Dry run: the exact cost, nothing is spent. Confirm it before going on. const { estimate } = await call({ ...input, dryRun: true }); console.log(estimate); // { credits, minutes, wouldSucceed, reason } // 2. Create. Keep the key and reuse it on retries, so a retry never charges twice. let { job } = await call(input, { 'Idempotency-Key': randomUUID() }); // 3. Wait. Each call holds for up to 30 seconds until the job changes. while (!['completed', 'failed', 'canceled'].includes(job.status)) { const res = await fetch(`https://api.clippie.ai/v1/jobs/${job.id}?wait=30`, { headers: auth }); ({ job } = await res.json()); } console.log(job.status, job.output); ``` ### Response `202 { job }`: the job, straight away. Wait for it with [Get job](https://clippie.ai/developers/docs/get-job.md) (`?wait=30`) or a `webhookUrl`; see [Jobs and waiting](https://clippie.ai/developers/docs.md#jobs). With `"dryRun": true` the answer is `200 { dryRun: true, estimate }` and nothing is started. 202 Accepted: ```json { "job": { "id": "job_abc123", "object": "job", "kind": "write_script", "status": "queued", "step": null, "progress": null, "input": { "prompt": "The lost library of Alexandria" }, "output": null, "cost": { "credits": 0, "minutes": { "voiceover": 1, "captions": 1, "export": 1 } }, "error": null, "source": "api", "webhookUrl": null, "createdAt": "2026-10-24T12:00:00.000Z", "startedAt": null, "completedAt": null } } ``` Dry run (200), shape only: ```json { "dryRun": true, "estimate": { "credits": 0, "minutes": { "voiceover": 0, "captions": 0, "export": 0 }, "wouldSucceed": true } } ``` ### From an MCP client Your AI calls `write_script` with the same fields as JSON arguments, plus `idempotencyKey` instead of the header. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "write_script", "arguments": { "prompt": "Why octopuses have three hearts", "durationSeconds": 60, "tone": "educational", "idempotencyKey": "a-unique-key-per-thing-you-make" } } ``` --- ## Create AI Story video URL: https://clippie.ai/developers/docs/create-ai-story-video Makes a narrated AI Story video (script, voiceover, scene images, captions, music) and renders it to MP4. Give one of prompt (a script is written to fill durationSeconds, 30-300, default 60), script, or voiceoverFileId. Spends minutes, not credits: about 1 voiceover, 1 caption and 1 export minute per minute of video (render: false skips the export minutes and returns an editable project). Run estimate_cost (or dryRun: true) first and confirm the cost. Takes a few minutes: wait with get_job (waitSeconds). At most 3 videos are made at once per account. The result has videoUrl and editorUrl. ### At a glance | Property | Value | | --- | --- | | REST | `POST https://api.clippie.ai/v1/videos/ai-story` | | MCP tool | `create_ai_story_video` | | Scope | `create` | | Cost | Spends credits or minutes, at the same prices as the app. Send `"dryRun": true` (or call [estimate_cost](https://clippie.ai/developers/docs/estimate-cost.md)) first to see the exact cost without spending anything. | | Limit bucket | `render` (see [Rate limits](https://clippie.ai/developers/docs.md#rate-limits)) | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `prompt` | body | string | No | | What the video is about; a script is written from it to fill durationSeconds. Give one of prompt, script or voiceoverFileId. 3-2000 chars. | | `script` | body | string | No | | The exact narration to use (it sets the length; durationSeconds is ignored). 20-12000 chars. | | `voiceoverFileId` | body | string | No | | file_… id of your own narration audio (upload it with upload_file). It is transcribed for captions; no voice is generated. Up to 5 minutes. Up to 80 chars. | | `title` | body | string | No | | Project and file name. Defaults to the start of the prompt or script. 1-120 chars. | | `storyTemplateId` | body | string | No | | A format from list_story_templates: sets the look and the aspect ratio in one go. artStyleId, imageModel and aspectRatio override it. Up to 64 chars. | | `voiceId` | body | string | No | | Narration voice id from list_voices (catalog voices or your own cloned ones). Defaults to your brand voice. 1-64 chars. | | `artStyleId` | body | string | No | | A look from list_art_styles: a type "style" id, or a type "stylePrompt" id (rendered with imageModel, default seedream-5.0-lite). Up to 64 chars. | | `imageModel` | body | string | No | | Draw the scene images with this model (from list_art_styles imageModels) instead of a named style. Included with paid plans. One of `nano-banana`, `nano-banana-2.1`, `nano-banana-pro`, `seedream-5.0-lite`, `flux-2-pro`. | | `stylePromptId` | body | string | No | | Style preset (a type "stylePrompt" id) for imageModel. Up to 64 chars. | | `aspectRatio` | body | string | No | | Frame shape. Defaults to the story template, then your brand. One of `9:16`, `16:9`. | | `durationSeconds` | body | integer | No | | Length of the whole video in seconds (30-300, default 60) when the script is written from prompt. The script is sized to the voice's measured speaking speed. 30-300. | | `language` | body | string | No | | Narration and caption language code from list_languages (e.g. "en", "es", "ja"). Default: detected. Up to 10 chars. | | `captionStyleId` | body | integer | No | | Caption style id from list_caption_styles. Defaults to your brand caption style. 1-17. | | `musicId` | body | string | No | | Background music id from list_music, or "none" for no music. Defaults to your brand track. Up to 64 chars. | | `theme` | body | string | No | | Subject direction for a script written from prompt. One of `any`, `historical`, `scientific`, `geography`, `politics`, `nature`, `religion`, `technology`, `psychology`, `mystery`, `space`, `mythology`, `war`. | | `tone` | body | string | No | | Voice and pacing for a script written from prompt. One of `cinematic`, `suspenseful`, `educational`, `dark`, `inspirational`, `humorous`, `dramatic`, `casual`, `horror`, `scary`, `thriller`, `creepy`. | | `render` | body | boolean | No | | Default true: render the MP4 (spends export minutes). false keeps an editable project and returns only editorUrl. | | `dryRun` | body | boolean | No | | Check the request and return { estimate } (credits and minutes) without spending anything. | | `webhookUrl` | body | string (URL) | No | | https URL that receives job.completed / job.failed, signed with your webhook secret. Up to 2048 chars. | | `Idempotency-Key` | header | string | No | | Retry-safe key: a retry with the same key and body within 24 hours returns the first job instead of starting (and charging) again. The same key with a different body is a 409. MCP clients pass it as the idempotencyKey argument. 1-255 chars. MCP: `idempotencyKey`. | ### Example curl: ```bash # 1. Dry run: the exact cost, nothing is spent. curl https://api.clippie.ai/v1/videos/ai-story \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "prompt": "The lost library of Alexandria", "aspectRatio": "9:16", "durationSeconds": 60, "dryRun": true }' # => { "dryRun": true, "estimate": { "credits": ..., "minutes": { ... }, "wouldSucceed": true } } # 2. The real call. Keep the key and send the same one if you retry, so it is never charged twice. KEY=$(uuidgen) curl https://api.clippie.ai/v1/videos/ai-story \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $KEY" \ -d '{ "prompt": "The lost library of Alexandria", "aspectRatio": "9:16", "durationSeconds": 60 }' # => 202 { "job": { "id": "job_...", "status": "queued", ... } } ``` create-ai-story-video.mjs: ```js import { randomUUID } from 'node:crypto'; const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const input = { "prompt": "The lost library of Alexandria", "aspectRatio": "9:16", "durationSeconds": 60 }; async function call(body, headers = {}) { const res = await fetch('https://api.clippie.ai/v1/videos/ai-story', { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json', ...headers }, body: JSON.stringify(body), }); const json = await res.json(); if (!res.ok) throw new Error(`${json.error.type}: ${json.error.message}`); return json; } // 1. Dry run: the exact cost, nothing is spent. Confirm it before going on. const { estimate } = await call({ ...input, dryRun: true }); console.log(estimate); // { credits, minutes, wouldSucceed, reason } // 2. Create. Keep the key and reuse it on retries, so a retry never charges twice. let { job } = await call(input, { 'Idempotency-Key': randomUUID() }); // 3. Wait. Each call holds for up to 30 seconds until the job changes. while (!['completed', 'failed', 'canceled'].includes(job.status)) { const res = await fetch(`https://api.clippie.ai/v1/jobs/${job.id}?wait=30`, { headers: auth }); ({ job } = await res.json()); } console.log(job.status, job.output); ``` ### Response `202 { job }`: the job, straight away. Wait for it with [Get job](https://clippie.ai/developers/docs/get-job.md) (`?wait=30`) or a `webhookUrl`; see [Jobs and waiting](https://clippie.ai/developers/docs.md#jobs). With `"dryRun": true` the answer is `200 { dryRun: true, estimate }` and nothing is started. 202 Accepted: ```json { "job": { "id": "job_abc123", "object": "job", "kind": "create_ai_story_video", "status": "queued", "step": null, "progress": null, "input": { "prompt": "The lost library of Alexandria" }, "output": null, "cost": { "credits": 0, "minutes": { "voiceover": 1, "captions": 1, "export": 1 } }, "error": null, "source": "api", "webhookUrl": null, "createdAt": "2026-10-24T12:00:00.000Z", "startedAt": null, "completedAt": null } } ``` Dry run (200), shape only: ```json { "dryRun": true, "estimate": { "credits": 0, "minutes": { "voiceover": 0, "captions": 0, "export": 0 }, "wouldSucceed": true } } ``` ### From an MCP client Your AI calls `create_ai_story_video` with the same fields as JSON arguments, plus `idempotencyKey` instead of the header. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "create_ai_story_video", "arguments": { "prompt": "The lost library of Alexandria", "aspectRatio": "9:16", "durationSeconds": 60, "idempotencyKey": "a-unique-key-per-thing-you-make" } } ``` --- ## Render video URL: https://clippie.ai/developers/docs/render-video Renders an AI Story project you own to MP4: one made with create_ai_story_video (render: false) or in the Clippie editor. Spends export minutes for the video length (1 per minute of video). Run estimate_cost (or dryRun: true) first. Projects with placeholder scene images are refused: regenerate those scenes in the editor first. ### At a glance | Property | Value | | --- | --- | | REST | `POST https://api.clippie.ai/v1/videos/:projectId/render` | | MCP tool | `render_video` | | Scope | `create` | | Cost | Spends credits or minutes, at the same prices as the app. Send `"dryRun": true` (or call [estimate_cost](https://clippie.ai/developers/docs/estimate-cost.md)) first to see the exact cost without spending anything. | | Limit bucket | `render` (see [Rate limits](https://clippie.ai/developers/docs.md#rate-limits)) | ### Parameters | Name | In | Type | Required | Default | Description | | --- | --- | --- | --- | --- | --- | | `projectId` | path | string | Yes | | The AI Story project id: output.projectId of a job, or the id in an editor URL. 1-64 chars. | | `dryRun` | body | boolean | No | | Check the request and return { estimate } (credits and minutes) without spending anything. | | `webhookUrl` | body | string (URL) | No | | https URL that receives job.completed / job.failed, signed with your webhook secret. Up to 2048 chars. | | `Idempotency-Key` | header | string | No | | Retry-safe key: a retry with the same key and body within 24 hours returns the first job instead of starting (and charging) again. The same key with a different body is a 409. MCP clients pass it as the idempotencyKey argument. 1-255 chars. MCP: `idempotencyKey`. | ### Example curl: ```bash # 1. Dry run: the exact cost, nothing is spent. curl https://api.clippie.ai/v1/videos/PROJECT_ID/render \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "dryRun": true }' # => { "dryRun": true, "estimate": { "credits": ..., "minutes": { ... }, "wouldSucceed": true } } # 2. The real call. Keep the key and send the same one if you retry, so it is never charged twice. KEY=$(uuidgen) curl https://api.clippie.ai/v1/videos/PROJECT_ID/render \ -H "Authorization: Bearer $CLIPPIE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $KEY" \ -d '{}' # => 202 { "job": { "id": "job_...", "status": "queued", ... } } ``` render-video.mjs: ```js import { randomUUID } from 'node:crypto'; const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` }; const input = {}; async function call(body, headers = {}) { const res = await fetch('https://api.clippie.ai/v1/videos/PROJECT_ID/render', { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json', ...headers }, body: JSON.stringify(body), }); const json = await res.json(); if (!res.ok) throw new Error(`${json.error.type}: ${json.error.message}`); return json; } // 1. Dry run: the exact cost, nothing is spent. Confirm it before going on. const { estimate } = await call({ ...input, dryRun: true }); console.log(estimate); // { credits, minutes, wouldSucceed, reason } // 2. Create. Keep the key and reuse it on retries, so a retry never charges twice. let { job } = await call(input, { 'Idempotency-Key': randomUUID() }); // 3. Wait. Each call holds for up to 30 seconds until the job changes. while (!['completed', 'failed', 'canceled'].includes(job.status)) { const res = await fetch(`https://api.clippie.ai/v1/jobs/${job.id}?wait=30`, { headers: auth }); ({ job } = await res.json()); } console.log(job.status, job.output); ``` ### Response `202 { job }`: the job, straight away. Wait for it with [Get job](https://clippie.ai/developers/docs/get-job.md) (`?wait=30`) or a `webhookUrl`; see [Jobs and waiting](https://clippie.ai/developers/docs.md#jobs). With `"dryRun": true` the answer is `200 { dryRun: true, estimate }` and nothing is started. 202 Accepted: ```json { "job": { "id": "job_abc123", "object": "job", "kind": "render_video", "status": "queued", "step": null, "progress": null, "input": { "prompt": "The lost library of Alexandria" }, "output": null, "cost": { "credits": 0, "minutes": { "voiceover": 1, "captions": 1, "export": 1 } }, "error": null, "source": "api", "webhookUrl": null, "createdAt": "2026-10-24T12:00:00.000Z", "startedAt": null, "completedAt": null } } ``` Dry run (200), shape only: ```json { "dryRun": true, "estimate": { "credits": 0, "minutes": { "voiceover": 0, "captions": 0, "export": 0 }, "wouldSucceed": true } } ``` ### From an MCP client Your AI calls `render_video` with the same fields as JSON arguments, plus `idempotencyKey` instead of the header. See [MCP server](https://clippie.ai/developers/docs.md#mcp) to connect one. tools/call: ```json { "name": "render_video", "arguments": { "projectId": "PROJECT_ID", "idempotencyKey": "a-unique-key-per-thing-you-make" } } ```