OpusClip AI Producer turns raw footage into polished talking-head videos with AI-assisted editing. Trim clips, generate captions, enhance voice audio, reframe speakers, add B-roll and motion graphics, review transcripts, and export finished videos.
Encrypted at rest, isolated from the model
Resolved from an AES-256-GCM vault at the moment of the call and attached to the request — the model never sees the secrets.
Try asking
Opens the AI Producer sidebar app with published video examples. Returns title, message and next_data. Call it without arguments; opening it creates nothing. When the user then attaches a video and asks for an edit, follow the AI Producer plugin's aip skill: it starts the AI Producer project with these tools. The app itself passes open to show its home, your videos or a project in its frame. Billing: free.
Opens one of your projects inside the AI Producer sidebar app. Returns exchange_url, a single-use sign-in link for the app's own frame. Called by the app, not the model. Billing: free.
Opens your AI Producer videos inside the AI Producer sidebar app. Returns exchange_url, a single-use sign-in link for the app's own frame. Called by the app, not the model. Billing: free.
Searches your AI Producer projects by name for the composer's @ menu. Returns items, resource links to at most 20 projects, newest first. Called by the host, not the model. Billing: free.
Read the live state of the exact task a mutation on this surface returned. Terminal exactly when status is one of done | failed | cancelled; settled only then. succeeded is the outcome to branch on and done is only the lifecycle: a commit_workspace round that refused every file is done with succeeded false, and answers that round in result (outcome, accepted, issues, next_action). failed carries error, and a failed export's result names what to repair: result.issues (composition_id, path, code, cause) or result.planner (reason, duration_s, max_duration_s, plus file and check when it could not read the tree). Beside it, failure carries a stable code and a repair hint where one is known. Show failure.message to the user; error is bounded technical diagnostic text for repair, not user-facing copy. retryable: false means this edit can only fail the same way, null means retryability is unknown. stage, heartbeat_at, attempt and typical_duration_seconds describe progress. deferred is an export parked behind a live turn: it starts when that turn ends. Every reply carries page_url, the durable project page: the link a person can be given. wait_seconds holds the call open until the task settles or its status changes; next_data.poll_args is that held call again: one call per wait, no sleep. A terminal task that took a paid hold also carries billing: credits_held is the ceiling it was quoted, and once settled is true, credits_charged is what it actually took and billable_units what that paid for where the price is per unit. A task turns terminal before its charge settles, so settled false means the amount is not known yet and the next poll carries it; billing absent means no user-paid hold. A project's run_status is not this task's outcome. Billing: free.
Cancel one active orchestration task, queued or running. Cancellation is graceful rather than immediate: the worker finishes its terminal bookkeeping first, so status reaches cancelled some time after the request is accepted. A task holds its project's lock until it reaches a terminal status, and while it is held every tool that starts a stage or an edit on the project refuses with the active task id; a task nobody cancels holds the lock until it ends on its own. A paid task you cancel keeps what it had already spent, never more than the amount it held, and releases the rest. Billing: free.
Wait inside one call for the exact task a mutation on this surface returned, answering with get_task's fields once status is one of done | failed | cancelled or wait_seconds elapses; a status change alone does not answer. wait.woke_on names what ended the hold. A caller already waiting on 8 tasks is answered at once with woke_on hold_limit; a wait past 4 on one task ends the caller's oldest with woke_on superseded. Sends a notification every 15 seconds while held. A reply that is not terminal carries next_data.poll_args, this wait again. Billing: free.
Make an editable draft from a video_ref you PUT; needs a credit wallet. Cannot PUT: start_project_from_attachment; no video: ask, never invent a video_ref. Over 30 min (staff 60) is refused before billing. Returns project_id, task_id, agent_page_url, page_url (the link here to give a person); get_task reports preparation progress. A video with no speech or no audio track (a screen recording, a music clip) is accepted too; that project has no transcript, so build_captions, add_music and enhance_voice refuse it. Voice leveled, never isolated: a clean voice is the separately billed enhance_voice. Billing: free.
Write music, beat timing and a ducked audio mix. Send sections on every call; direction only steers them. Sound effects use the plan's sfx beats. Takes the project lock and returns task_id; get_task reports completion. Repeats start another priced run. Billing: free.
Enable voice enhancement for an editable project, or disable it; disabling reverts to the original audio. A separate task the user chooses explicitly: creation never runs it. Returns task_id to poll with get_task. Billing: free.
Find b-roll images: one search per query, then a vision pass picks the best for each; poll task_id with get_task. A query also in cutout_queries gets its subject cut out on a transparent background. The result maps each query to its image and lists files ({path, sha256}) staged for your next commit_workspace to promote with its batch; your tree is never written. Billing: free.
Read what every capability costs, in credits. Each row carries band (free / negligible / paid / expensive), credits per run, and available; a per_unit row also charges credits_per_unit per unit consumed, up to max_units. On a row without per_unit, credits is the whole price per run; read per_unit before quoting a row that has one, since such rows carry credits 0 and quoting that 0 calls a paid step free. billing.metered says whether credits move here; free_usage says how much free usage is left; project_id adds that project's spend. Every row is callable here. Billing: free.
Read the footage, optional reference and note (the creator's own words) sent from the AI Producer web page, by the web assignment id in their prompt. While a file uploads the call holds up to 45 seconds and answers next_data.state waiting; read again. wait false answers at once, next_data.reference_ready saying the reference can be read. ready carries start_args for start_transcribe_project; missing names the files that did not arrive and how to resend; started carries the project. Uploaded reference files and landed footage carry a 15-minute download_url. Billing: free.
List the reference style presets a creator can recreate by name: each preset's preset_id, name, look and category, from the current catalogue or the catalogue_version a web assignment carries. Read one with get_reference_preset. Billing: free.
Read one reference style preset: its name and look, and a 15-minute download_url to its folder, a zip of the reference analysis the reference-style skill reads, with sha256 and bytes to check. Use the catalogue_version a web assignment carries when it has one. A withdrawn preset is refused. Billing: free.
Create a project from a video the user attached in chat to edit, caption, or rough cut; the server fetches it. One per call; .mp4/.mov/.webm to 3584 MiB (2040 restricted) and 30 minutes (staff 60). A stub with no file is refused with a fix. Returns project_id, task_id, agent_page_url, page_url; get_task reports progress. A video with no speech or no audio track (a screen recording, a music clip) is accepted too; that project has no transcript, so build_captions, add_music and enhance_voice refuse it. Voice leveled, never isolated: a clean voice is the separately billed enhance_voice. Billing: free.
Bake the caption layer from the subtitle pattern the caller picks into compositions/narrator_captions.html and transcript-src.json. A host project's result carries host_div for index.html and files ({path, sha256}) to commit, adding those entries when you name expected_files, or they stay staged. position, placement, hide_intervals, caption_windows, emphasis_word_ids, font and accent are optional; one model call fills the pattern and emphasis left out. Billing: free.
Measure the speaker per window (cut seconds) and, with matte true, cut them out. The task result gives each window its on-camera share, face and head band, and for a slot the object_position that keeps the head whole and fits; a matted window adds its pop geometry (sink, card edge, headroom) and render-engine/public/speaker_<slug>.webm, staged for your next commit_workspace (media_start 0). Never writes your tree. Refused before compute: no transcript; a matte without a slot, off camera, unmeasured, or across a cut; over 8 windows; a matte over 30 s a window or 60 s a call. Billing: free.
Generate sound effects at the points you choose on the finished cut's timeline (at_ms) and mix them with the voice and any music. Send 1 to 12 hits in one call: each call replaces the project's previous sound-effect track. A hit that starts past the end of the cut is refused, not retried. Takes the project lock and returns task_id; get_task reports completion. Repeats start another priced run. Billing: free.
List the sound effect library with each sound's measurements: duration_ms, onset_ms, peak_ms (its loudest moment), hits and hits_ms (where each hit lands, ms from the sound's start), phone (audible on a phone speaker), tone_gate, use_when and suggested_gain_db (the level that sits it under speech), plus this project's project_revision and cut_end_ms. family narrows to one family; query matches every term against names and descriptions. Pages by limit and cursor. Billing: free.
Place library sounds on this project's cut in one undoable edit. Each hit names a sound_id, the at_ms its anchor (first_hit, peak or onset) lands on, and an optional gain_db; a sound is trimmed to the cut's edges, and a hit past the end is refused in refused. replace removes the effects this tool or the generated stage placed before, keeping a person's own. expected_project_revision and expected_library_version must equal list_sound_effects'. The same idempotency_key replays. Billing: free.
Mark an externally created, settled editable preview finished. Returns task_id, or success without a task if already finished. Refuses a busy or failed project or one without a settled editable preview. Does not assess editing quality, export or add treatments; the project remains editable. page_url opens the project in the product app, with its navigation and without the AIP chat, and is the durable link to give a person in the final reply. agent_page_url opens the editor view for embedded panels; it is not the final delivery link. Billing: free.
Parse one @aip reference line the user copied off a project page into data. A line names its project, optionally a moment in composition time, and either an element address or a frame region the user drew. references carries one entry per line in the paste, each with kind and the full address for that kind; composition_id is set only for a moment. A library card line has no time; read a motion-asset with get_motion_asset, a project-asset with get_project_asset. Refused when the line names a project other than project_id, or carries no readable reference. Nothing changes. Billing: free.
Read one stable page of a project's canonical chat transcript. The cursor is exclusive. Forward order starts at the oldest message and can enumerate the complete history; backward order starts at the newest. next_cursor is null on the last page. Billing: free.
Store one decision card's selections. The latest answer replaces that card's previous answer; [] records an empty selection. Returns choices and pending_choices. The archive does not affect execution, pricing or stage admission. Billing: free.
The editing preset saved on the calling account: the answers it keeps as defaults for a new project. Returns editing_preset, null when nothing is saved. The preset is a record of what the user chose; it admits no stage, changes no price, and no other tool on this server reads it. caption_style=no-caption means skip caption generation and leave the caption layer unmounted, not a pattern. Billing: free.
Stores the calling account's editing preset. The object sent replaces the saved one whole, so a key left out of it is a key dropped; null, or an object with no answers in it, clears the preset. Returns editing_preset as it now stands. The preset is a record of what the user chose; it admits no stage, changes no price, and no other tool on this server reads it. caption_style=no-caption means skip caption generation and leave the caption layer unmounted, not a pattern. Billing: free.
List one project's saved editable versions and which of them is live, one page per call in stored order; pass next_cursor back as cursor for the rest. Only a version_id returned here names a version: restore_version answers 404 for any other value. Read-only. Billing: free.
Replace the live editable preview with one saved version, through the editor's own restore path. It takes the project lock and returns task_id to poll with get_task; while another task holds the lock it refuses with that active task id. Only a version_id from list_versions is accepted; any other value answers 404. Billing: free.
Generate a scene restyle cover for each window and write it into the project workspace; a timeline this server built also gets it placed, one you authored does not. Up to 25 windows per call, each start_ms to end_ms spanning 4-10s of output time, non-overlapping; a window out of bounds is refused at no cost. Covers a submitted visual plan reserved are bought after your windows while the hold has room; the rest stay pending. A window already covered is not bought again; an identical window addresses the one it made. Each verdict in get_task.result carries the cover's workspace path, whether the timeline already carries it, how many generations this call was priced for (generations, billed_generated_seconds), and for a refused window its class, the provider's reason, and whether it could ever differ; that window keeps no cover, plays the raw footage, and is terminal: a re-run never re-attempts it. A content filter refusing a recognizable individual refuses every window of that speaker. A window may name a preset from list_motion_assets.scene_restyle; a preset outside this project's catalogue is refused at no cost. While the project's Scene restyle switch is off the call is refused at no cost unless enable_skill_quote carries the user's literal words asking for it, which turns the switch on for this project first. Billing: paid, 6 credits per started second of video generated, each window once and twice when the identity check rejects its first try, whether or not it ends with a cover, and never for a generation returned in the wrong frame shape; the hold is twice every window sent, and a window already covered, or refused before generating, costs nothing; get_pricing states the amount.
Sign PUT targets into one project's workspace. No bytes move: your shell PUTs each file to its upload_url, again if needed until upload_expires_at (last write wins); content_type is bound into the signature. Nothing is visible until commit_workspace accepts it. Writable paths are render-engine/index.html, render-engine/compositions/, render-engine/public/, render-engine/styles/, render-engine/fonts/. Other paths, and types no commit accepts (no script, no .svg: only markup, stylesheets, JSON, fonts, images, video and audio), land in rejected with a code; the rest in uploads. Billing: free.
List what one project's render-engine workspace holds, one page per call; pass next_cursor back as cursor. Each file carries path, size in bytes, and md5, the base64 MD5 the store holds (null means unknown). The listing is recursive under path_prefix and covers only render-engine/index.html, render-engine/compositions/, render-engine/public/, render-engine/styles/, render-engine/fonts/. digest is one token over the whole face on every page, whatever path_prefix or cursor; it changes on any write under the face, the editor's included, and is what base_digest is compared against. A cursor is bound to the digest it was read under: a continuation after the face changed is refused with 409 workspace_changed and paging restarts from the first page. staged (what you uploaded that no commit has accepted, by destination; in no render, visible to nobody) and last_promote (the latest commit_workspace round, which the next commit replaces; a commit's own verdict is get_task's result, null before any round) come on the first page only. Billing: free.
Read one file out of a project's render-engine workspace: what is deployed, not what you believe you uploaded. A text source comes back inline in content as utf-8, whole, or the window offset and max_chars name (the reply carries chars and next_offset), so a large file is sampled, not swallowed. Anything else, and any file over 524288 bytes, comes back as download_url with download_expires_at and content null. Readable paths: render-engine/index.html, render-engine/compositions/, render-engine/public/, render-engine/styles/, render-engine/fonts/. Missing answers 404. Read-only. Billing: free.
Judge every file staged for one project and accept all or refuse all; only this call moves a staged file into the workspace a render reads. A composition is refused for external scripts, inline handlers, javascript: URLs, network calls in executable script code, or more than 64 KiB (4 MiB for compositions/narrator_captions.html), because it is served into the project preview beside the page. A sub-composition is also refused when it carries no <template> (missing_composition_template), when its window.__timelines[id] = tl assignment sits after </template> (timeline_outside_template), when it registers a key no host in the file carries (timeline_id_mismatch) - only template content is cloned, so that timeline never registers and the export blocks on it - or when a script binds a literal [data-composition-id=id] selector with a class the host does not carry or :not(.visual-host), or sends a :scope > child query to that root, because the export mount strips the inner root's data-* attributes and only the host carries the id, so that root reads null and the script throws before it registers. A composition's media, stylesheet and mount references (img, video, audio, source, link, data-composition-src) are read from the project root (public/..., styles/..., compositions/...), never from compositions/, because the editor mounts the template into index.html; a ../ path there names a file above the root and is refused as missing_reference. Only a composition's script src is read from its own file (../public/vendor/gsap.min.js), as the export's page load reads it, and a url() inside a .css file is read from that stylesheet. Every file a document or a live style names must be in the workspace or in the same commit, or the round is refused as missing_reference instead of exporting without it; a @font-face src is the exception, since the browser falls back when a face is absent. data-start, data-duration and data-media-start are seconds, and a value that is not a finite number in range (start and media offset at or above 0, duration above 0) is refused as invalid_timing rather than failing the export later. A host (data-composition-src) written inside another host is refused as nested_composition_host, since the outer composition fills its host and the inner one never registers; close each host's </div> before the next host opens. An <iframe srcdoc> that embeds a whole HTML document, or any other attribute value, inline <script>, <style> or HTML comment holding the text </body>, in index.html or inside the <template> of a composition this round writes or newly mounts, is refused as embedded_body_close: the export inserts its frame-capture script before the first </body> of the page it builds, so that one captures it and the render fails; mount such a slide as a composition with a data-composition-src host instead of an iframe. A sub-composition <style> the export's CSS parser rejects (a missing semicolon, an unbalanced brace, a stylesheet cut off mid-rule) is refused as composition_css_syntax with the parser's reason, line and column, when this round writes that composition or newly mounts it, since the preview tolerates it but the export compiles every mounted composition stylesheet and fails. An inline <script> of a document this round writes, or a .js file it writes, that the browser cannot parse (a script collapsed onto one line so a // comment swallows the rest, an unbalanced bracket, placeholder text pasted into the code) is refused as composition_script_syntax with the reason, line and column, since a script that does not parse stops every script after it in the export and nothing registers its timeline. An index.html whose root declares a canvas larger than 1080p (data-width/data-height with a long edge past 1920 or a short edge past 1080) is refused as composition_canvas_too_large, since the export is at most 1080p; author at 1080x1920, 1920x1080 or 1080x1080. An index that keeps the canvas the workspace already has is accepted. The caption layer, the composition declaring data-composition-id narrator-captions, is accepted only when its words are one inline JSON array declared as const WORDS = [...];, one object per word with double-quoted keys word, start and end (finite seconds on the cut) and group_id (an integer per phrase), because that array is all the editor and a long export read; any other caption layer is refused as invalid_caption_words, whatever the cut's length, with the accepted form and an example in its repair_hint, and build_captions stages a caption layer in that form. A document that draws on a canvas (it calls getContext or builds a WebGLRenderer) is refused as code_drawn_clock when it schedules frames with requestAnimationFrame, setAnimationLoop, setTimeout or setInterval, because the editor and the export move it only by seeking its paused timeline, so draw from that timeline's onUpdate; and as webgl_context_not_released when it creates a WebGL context and never calls loseContext or forceContextLoss, because the editor keeps every composition mounted and the browser holds 16 live WebGL contexts per page, so create the context when the moment's time enters its window and release it when time leaves. compositions/editing-script.json, the edit document every stage reads, is accepted only whole: one JSON object with schemaVersion 1, an integer rev and a tracks array holding at most one captions track, or it is refused as invalid_editing_script, since a truncated copy would fail every later stage; copy it byte for byte, or leave it out of the upload to keep the project's own. A file already committed under public/, and every committed image, video, audio file or font, is never replaced in place: staging different bytes at its path is refused as immutable_asset, so upload the new bytes under a new name and repoint its references in the same round. A document or stylesheet under compositions/, styles/ or fonts/ is replaced as before, within the stylesheet budget: every export re-copies and re-fingerprints them, so one over 1 MiB is stylesheet_too_large, a project past 16 MiB of them is stylesheet_total_bytes, and one past 512 is stylesheet_file_count. Already over a limit, a project still previews, exports and replaces a stylesheet without growing that dimension. On a project that records its input cap (every new project; an older one keeps its one-pass export and is not judged), a cut longer than 5 minutes is exported in stretches, and the export shifts the clock of a layer mounted for the whole cut only by rewriting an inline const WORDS = [...]; array of rows with start, end and group_id. So on such a cut a round is refused as unexportable_long_cut when the tree it leaves is one the export's planner cannot read - most often a subtitle or overlay layer of your own that spans the whole cut and keeps its times in another array - naming the file, the planner's check as code, and the change in repair_hint: build that layer's timeline from const WORDS rows and from nothing else. The same tree on a cut of 5 minutes or less is accepted, since it exports in one pass. The speaker's voice is always mixed from this project's own upload (public/source.mp3, or its enhanced copy), so a round whose cut starts every span past that file's end is refused as cut_past_voice_stem: the export would bake no audio. The export runs as long as that voice on the cut, however long music or sound effects run, or longer when footage is appended after it: a root <video class="clip"> under #stage, neither the speaker nor inside a visual host, that ends after the voice runs the export to its own end, capped at #stage data-duration and silent under the extension, so raise #stage to cover it. Anything else timed past the voice, a visual moment included, is accepted here and then cut from the export, never refused, and appended footage extends nothing while an exploded speaker's .speaker-clip spans do not sit back to back. A different recording needs a new project (sign_upload, then start_transcribe_project). The export plays an exploded speaker lane back to back from 0 s whatever its data-start says, so a lane written to start later, with an intro before the speaker, is heard that much earlier than written: put an intro inside the lane as speaker clips or lay it over the speaker from 0 s, and time captions, cards and PIPs on that clock. A round whose speaker PIP (a data-pip-src on the recording), read on that clock, runs past the lane's summed length is refused as speaker_pip_past_cut, since it has no frames to show there; a lane written elsewhere than it plays is a speaker_lane_not_back_to_back warning. A referenced video or audio file no decoder can open is refused as invalid_media naming the element that plays it, and the export refuses the same bytes before it captures. Where the deployment also boots the candidate, a composition this round writes or newly mounts, whose setup script throws, is refused as composition_setup_failed carrying the thrown message, and one that finishes without throwing and still never registers its timeline as composition_timeline_not_registered. A page whose own script throws before any composition can register is refused as composition_setup_failed, carrying the thrown message, when this round writes index.html, and is a composition_broken_elsewhere warning otherwise. A composition the round neither wrote nor newly mounted is not refused for how it starts: it rides the accepted round under warnings as composition_broken_elsewhere, and the export drops that moment, or refuses the render once every mounted moment in the tree is one of them. A check the server did not run refuses nothing and says so under warnings.A refused round moves nothing and names each offending file with the rule it broke. Returns task_id, agent_page_url and page_url. page_url opens the project in the product app, with its navigation and without the AIP chat, and is the durable link to give a person in the final reply. agent_page_url opens the editor view for embedded panels; it is not the final delivery link. get_task on that task_id answers this exact round in result: outcome, accepted, issues (code, path, message, retryable, repair_hint), the digest it left, and next_action; succeeded is that verdict rather than the lifecycle, and a refused round is a done task that accepted nothing. list_workspace.last_promote is the latest round only, which the next commit replaces. Two signed uploads to one path are one staged object and the last write wins, so expected_files binds the round to the staged bytes it claims, yours and the ones a stage result named for you (build_captions files): a named file that is absent or changed refuses the round with workspace_upload_missing or workspace_upload_conflict, and a staged file the batch does not name stays staged. An entry may carry the file itself instead: content is its complete UTF-8 text, staged by this call, so markup, stylesheets and JSON need no sign_workspace_upload, no PUT and no poll, and the server hashes what it stages, and stages it before the round starts, so a commit refused for the project lock leaves those files staged and the same call later stages them again. Send at most 262144 bytes of content over the whole call, and upload anything heavier or binary. A round that changes a composition also proves each one initializes, and any round proves each changed media file opens as media, unless warnings say a check did not run; it never proves the whole tree renders, which start_export runs. An accepted round also lists warnings ({rule, count, message}) for what the tree does that the editor or the export will not follow: timed elements on the root timeline outside a visual host, which play but are not listed or movable as effects; a speaker lane that plays a different cut from the one the audio mix will bake, so the export desyncs; a root audio the editor's player does not resolve (no audio#speaker-audio and no audio.speaker-clip), so the project opens with no player; a clip inside a composition whose window never meets its host's (a clip's data-start is the host's absolute clock), which never shows; a document that reads a vendor script's global before that script loads, which throws in the editor; and a canvas layer reading Math.random, Date or performance.now, whose frames differ between the editor and each export. A warning refuses nothing; re-author and commit again. An accepted round becomes a version (source host, this task_id) capturing index.html and the .html and .css under compositions/ and styles/, not fonts or media. base_digest is a digest from list_workspace or last_promote; a base that no longer matches is refused with nothing moved, as 409 with current_digest when already visible and as an outcome stale_base on the round's own verdict when it landed after this call. No base means no comparison. Takes the project lock: one round runs per project at a time, and a commit sent while one runs is refused as a conflict that moved nothing. Billing: free.
List registered project uploads available to composer @asset/handle mentions. Returns asset_id, handle, kind, path and media metadata, without media bytes or download URLs. The source video and other Asset Library entries, such as generated or Motion library media, are not listed; get_project_asset still reads such an entry by asset_id or handle. Billing: free.
Read one project asset: a registered upload by asset_id, handle or @asset/handle, else a project Asset Library entry, including generated and Motion library copies, by asset_id or handle. mode inspect (default) returns metadata and native image content for PNG, JPEG or WebP up to 3 MiB. mode download returns metadata and a download_url valid for 15 minutes, including larger or other media. A registered upload path is not a render URL; an Asset Library entry carries its origin, and its path is the workspace file a composition loads. Billing: free.
List the Motion library packages with this project's placement state for each, read at project_revision. An entry carries its inputs (a video input that needs a speaker cutout states its slot) and an action: add, chooseInput, chat or unavailable, with the reason and the one call that acts. With at_ms (and the duration_ms you will place with), a cutout package's next carries the complete frame_speaker calls for that effect window, one matte window per kept span. scene_restyle lists the presets this project is served and whether its restyle switch is on. Billing: free.
Read one package by motion_asset_id and version from the registry: the pinned manifest, the entry source per supported orientation, or for the one orientation named, with each repeated @font-face rule kept once, and its preview. Reads no project. A version the registry does not hold, or an orientation the package does not support, is refused. Billing: free.
Place one certified package on this project's timeline at timeline_in_ms, the placement the browser's Add runs. expected_project_revision must equal the revision list_motion_assets returned; another is refused with the current one. A media input is a registered asset id or, for a video input, ordered staged segments {path, sha256, start_ms, end_ms, geometry} from frame_speaker, verified before they bind. The same idempotency_key replays the first receipt. Refused: an unknown or retired package, a required input without a value, a project under a task. Billing: free.
Display a question as an interactive card, with a text fallback for hosts without card support. Returns the question and options, not a user answer; no project setting changes. Component ids: visual_style, caption_style, rough_cut_boldness, editing_preference_example, editing_preference, bgm_enabled, sfx_enabled, visual_preference_prompt. project_id associates the card with an accessible project. agent_components supplies custom questions. locale sets the card's language; rough_cut_boldness, caption_style, bgm_enabled, sfx_enabled, editing_preference are translated, and a card in any language but en that names another server component is refused. No task or project lock. Billing: free.
Read one project's transcript. detail segments is one line per segment with its text and bounds, a tenth of the bytes: read it first to plan a cut. detail words (default) is every word with its times, what captions are built from. src_*_ms is a position in the SOURCE media (null when none); timeline_*_ms is on the CUT timeline. state ready is the whole document; invalid failed its checks (captions.issues says how); pending has no words yet. state deleted was cut; hidden is not drawn. One call is one page: pass page.next_cursor back as cursor, stitch per page.merge_rule. Billing: free.
Sign a local .mp4/.mov/.webm upload, only for a client that PUTs the bytes itself. A host with no shell that holds only an attachment, such as ChatGPT, uses start_project_from_attachment; a host that can run a shell, the claude.ai code execution sandbox included, PUTs the bytes itself; with no video, ask the user to attach one, never invent a file. Byte limit: 3584 MiB, or 2040 MiB restricted; duration is separate (start_transcribe_project). At 30 minutes these allow ~17 or ~9.5 Mbps: compress or trim larger files. Returns upload_url, required PUT headers, video_ref, upload_expires_at (null=no deadline). No bytes move until your shell PUTs them; repeat PUTs before expiry replace bytes (single_use=false). Resolve missing authorization for this file/destination before sending; reuse prior authorization; a host denial is not consent. Billing: free.
Read project phase, status, active task, optional preferences, and editable readiness. include_spend adds ledger spend; null means unavailable. page_url opens the project in the product app, with its navigation and without the AIP chat, and is the durable link to give a person in the final reply. agent_page_url opens the editor view for embedded panels; it is not the final delivery link. Billing: free.
Mint a single-use browser URL for one readable project. url works exactly once: it starts an agent session and redirects to the project page in agent mode, and url_expires_at is when an unused link dies. A session opened under an OAuth authorization lives as long as that authorization (up to about 30 days) and ends the moment it is revoked; any other caller gets about 8 hours. agent_page_url is the durable panel link and reuses the same browser cookie, so it needs no second ticket. Without project_id it mints a single-use link to the style presets view, for staff accounts only. Billing: free.
List your own projects, newest first, one page per call. total counts every project, and next_cursor is null on the last page. An item's progress is not authoritative: get_project is. page_url opens the project in the product app, with its navigation and without the AIP chat, and is the durable link to give a person in the final reply. agent_page_url opens the editor view for embedded panels; it is not the final delivery link. Billing: free.
Render the project's current editable preview to a downloadable mp4. A render this call starts passes render-tree admission first: a preview that fails it is refused with the blocking issues and no export is created. Admission is capped at 8 per 5 minutes per caller plus a shared server cap; over it the reply is a 429 with no verdict. An export already running, or still fresh for this preview, is returned without admission, and says so in reuse_reason; so is the latest export when it failed for good on this same preview (status failed, reuse_reason known_failure: repair per get_task). status deferred means a live turn owns the project and starts the export when it ends. Every reply that started, reused or parked an export carries task_id, current_workspace_digest (the accepted workspace NOW, the same token list_workspace reports), staged_files_present and warnings, so a refused commit, an upload nobody committed and an unchanged workspace no longer read alike. Those fields describe the workspace, not the returned file: this call does not yet tell you which workspace a given mp4 rendered, and naming the one you want is not supported. current_workspace_digest is null when the workspace could not be read, which does not affect the export. The file comes from get_export_url. page_url opens the project in the product app, with its navigation and without the AIP chat, and is the durable link to give a person in the final reply. agent_page_url opens the editor view for embedded panels; it is not the final delivery link. Billing: free.
Read the download link for the project's latest delivered export. state ready carries download_url and download_expires_at; deferred means a live turn still owns the project; not_done means an export has not delivered; no_export means none was requested. Render progress is get_task's, never here. download_url is a one-use credential minted for the CALLING account (not the owner, which differs on a staff read) and signs whoever opens it into that account. A ready reply lists dropped_visuals, moments the render shipped without because their setup script threw, and what the workspace looks like NOW: current_workspace_digest, staged_files_present and warnings. Those describe the workspace, not this file, which may have rendered an earlier one; current_workspace_digest is null when it could not be read. page_url opens the project in the product app, with its navigation and without the AIP chat, and is the durable link to give a person in the final reply. agent_page_url opens the editor view for embedded panels; it is not the final delivery link. Billing: free.
One endpoint, the same key, whichever client you use.
~/Library/Application Support/Claude/claude_desktop_config.json (Mac) · %APPDATA%\Claude\claude_desktop_config.json (Windows)
Replace API_KEY with your own key.
Already have an "mcpServers" section in your config? Just add the server entry inside it.
Discovery, routing, credentials, tool scoping and execution logs all happen at the gateway→connections stay ACTIVE with no work from you
OpusClip AI Producer MCP runs through a gateway that holds the credentials, scopes the access and records every call.
Managed auth, hosted MCP servers, and every OpusClip AI Producer MCP tool your agent needs.
Free to start.