Studio
Studio is the multi-track editor: tracks, clips, media and a canvas. This API manages those projects - listing them, creating them, copying them, and sharing them.
The timeline is writable, so you can assemble a video programmatically - generate a script, place the clips, then open it in the editor or render it.
Projects
GET /v1/studio/projects · POST /v1/studio/projects · GET /v1/studio/projects/:id ·
PATCH /v1/studio/projects/:id · DELETE /v1/studio/projects/:id
curl -X POST https://api.vidocu.ai/v1/studio/projects \
-H "Authorization: Bearer vdo_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"name": "Q3 launch cut"}'
Creating gives you an empty project with the editor's default canvas and one track.
The list is cursor-paginated, searchable by name, and
returns counts rather than the timeline:
{
"id": "...",
"name": "Q3 launch cut",
"canvas": { "width": 1920, "height": 1080, "ratio": "16:9", "fps": 30 },
"totalDuration": 84.5,
"counts": { "tracks": 3, "clips": 12, "media": 5 },
"shared": false,
"isSharePublic": false
}
Fetch a single project to get tracks, clips and media in full.
Projects built by importing a video from the app (Edit in Studio) also carry sourceVideoId,
the id of that video. It is informational: the video is a separate object that Studio does not own,
it is unaffected by anything you do to the project, and it may since have been deleted. The field is
null on every other project, is set once when the project is created, and is not writable.
PATCH covers the name, project folder and canvas settings (canvasWidth, canvasHeight,
canvasRatio, fps). The timeline has its own endpoint.
Deleting removes the project. A video already exported from it is a separate object and is kept.
The timeline
PUT /v1/studio/projects/:id/timeline
Whole-document replacement. Send tracks, clips and media in full - read the project
first and send the modified document back, not a fragment. Replacement rather than patching is
deliberate: the three lists reference each other, and patching a graph piecemeal is how you end up
with a clip pointing at a track that no longer exists.
curl -X PUT https://api.vidocu.ai/v1/studio/projects/std_123/timeline \
-H "Authorization: Bearer vdo_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"tracks": [
{"id": "t1", "type": "main", "name": "Main", "order": 0,
"locked": false, "visible": true, "volume": 100}
],
"media": [
{"id": "m1", "name": "intro.mp4", "type": "video",
"url": "https://storage.vidocu.ai/..."}
],
"clips": [
{"id": "c1", "type": "video", "trackId": "t1", "mediaId": "m1",
"startOnTimeline": 0, "duration": 12.5,
"sourceStartTime": 0, "sourceEndTime": 12.5,
"speed": 1, "volume": 100, "opacity": 100}
]
}'
What's checked
Beyond the field types:
| Rule | Why |
|---|---|
Every clip.trackId exists in tracks | A clip on a missing track opens to an empty editor |
video/image/audio clips have a mediaId that exists in media | Same - the clip renders nothing |
| No duplicate ids within tracks, clips or media | Ambiguous references |
sourceEndTime is after sourceStartTime | A zero or negative source range |
Failures name the offending clip - Clip c4 is on track t9, which isn't in tracks - rather than
just rejecting the document.
totalDuration is computed from the clips, not taken from the request, so the editor and the
renderer can't disagree about where the video ends.
Media must already be uploaded; register the URL you get from
POST /v1/upload-url.
Duplicate
POST /v1/studio/projects/:id/duplicate
Copies the timeline into a new project, named <original> (copy) unless you pass a name.
The copy never inherits the original's share link or export state. A share token maps to one project forever, so a clone carrying it would be reachable at a URL its owner never created. Duplicating a shared project is safe.
Share links
POST /v1/studio/projects/:id/share
{ "shareUrl": "https://vidocu.ai/share/8f2a...", "isPublic": true }
Anyone with the URL can view the project, so treat creating one as publishing.
Minting is idempotent - a project keeps the same token forever, and calling again returns the
existing link. {"isPublic": false} revokes visibility without changing the URL, so re-enabling it
later restores the same link rather than breaking anything already shared.
A project's shared and isSharePublic flags appear on reads; the token itself is never returned
in a list or a get.
Templates
GET /v1/studio/templates · POST /v1/studio/templates/:id/use
Templates the workspace can start from: its own, plus Vidocu's global gallery (marked
global: true). Using one creates a new project and leaves the template untouched.
Export
POST /v1/studio/projects/:id/export
Renders the timeline to a video on Remotion Lambda.
{ "jobId": "...", "renderId": "...", "status": "processing" }
Returns 202. Rendering takes minutes - poll GET /v1/jobs/:jobId until it completes,
then read exportedVideoUrl from the project.
The render runs server-side to completion, so nothing is lost if the client disconnects.
Each export counts as one against the plan's allowance regardless of length; 402 when that's
exhausted. Free plans get the Vidocu watermark baked in.
This is the same render the editor runs - both call the same function, so a project exports identically whichever surface starts it.
Errors
| Status | Code | Cause |
|---|---|---|
| 404 | not_found | No such project or template in this workspace |
| 400 | validation_error | A clip references a missing track or media item, or a field is out of range |
| 409 | conflict | Nothing on the timeline to work with |