curl --request POST \
--url https://www.genviral.io/api/partner/v1/editor/projects/b1d4e8f2-3a5c-4d7e-8f90-1a2b3c4d5e6f/captions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: 5a1c7e93-2d4b-4f86-9b0e-3c6d8a2f4e17' \
--data '{
"expected_updated_at": "2026-10-02T12:05:00.000Z",
"note": "Added captions"
}'
{
"ok": true,
"code": 200,
"message": "Editor captions added",
"data": {
"project_id": "b1d4e8f2-3a5c-4d7e-8f90-1a2b3c4d5e6f",
"title": "Launch cut",
"aspect_ratio": "9:16",
"duration_ms": 37100,
"updated_at": "2026-10-02T12:06:00.000Z",
"assets": [],
"clips": [],
"text_clips": [
{
"clip_id": "caption-8f1d2c3b-4a5e-4f60-9b7c-1d2e3f4a5b6c",
"text": "this changed how I plan",
"start_ms": 0,
"end_ms": 1450,
"role": "caption"
},
{
"clip_id": "caption-2b3c4d5e-6f70-4a81-9c2d-3e4f5a6b7c8d",
"text": "my whole week",
"start_ms": 1450,
"end_ms": 2300,
"role": "caption"
}
],
"undo": null,
"render": null,
"editor_url": "https://www.genviral.io/editor/b1d4e8f2-3a5c-4d7e-8f90-1a2b3c4d5e6f?workspace=personal"
}
}
Video Editor
Add Editor Captions
Transcribe the cut and add timed captions to a video project.
POST
/
api
/
partner
/
v1
/
editor
/
projects
/
{projectId}
/
captions
curl --request POST \
--url https://www.genviral.io/api/partner/v1/editor/projects/b1d4e8f2-3a5c-4d7e-8f90-1a2b3c4d5e6f/captions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: 5a1c7e93-2d4b-4f86-9b0e-3c6d8a2f4e17' \
--data '{
"expected_updated_at": "2026-10-02T12:05:00.000Z",
"note": "Added captions"
}'
{
"ok": true,
"code": 200,
"message": "Editor captions added",
"data": {
"project_id": "b1d4e8f2-3a5c-4d7e-8f90-1a2b3c4d5e6f",
"title": "Launch cut",
"aspect_ratio": "9:16",
"duration_ms": 37100,
"updated_at": "2026-10-02T12:06:00.000Z",
"assets": [],
"clips": [],
"text_clips": [
{
"clip_id": "caption-8f1d2c3b-4a5e-4f60-9b7c-1d2e3f4a5b6c",
"text": "this changed how I plan",
"start_ms": 0,
"end_ms": 1450,
"role": "caption"
},
{
"clip_id": "caption-2b3c4d5e-6f70-4a81-9c2d-3e4f5a6b7c8d",
"text": "my whole week",
"start_ms": 1450,
"end_ms": 2300,
"role": "caption"
}
],
"undo": null,
"render": null,
"editor_url": "https://www.genviral.io/editor/b1d4e8f2-3a5c-4d7e-8f90-1a2b3c4d5e6f?workspace=personal"
}
}
Transcribes the speech in the project’s current cut and adds timed caption text clips in the
default caption style, then returns the updated project. Captions follow the cut: each one is
placed on the timeline where its words are spoken, so cut first and caption last.
Adding captions is free (no credits). It can take a while for long videos, and it is rate
limited per account owner because every request transcribes the cut.
The captions appear in the project’s
text_clips with role: "caption", and live in the Genviral
web editor at the project’s editor_url, where the user can restyle or edit them.
- A cut that already has captions is left as it is, and the project is returned unchanged. Your request never adds a second set of captions.
- A cut with no speech is returned unchanged, with no captions added.
- To read the words with their timings instead, use Get Editor Transcript.
Concurrency
Passexpected_updated_at with the updated_at you last read. If the project changed since, the
request is refused with 409 editor_conflict: read the project again and retry with a new
Idempotency-Key.
The caption write also checks the revision that was transcribed, even if you omit
expected_updated_at. An edit made during transcription returns 409 editor_conflict
without inserting captions timed to the old cut.
While Vira (the Genviral assistant) is editing the same project, the request is refused with
409 editor_locked. Wait a few seconds and retry.
Idempotency
Idempotency-Key is required and is scoped to the API key (or MCP grant) that sends it.
- Retrying with the same key and the same body returns the project and never transcribes or adds captions twice.
- Reusing a key with a different body returns
409 idempotency_key_reused. - A refused request (for example
502 transcription_failed) does not consume its key.
Path Parameters
string (UUID)
required
The project id.
Body Parameters
The body is optional. Send{} or no body to caption the cut as it is.
string
The
updated_at you last read (ISO 8601). The request is refused with editor_conflict if the
project changed since.string
One short plain-text line (at most 140 characters, no line breaks) describing this step in the
user’s terms, for example
Added captions. The open Genviral web editor shows it live in its
activity feed, labelled with who made the edit (Claude, ChatGPT, another MCP client, or the API).Response
Returns200 with the updated project, in the same shape as
Get Editor Project. Any earlier render is no longer current
once captions are added.
curl --request POST \
--url https://www.genviral.io/api/partner/v1/editor/projects/b1d4e8f2-3a5c-4d7e-8f90-1a2b3c4d5e6f/captions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: 5a1c7e93-2d4b-4f86-9b0e-3c6d8a2f4e17' \
--data '{
"expected_updated_at": "2026-10-02T12:05:00.000Z",
"note": "Added captions"
}'
{
"ok": true,
"code": 200,
"message": "Editor captions added",
"data": {
"project_id": "b1d4e8f2-3a5c-4d7e-8f90-1a2b3c4d5e6f",
"title": "Launch cut",
"aspect_ratio": "9:16",
"duration_ms": 37100,
"updated_at": "2026-10-02T12:06:00.000Z",
"assets": [],
"clips": [],
"text_clips": [
{
"clip_id": "caption-8f1d2c3b-4a5e-4f60-9b7c-1d2e3f4a5b6c",
"text": "this changed how I plan",
"start_ms": 0,
"end_ms": 1450,
"role": "caption"
},
{
"clip_id": "caption-2b3c4d5e-6f70-4a81-9c2d-3e4f5a6b7c8d",
"text": "my whole week",
"start_ms": 1450,
"end_ms": 2300,
"role": "caption"
}
],
"undo": null,
"render": null,
"editor_url": "https://www.genviral.io/editor/b1d4e8f2-3a5c-4d7e-8f90-1a2b3c4d5e6f?workspace=personal"
}
}
Error Responses
400 invalid_json- request body is not valid JSON400 invalid_request-Idempotency-Keyheader is missing or longer than 255 characters401- authentication failed (missing/invalid/revoked token)404 project_not_found- the project does not exist in the key scope409 editor_locked- Vira is editing this project right now; retry shortly409 editor_conflict- the project changed since your read or during transcription; read it again409 idempotency_key_reused- thisIdempotency-Keywas already used with a different body409 request_in_progress- an identical request with this key is still being processed; retry shortly422 invalid_payload- body failed validation (seeissues)422 no_audio_source- nothing on the timeline has audible sound to caption422 invalid_document- the project could not be read or the result would not be a valid project429 rate_limited- the hourly caption budget is spent; retry afterretry_after_seconds502 transcription_failed- the cut could not be transcribed; retry later
