Remix
One long video in, a set of shorts and drafted copy out. Remix watches the whole thing, finds the moments worth cutting, and writes the blog post and social posts to go with them. For webinars, it finds clips of the real speaker and builds a recap video.
Create
POST /v1/remixes
Two sources. A video already in the workspace:
curl -X POST https://api.vidocu.ai/v1/remixes \
-H "Authorization: Bearer vdo_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"videoId": "vid_abc123"}'
Or an external URL, which needs the duration since nothing has inspected the file yet:
-d '{"videoUrl": "https://example.com/talk.mp4", "durationSec": 2400, "title": "Q3 keynote"}'
Exactly one of videoId or videoUrl. Creating a remix analyzes nothing - it just sets it up.
402 if the workspace hasn't the balance for a video that long. The check happens here; the charge
happens when analysis completes.
Webinar remixes
Add a plan to make clips of the real speaker, in their own voice, instead of scripted shorts.
Remix transcribes who said what, maps the chapters, and scores the moments that stand on their
own.
curl -X POST https://api.vidocu.ai/v1/remixes \
-H "Authorization: Bearer vdo_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"videoId": "vid_abc123",
"plan": {
"outputs": {
"clips": { "count": "auto", "length": "30to60", "tone": "marketing" },
"recap": { "targetSec": 120 },
"text": { "blog": true, "linkedin": true, "x": false, "quotes": true }
},
"focus": "the pricing Q&A",
"range": { "start": 180, "end": 3480 },
"cleanup": { "voice": true, "fillers": true, "longPauses": true }
}
}'
| Field | Values |
|---|---|
outputs.clips | Vertical 9:16 clips. count: "auto" or 1-15. length: auto, lt30, 30to60, 60to90. tone: marketing or educational |
outputs.recap | A 16:9 highlights video. targetSec: 60, 120, 180 or 300 |
outputs.text | Which posts to draft |
focus | Optional topic to favour. Other standout moments are still picked |
range | Optional seconds of the source to use. At least a minute, at most 90 minutes |
language | Optional spoken language code. Detected when left out |
cleanup | Voice cleanup, filler-word cuts and long-pause trims, applied on export |
Choose at least one output. The source can be any length if range picks 90 minutes or less of
it, and only the range is billed. The response carries "pipelineVersion": 2.
Analysis costs 3 credits per minute of the range.
Analyze
POST /v1/remixes/:id/analyze
{ "status": "analyzing", "remixId": "...", "jobId": "..." }
Returns 202. This is the slow part - poll GET /v1/remixes/:id until status is ready.
A workspace runs one analysis at a time. If another is already running you get:
{ "status": "queued", "remixId": "..." }
That is not an error and needs no retry. The queued remix starts on its own when the running one finishes, oldest first.
Billed per minute of source video, when the analysis completes.
Read the result
GET /v1/remixes/:id returns the full analysis once ready: the shorts it found with their
timings, chapters, and drafted blog, LinkedIn and X copy.
GET /v1/remixes lists them, cursor-paginated and
filterable by status and projectId. The list omits analysis - it is far too large to return
per row.
Statuses: pending (created, not analyzed), queued, analyzing, ready, failed. When a
remix fails, error says why and analysis can be run again.
While a webinar remix analyzes, analysisProgress reports where it is:
{ "message": "Finding the best moments", "percentage": 52, "at": "2026-10-18T19:47:58.000Z" }
Review moments
A ready webinar remix holds analysis.speakers, analysis.chapters and analysis.moments. Each
moment is a range of the source:
{
"id": "m4",
"start": 136.44,
"end": 161.62,
"title": "The REINS Act targets Washington bureaucracy",
"kind": "insight",
"speakerIds": ["spk_1"],
"score": { "total": 85, "hook": 82, "standalone": 85, "value": 88, "clarity": 86 },
"cuts": [{ "start": 150.2, "end": 150.6 }],
"status": "proposed",
"use": { "clip": true, "recap": true },
"origin": "ai"
}
cuts are the filler words and long pauses that are skipped when it plays. Times are seconds in
the source video.
Update a moment
PATCH /v1/remixes/:id/moments/:momentId
-d '{"status": "kept", "title": "Why the REINS Act matters", "end": 168}'
Any of status (proposed, kept, dropped), title, start, end, use, kind, cuts
and hookText (the headline over the clip's first seconds; an empty string goes back to the
title).
A new start or end snaps to the nearest transcript line. Cuts already in the clip stay, and
the newly covered part gets its own filler and pause cuts. Returns { "moment": ... }.
add and remove take a span of transcript in source seconds, { "start": 412.3, "end": 431 }:
addbrings that text into the clip, from anywhere in the webinar. Text outside the clip widens it and skips what sits in between, so a clip can join two separate sections, such as the question that gives an answer its context. The clip can span at most 10 minutes from its first word to its last, skipped parts included.removetakes that text out of the clip. At the start or end, the clip starts or ends at the next word it keeps.
-d '{"add": {"start": 412.3, "end": 431}}'
framing frames a clip by hand instead of automatically. Boxes are { x, y, w, h }, 0 to 1 of the
source frame, and are fitted to the clip's shape around their centre:
framing | The clip shows |
|---|---|
{"mode": "speaker", "box": {...}} | That part of the frame, all the way through |
{"mode": "screen", "screen": {...}, "camera": {...}} | That part of a shared screen, with the camera box as a small card. Leave out camera for no card |
{"mode": "stack", "top": {...}, "bottom": {...}} | Two parts of the frame, one over the other, each filling half the clip. The speaker over the screen, or two people |
{"mode": "whole"} | The whole frame, with bars |
{"mode": "auto"} or null | Back to automatic |
A made clip's export row has shots: how it was framed automatically, in the same boxes, so you
can start from there. Changing framing marks a made clip as changed.
Add a moment
POST /v1/remixes/:id/moments with {"start": 612.4, "end": 655, "title": "Optional"}
Use analysis.subtitles to find the times. The range snaps to the transcript like a trim, and the
new moment starts as kept. Returns 201 with { "moment": ... }.
Set the recap order
PUT /v1/remixes/:id/recap
{
"items": [
{ "type": "title_card", "id": "t1", "text": "Where AI policy goes next", "durationSec": 2.5 },
{ "type": "moment", "momentId": "m7" },
{ "type": "moment", "momentId": "m10" }
]
}
Replaces the whole list. Title cards show for 1-6 seconds. Until you set it, recap is null and
analysis.recapOrder holds the suggested order. Changing a moment's use.recap doesn't reorder
the recap, so send the list you want.
These three return 409 on a remix made without a plan, or one that hasn't finished analyzing.
Style
Send clipStyle or recapStyle with PATCH /v1/remixes/:id. Both are partial: what you send
merges over what's saved.
{
"clipStyle": {
"ratio": "9:16",
"captions": { "enabled": true, "preset": "bold", "position": "bottom", "highlightColor": "#FACC15" },
"hook": { "enabled": true },
"endCard": { "enabled": true, "text": "Watch the full webinar" },
"logo": true
},
"recapStyle": { "captions": true, "logo": true }
}
| Field | Values |
|---|---|
clipStyle.ratio | 9:16, 4:5, 1:1 or 16:9. A 16:9 clip shows the whole webinar frame unless you set its framing |
clipStyle.captions.preset | bold, clean or boxed |
clipStyle.captions.position | bottom (lower third) or middle |
clipStyle.captions.highlightColor | Hex colour of the word being spoken |
clipStyle.hook.enabled | Each clip's hookText (or title) over its first seconds |
clipStyle.endCard | A closing line on screen for 2.5 seconds |
logo | Your Brand Kit logo. Free plans always show the Vidocu mark |
The defaults are what's shown above, with the end card off. Clips already made keep their look until you make them again.
Update and delete
PATCH /v1/remixes/:id changes the title, moves the remix to a project folder, or sets its
style. Status, analysis and exports belong to the pipeline and can't be set - writing
them by hand would desynchronise a running job.
DELETE /v1/remixes/:id is refused with 409 while analysis is running. Videos already exported
from the remix are separate objects and are untouched.
Export
POST /v1/remixes/:id/export
Text outputs come back immediately:
curl -X POST https://api.vidocu.ai/v1/remixes/rmx_123/export \
-H "Authorization: Bearer vdo_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"outputType": "blog"}'
{ "exportId": "...", "status": "completed", "content": "# How we cut onboarding in half\n\n..." }
Video outputs start a render and return 202 with a job:
{ "exportId": "...", "jobId": "...", "status": "processing" }
outputType | Kind | Notes |
|---|---|---|
blog | text | Returns Markdown |
linkedin | text | variantIndex picks which draft; defaults to the first |
x | text | The thread, joined |
clips | video | Webinar remixes. Every kept clip, or the ones in momentIds |
recap | video | Webinar remixes. The 16:9 recap in its order |
short | video | Earlier remixes. Needs shortId from the analysis |
yt-trim | video | Earlier remixes. The trimmed cut |
Each short or yt-trim export counts as one against the plan's export allowance regardless
of length; 402 when that's exhausted. Poll GET /v1/remixes/:id/exports for a video export's URL.
Clips and the recap
curl -X POST https://api.vidocu.ai/v1/remixes/rmx_123/export \
-H "Authorization: Bearer vdo_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"outputType": "clips", "momentIds": ["m4", "m7"]}'
{
"status": "processing",
"started": [{ "exportId": "...", "momentId": "m4" }],
"unchanged": ["m7"],
"estimatedCredits": 18
}
Each clip is its own export row (outputType: "clip", outputId = the moment id); the recap is
outputType: "recap". Rows carry progress (0-100) while they're made. Clips follow the speaker
and come with the voice cleaned up when the plan asked for it.
When the speaker shares their screen or slides with their camera on, a clip splits in two, the way Reels and TikTok do: the speaker fills the top half and the screen the bottom half, with captions on the line between them. A small camera gets a shorter top part (down to 30% of the clip) so the speaker isn't blown up into a blur, and a tiny one shows as a small card over the screen. A shared screen zooms in on where things happen; a slide shows whole, so no word is cut off. Without a camera, the clip shows the busy part of the screen with captions below it. Two people on a call stack one over the other; with three, the one talking goes on top and the other two sit side by side below. With more, the clip follows whoever is talking. The recap shows shared screens as they were.
A clip already made from the same trims, cuts and style, or still being made, isn't made again.
It's listed in unchanged and costs nothing. When nothing is left to make, the call returns 200
with "status": "completed".
Credits are checked before anything starts (402 when the balance is short) and charged when each
video is ready, on what was made: 10 credits per output minute, plus 25 per minute of voice cleanup.
Cleanup for a range is paid once, so a recap reusing a made clip doesn't pay for it again. A failed
video costs nothing. The final charge is on each row as creditsCharged.
Regenerate the copy
POST /v1/remixes/:id/regenerate with {"outputType": "blog" | "linkedin" | "x"}
Rewrites that output from the analyzed transcript and returns the updated analysis. It replaces the current draft, so don't call it on copy someone has edited.
1 credit per run. It doesn't count as an export.
Exports list
GET /v1/remixes/:id/exports lists what has been produced - each with its output type, status and
URL. Webinar clips and the recap also carry progress, durationSec and creditsCharged.
Errors
| Status | Code | Cause |
|---|---|---|
| 400 | validation_error | Both or neither of videoId/videoUrl, or videoUrl without durationSec |
| 402 | limit_exceeded | Not enough balance for a video that long |
| 404 | not_found | No such remix in this workspace |
| 400 | validation_error | A plan with no outputs, or a range under a minute or over 90 minutes |
| 404 | not_found | No such moment in this remix |
| 409 | conflict | Already analyzing, or deleting while analysis runs. Moment and recap edits on a remix without a plan, or before analysis finishes |