API reference
Everything the dashboard does, an integration can do. 24 resources, 151 actions, 56 webhook events, all of it behind one scoped key.
Authentication
Send your key as a bearer token. X-Api-Key works too.
curl -H "Authorization: Bearer sk_live_..." https://autostud.ai/api/v1
GET /api/v1 answers what can this key do: the resources, actions and
limits it holds. Start there instead of hard-coding a route list.
A key prefixed sk_test_ runs the whole request path (authentication, scopes,
limits, validation, routing), then stops before any write or credit spend, and answers
X-Test-Mode: true.
Scopes
A scope is <resource>:<action>. Wildcards are matched at request time,
so videos:* covers a video action added later.
write never implies execute: spending credits is its own
permission.
| Resource | Actions | What it covers |
|---|---|---|
videos | readwritedeleteexecute | Video projects: timeline, blocks, metadata. |
scripts | readwritedeleteexecute | The script writer: documents, paragraphs, the AI passes and the voice-over takes. |
renders | readwritedeleteexecute | Render jobs and their output files. |
generations | readwritedeleteexecute | Image, video, audio and music generations. |
assets | readwritedelete | Uploaded files and generated media. |
folders | readwritedelete | Folder tree used to organise videos and assets. |
characters | readwritedeleteexecute | Recurring characters and their reference images. |
backgrounds | read | The curated shelf of background stills and loops. |
voices | readwritedeleteexecute | Voice catalog, cloned voices and TTS connections. |
templates | readwritedelete | Video templates, styles and smart-group configurations. |
music | readwritedeleteexecute | Music library and AI song generation. |
agents | readwritedeleteexecute | Image/video agents and their executions. |
flows | readwritedeleteexecute | Automation flows and their runs. |
publishing | readwritedeleteexecute | Social accounts and scheduled posts. |
webhooks | readwritedelete | Webhook endpoints and their delivery history. |
workspace | readwrite | Workspace profile, members and settings. |
usage | read | Credit balance, API usage and request logs. |
api_keys | readwritedelete | Manage API keys themselves. Grant with care. |
Presets
| Preset | Scopes |
|---|---|
read_onlyFetch everything, change nothing. Safe for dashboards and exports. | *:read |
content_editorCreate and edit videos, assets and library items — no credit spending. | videos:read videos:write scripts:read scripts:write assets:read assets:write folders:read folders:write templates:read characters:read voices:read backgrounds:read usage:read |
automationFull production pipeline: create, generate, render, publish. | videos:* scripts:* renders:* generations:* assets:* folders:* characters:read voices:read backgrounds:read music:* templates:read publishing:* usage:read |
full_accessEverything except managing API keys. | videos:* scripts:* renders:* generations:* assets:* folders:* characters:* backgrounds:* voices:* templates:* music:* agents:* flows:* publishing:* webhooks:* workspace:* usage:* |
Limits & quotas
Every key carries four rate windows: per minute, per hour, per day, and a separate, much tighter daily budget for anything that spends credits. Heavy endpoints cost more than one unit. Every response reports where you stand:
X-RateLimit-Limit: 120 X-RateLimit-Remaining: 118 X-RateLimit-Reset: 1786500000 X-RateLimit-Window: minute X-RateLimit-Policy: 120;w=60, 3000;w=3600, 20000;w=86400
A key may also carry a monthly ceiling on requests or on credits
(X-Quota-* headers). Both refusals are 429 with
Retry-After, and differ by code: rate_limit_exceeded vs
quota_exceeded.
Did it work?
One question, one answer, on every endpoint: a response is a failure if and only if
it has a top-level error key. Nothing reports success with a
success: true flag, and nothing reports failure with a 200.
You never have to compare status codes to find out which happened.
{
"object": "video",
"id": "vid_8f2c...",
"video_name": "Quiz - capitals",
"livemode": true,
"request_id": "req_8f2c..."
}
object names what you got, so one branch handles every payload.
livemode is false when an sk_test_ key produced the
response. request_id is on successes as well as failures. It is echoed in the
X-Request-Id header, stored in your log at /api/v1/logs, and it is
what support will ask for.
Errors
Every failure has the same shape and a stable code to branch on.
{
"error": {
"type": "validation_error",
"code": "validation_failed",
"status": 422,
"message": "tools.generate_from_prompt validation failed, `format`: `format` is not allowed (1 more).",
"param": "format",
"details": { "issues": [
{ "path": "format", "message": "`format` is not allowed", "type": "object.unknown",
"allowed": ["prompt", "model", "size", "quality"] },
{ "path": "quallity", "message": "`quallity` is not allowed", "type": "object.unknown",
"allowed": ["prompt", "model", "size", "quality"], "did_you_mean": "quality" }
] },
"hint": "`details.issues` names each offending field. Fix them and retry; retrying unchanged will fail identically.",
"retryable": false,
"livemode": true,
"request_id": "req_8f2c...",
"doc_url": "https://autostud.ai/docs/api/errors#validation_failed"
}
}
Read retryable before you back off. It is false for
every client error: repeating the identical request will fail identically, however long you
wait, so the fix is in the payload and details says where. It is
true only for a rate limit, a conflict, a provider failure or a fault of ours.
internal_error (500) means our code failed;
upstream_error (502) means a provider under us did. A wrong
request is never either of them.
details.issues has one shape everywhere: path is dotted
(config.fps), type is the stable code to branch on, and a field
rejected as unknown also carries allowed, the fields that object does take,
plus did_you_mean when what you sent was close to one of them. You should never
need a second round-trip to find out what a payload accepts.
Health
GET /api/v1/health is the one endpoint that answers without a
key. That is the point of it: when a call fails you need to know whether the API is
down or your credential is wrong, and an endpoint that returns 401 cannot tell
you.
{ "object": "health", "status": "ok", "api_version": "2026-08-14",
"checks": { "database": { "status": "ok", "latency_ms": 3 } },
"time": "2026-08-24T09:12:44.108Z" }
It always answers 200; a degraded dependency shows up in status and
checks, not in the status code. It reports nothing about your key, your
workspace or your usage.
Pagination
Cursor pagination by default, stable while rows are being inserted, and cheap at any
depth. Follow next_cursor until has_more is false.
offset exists for UIs that need page numbers.
GET /api/v1/videos?limit=50 GET /api/v1/videos?limit=50&cursor=eyJ2Ijp7...
{ "object": "list", "data": [ ... ], "has_more": true, "next_cursor": "eyJ2...", "total": null }
Filtering & search
Filters are declarative: only the fields a resource declares, only these operators. There is no path from a query string to a raw database operator.
?published=true ?video_format[in]=portrait,square ?video_created_at[gte]=2026-01-01 ?video_name[starts_with]=Quiz ?q=karaoke&created_after=2026-06-01
Operators: eq (implicit), ne, gt, gte,
lt, lte, in, nin, exists,
contains, starts_with.
Formats
Any list reads three ways, via ?format= or Accept:.
| Format | Use |
|---|---|
json | The envelope above. The default. |
ndjson | One object per line, for streams, jq and pipelines. |
csv | Flattened and downloadable, for spreadsheets and BI tools. |
?fields=a,b narrows the response to what you need;
?expand= brings back the heavy fields a list omits by default.
Idempotency
Send Idempotency-Key on any unsafe request. A replay within 24 hours returns the
first response instead of running again, so a retry after a timeout cannot double-charge.
Reusing a key with a different body is a 409, not a wrong replay.
curl -X POST https://autostud.ai/api/v1/actions/renders.create \
-H "Authorization: Bearer sk_live_..." \
-H "Idempotency-Key: render-2026-08-14-001" \
-H "Content-Type: application/json" \
-d '{"video_id": "..."}'
Bulk
POST /api/v1/{resource}/bulk takes up to 100 creates, updates and deletes
in one call. Each item reports its own status; the response is 207 when at least
one failed. It is not a transaction: successful items stay applied.
{
"create": [ { "folder_name": "Q3" } ],
"update": [ { "id": "...", "data": { "folder_name": "Q4" } } ],
"delete": [ "..." ]
}
Batch
POST /api/v1/batch takes up to 20 different calls in one round trip,
sequential by default. Each member goes through its real route: same scopes, same limits,
same billing. The envelope is always 200; read each member's own status.
{
"requests": [
{ "id": "folder", "method": "POST", "path": "/v1/folders", "body": { "folder_name": "Q3" } },
{ "id": "videos", "method": "GET", "path": "/v1/videos?limit=5" }
]
}
Webhooks
Register an https endpoint, subscribe it to events, and stop polling. Deliveries are signed, retried with exponential backoff over ~2 hours, and logged for 7 days.
curl -X POST https://autostud.ai/api/v1/webhooks \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"url": "https://you.example/hooks", "events": ["render.completed", "video.*"]}'
The signing secret is returned once, on creation. Verify every delivery:
X-Autostud-Signature is t=<unix seconds>,v1=<hex>, where the
hex is HMAC-SHA256(secret, "<t>.<raw body>"). Reject anything older than
five minutes.
const [t, v1] = header.split(',').map(part => part.split('=')[1]);
const expected = crypto.createHmac('sha256', secret)
.update(t + '.' + raw_body)
.digest('hex');
// compare with crypto.timingSafeEqual
Deduplicate on X-Autostud-Delivery. Answer 2xx quickly, anything else
is retried, and an endpoint that fails 20 times in a row is disabled.
POST /api/v1/webhooks/{id}/test sends a real signed delivery and reports what came
back.
Event catalog
Subscribe to a name, a family (video.*) or everything (*).
| Event | Meaning |
|---|---|
render.started | A worker picked a queued render up and started working on it. |
render.completed | A render finished and its output URL is available. |
render.failed | A render stopped on an error. |
generation.completed | An AI generation (image, video, audio) produced its result. |
generation.failed | An AI generation failed. Credits already spent are reported in the payload. |
music.completed | A music generation finished and its tracks are hosted. |
music.failed | A music generation failed. The credits it reserved were refunded. |
post.published | A scheduled post went out. Also fired on a partial publish — read `target_accounts` for the per-account outcome. |
post.failed | A scheduled post could not be published. |
flow.completed | A flow execution reached its end. |
flow.failed | A flow execution stopped on an error. |
agent_execution.completed | An agent finished running. |
action.completed | Any `/v1/actions/*` call that succeeded. Useful as a catch-all audit feed. |
action.failed | Any `/v1/actions/*` call that returned an error. |
credits.low | A call was refused for lack of credits. The payload carries what it needed, what was left and the shortfall. |
webhook.test | Sent by `POST /v1/webhooks/{id}/test`. Never emitted by the platform. |
video.created | A video was created through the API or the dashboard. |
video.updated | A video was updated through the API or the dashboard. |
video.deleted | A video was deleted through the API or the dashboard. |
folder.created | A folder was created through the API or the dashboard. |
folder.updated | A folder was updated through the API or the dashboard. |
folder.deleted | A folder was deleted through the API or the dashboard. |
asset.created | A asset was created through the API or the dashboard. |
asset.deleted | A asset was deleted through the API or the dashboard. |
character.created | A character was created through the API or the dashboard. |
character.updated | A character was updated through the API or the dashboard. |
character.deleted | A character was deleted through the API or the dashboard. |
agent.created | A agent was created through the API or the dashboard. |
agent.updated | A agent was updated through the API or the dashboard. |
agent.deleted | A agent was deleted through the API or the dashboard. |
flow.created | A flow was created through the API or the dashboard. |
flow.updated | A flow was updated through the API or the dashboard. |
flow.deleted | A flow was deleted through the API or the dashboard. |
slideshow.created | A slideshow was created through the API or the dashboard. |
slideshow.updated | A slideshow was updated through the API or the dashboard. |
slideshow.deleted | A slideshow was deleted through the API or the dashboard. |
template.created | A template was created through the API or the dashboard. |
template.updated | A template was updated through the API or the dashboard. |
template.deleted | A template was deleted through the API or the dashboard. |
video_style.created | A video_style was created through the API or the dashboard. |
video_style.updated | A video_style was updated through the API or the dashboard. |
video_style.deleted | A video_style was deleted through the API or the dashboard. |
smart_group_config.created | A smart_group_config was created through the API or the dashboard. |
smart_group_config.updated | A smart_group_config was updated through the API or the dashboard. |
smart_group_config.deleted | A smart_group_config was deleted through the API or the dashboard. |
custom_video_type.created | A custom_video_type was created through the API or the dashboard. |
custom_video_type.updated | A custom_video_type was updated through the API or the dashboard. |
custom_video_type.deleted | A custom_video_type was deleted through the API or the dashboard. |
scheduled_post.created | A scheduled_post was created through the API or the dashboard. |
scheduled_post.updated | A scheduled_post was updated through the API or the dashboard. |
scheduled_post.deleted | A scheduled_post was deleted through the API or the dashboard. |
character_tag.created | A character_tag was created through the API or the dashboard. |
character_tag.updated | A character_tag was updated through the API or the dashboard. |
character_tag.deleted | A character_tag was deleted through the API or the dashboard. |
playground_generation.updated | A playground_generation was updated through the API or the dashboard. |
playground_generation.deleted | A playground_generation was deleted through the API or the dashboard. |
Resources
Videos videos
Video projects with their timeline, blocks and generation settings.
| Method | Path |
|---|---|
| GET | /api/v1/videos |
| GET | /api/v1/videos/{id} |
| POST | /api/v1/videos |
| PATCH | /api/v1/videos/{id} |
| DELETE | /api/v1/videos/{id} |
id video_id ·
sort video_created_at video_name video_last_audios_edition ·
filter video_format video_type_id video_sub_type_id video_type video_lang published video_ready_to_be_processed folder_ids faceless_status video_created_at
· search video_name video_keyword video_comment
· heavy video_json, blocks, groups, composition, smart_groups, faceless_scenes, faceless_template, voice_settings_map, video_captions
Scripts scripts
Video scripts: paragraphs, chapters, speakers, takes and the research behind them. Writing goes through the `scripts.*` actions.
| Method | Path |
|---|---|
| GET | /api/v1/scripts |
| GET | /api/v1/scripts/{id} |
id script_id ·
sort updated_at created_at title ·
filter language format structure video_id created_at updated_at
· search title topic
· heavy paragraphs, snapshots, research, review_notes, sources, chapters
Folders folders
Folders organising videos and playground generations.
| Method | Path |
|---|---|
| GET | /api/v1/folders |
| GET | /api/v1/folders/{id} |
| POST | /api/v1/folders |
| PATCH | /api/v1/folders/{id} |
| DELETE | /api/v1/folders/{id} |
id folder_id ·
sort folder_order folder_created_at folder_name ·
filter folder_scope folder_color folder_created_at
· search folder_name
Assets assets
Files uploaded to the workspace library.
| Method | Path |
|---|---|
| GET | /api/v1/assets |
| GET | /api/v1/assets/{id} |
| POST | /api/v1/assets |
| DELETE | /api/v1/assets/{id} |
id _id ·
sort provider_date_time_uploaded provider_file_name ·
filter file_mime_type sync_method provider_file_id provider_date_time_uploaded
· search provider_file_name
Characters characters
Recurring characters with their visual and voice configuration.
| Method | Path |
|---|---|
| GET | /api/v1/characters |
| GET | /api/v1/characters/{id} |
| POST | /api/v1/characters |
| PATCH | /api/v1/characters/{id} |
| DELETE | /api/v1/characters/{id} |
id character_id ·
sort created_at updated_at character_name ·
filter character_tags created_at
· search character_name character_description
· heavy media
Agents agents
Image and video generation agents.
| Method | Path |
|---|---|
| GET | /api/v1/agents |
| GET | /api/v1/agents/{id} |
| POST | /api/v1/agents |
| PATCH | /api/v1/agents/{id} |
| DELETE | /api/v1/agents/{id} |
id agent_id ·
sort created_at updated_at agent_name ·
filter agent_type agent_model is_default created_at
· search agent_name agent_description
Flows flows
Automation flows: nodes, edges and variables.
| Method | Path |
|---|---|
| GET | /api/v1/flows |
| GET | /api/v1/flows/{id} |
| POST | /api/v1/flows |
| PATCH | /api/v1/flows/{id} |
| DELETE | /api/v1/flows/{id} |
id flow_id ·
sort created_at updated_at name usage_count last_executed_at ·
filter status is_template is_public template_category created_at
· search name description
· heavy nodes, edges
Slideshows videos
Slideshow projects and their slides.
| Method | Path |
|---|---|
| GET | /api/v1/slideshows |
| GET | /api/v1/slideshows/{id} |
| POST | /api/v1/slideshows |
| PATCH | /api/v1/slideshows/{id} |
| DELETE | /api/v1/slideshows/{id} |
id slideshow_id ·
sort created_at updated_at slideshow_name ·
filter slideshow_status character_id created_at
· search slideshow_name
· heavy slides
Video templates templates
Saved video templates.
| Method | Path |
|---|---|
| GET | /api/v1/templates |
| GET | /api/v1/templates/{id} |
| POST | /api/v1/templates |
| PATCH | /api/v1/templates/{id} |
| DELETE | /api/v1/templates/{id} |
id template_id ·
sort created_at updated_at name ·
filter category sub_category is_public created_at
· search name description
· heavy template_data, blocks, composition
Video styles templates
Reusable style presets applied to timelines.
| Method | Path |
|---|---|
| GET | /api/v1/video_styles |
| GET | /api/v1/video_styles/{id} |
| POST | /api/v1/video_styles |
| PATCH | /api/v1/video_styles/{id} |
| DELETE | /api/v1/video_styles/{id} |
id style_id ·
sort created_at updated_at name ·
filter category is_public created_at
· search name description
· heavy style_data, layer_styles
Smart group configs templates
Custom smart-group definitions used by the timeline editor.
| Method | Path |
|---|---|
| GET | /api/v1/smart_group_configs |
| GET | /api/v1/smart_group_configs/{id} |
| POST | /api/v1/smart_group_configs |
| PATCH | /api/v1/smart_group_configs/{id} |
| DELETE | /api/v1/smart_group_configs/{id} |
id config_id ·
sort created_at updated_at name ·
filter type base_type category created_at
· search name description
· heavy layer_mappings
Custom video types templates
User-defined video types available in the editor.
| Method | Path |
|---|---|
| GET | /api/v1/custom_video_types |
| GET | /api/v1/custom_video_types/{id} |
| POST | /api/v1/custom_video_types |
| PATCH | /api/v1/custom_video_types/{id} |
| DELETE | /api/v1/custom_video_types/{id} |
id config_id ·
sort created_at updated_at name ·
filter type is_public created_at
· search name description
AI generations generations
Every AI generation billed to the workspace: image, video, audio, music.
| Method | Path |
|---|---|
| GET | /api/v1/generations |
| GET | /api/v1/generations/{id} |
id generation_id ·
sort created_at credits_consumed ·
filter agent_type agent_model agent_id folder_id status created_at
· search prompt
· heavy generation_metadata, variables
Music generations music
AI song generations and their tracks.
| Method | Path |
|---|---|
| GET | /api/v1/music |
| GET | /api/v1/music/{id} |
id generation_id ·
sort created_at updated_at credits_consumed ·
filter status provider model_id created_at
· heavy tracks, request
Scheduled posts publishing
Posts queued for publication on connected social accounts.
| Method | Path |
|---|---|
| GET | /api/v1/scheduled_posts |
| GET | /api/v1/scheduled_posts/{id} |
| POST | /api/v1/scheduled_posts |
| PATCH | /api/v1/scheduled_posts/{id} |
| DELETE | /api/v1/scheduled_posts/{id} |
id _id ·
sort scheduled_at created_at updated_at ·
filter status platform video_id scheduled_at created_at
· search caption title
Social accounts publishing
Connected publishing destinations. Read-only: connect them from the dashboard.
| Method | Path |
|---|---|
| GET | /api/v1/social_accounts |
| GET | /api/v1/social_accounts/{id} |
id _id ·
sort created_at updated_at ·
filter platform status created_at
· search account_name
· heavy platform_metadata
Renders renders
Render jobs and their output files. Start one with the `renders.create` action — it charges credits.
| Method | Path |
|---|---|
| GET | /api/v1/renders |
| GET | /api/v1/renders/{id} |
id render_id ·
sort render_creation_date ·
filter render_status video_id video_type_id render_creation_date
Backgrounds backgrounds
The curated background shelf: the stills and loops an admin published for everyone to build on.
| Method | Path |
|---|---|
| GET | /api/v1/backgrounds |
| GET | /api/v1/backgrounds/{id} |
id background_ref ·
sort sort_order created_at name ·
filter kind orientation category tags is_loop created_at
· search name description background_ref
Voices voices
The voice catalog: the shared global library plus the workspace and personal voices.
| Method | Path |
|---|---|
| GET | /api/v1/voices |
| GET | /api/v1/voices/{id} |
id voice_ref ·
sort sort_order created_at display_name ·
filter provider scope kind is_active labels.language labels.gender labels.accent connection_id created_at
· search display_name description
· heavy provider_meta
Character tags characters
Tags used to organise characters.
| Method | Path |
|---|---|
| GET | /api/v1/character_tags |
| GET | /api/v1/character_tags/{id} |
| POST | /api/v1/character_tags |
| PATCH | /api/v1/character_tags/{id} |
| DELETE | /api/v1/character_tags/{id} |
id tag_id ·
sort created_at tag_name ·
filter tag_color created_at
· search tag_name
Agent executions agents
History of agent runs with their prompt, variables and result.
| Method | Path |
|---|---|
| GET | /api/v1/agent_executions |
| GET | /api/v1/agent_executions/{id} |
id execution_id ·
sort created_at ·
filter agent_id created_at
· search prompt_used
· heavy result, variables_used, execution_metadata
Flow executions flows
Flow runs: status, node states, execution path and credits consumed.
| Method | Path |
|---|---|
| GET | /api/v1/flow_executions |
| GET | /api/v1/flow_executions/{id} |
id execution_id ·
sort created_at completed_at total_credits_consumed ·
filter flow_id status created_at
· heavy node_states, context, input_variables, execution_path
Credit transactions usage
Every credit movement of the workspace: what was charged, by whom, for what.
| Method | Path |
|---|---|
| GET | /api/v1/credit_transactions |
| GET | /api/v1/credit_transactions/{id} |
id _id ·
sort created_at amount ·
filter action_type amount created_at
· heavy metadata
Playground generations generations
Images and videos generated from the playground gallery.
| Method | Path |
|---|---|
| GET | /api/v1/playground_generations |
| GET | /api/v1/playground_generations/{id} |
| PATCH | /api/v1/playground_generations/{id} |
| DELETE | /api/v1/playground_generations/{id} |
id _id ·
sort created_at ·
filter folder_id generation_type generation_category created_at
· search prompt
· heavy input_params, result_data
Actions
Actions are what makes the platform do something. Each one runs the same controller the dashboard calls, so validation, billing and persistence are identical.
renders
| Action | Scopes | What it does |
|---|---|---|
POST renders.create
credits
emits
render.started |
renders:executerenders:write |
Queues a render for a video and returns the render job.
input:
video_id video_type_id video_format |
GET renders.list
|
renders:read |
Render jobs with their status and output URLs.
input:
video_id all_renders page limit render_status |
POST renders.delete
|
renders:delete |
Deletes a render job and its output reference.
input:
render_id |
videos
| Action | Scopes | What it does |
|---|---|---|
POST videos.create
|
videos:write |
Creates a video through the dashboard controller: applies the template, defaults and workspace stamping.
input:
video_name video_text_publication video_comment video_ready_to_be_processed published platform_published video_template video_format voice_settings config_avatars config video_json video_user_id video_keyword video_id folder_ids video_created_at video_music video_last_audios_edition video_type_id video_script video_sub_type_id video_lang creation_mode studio_origin studio_state studio_setup duplicate video_files_storage_provider video_captions web_infos precise_edit_config composition blocks groups voice_settings_map template_type video_type smart_groups faceless_scenes faceless_script faceless_voice_settings faceless_ai_model faceless_image_agent faceless_image_settings faceless_video_agent faceless_language faceless_style faceless_target_duration faceless_story_prompt faceless_dialogue_prompt faceless_script_prompt_image faceless_script_prompt_video faceless_status faceless_metadata faceless_output_url faceless_thumbnail_url faceless_scene_groups faceless_scene_types faceless_general_prompt faceless_detected_characters faceless_character_style_prompt faceless_selected_characters faceless_template |
POST videos.update
|
videos:write |
Validated update of a video document (same Joi schema as the editor).
input:
video_name video_text_publication video_comment video_ready_to_be_processed published platform_published video_template video_format voice_settings config_avatars config video_json video_user_id video_keyword video_id folder_ids video_created_at video_music video_last_audios_edition video_type_id video_script video_sub_type_id video_lang creation_mode studio_origin studio_state studio_setup duplicate video_files_storage_provider video_captions web_infos precise_edit_config composition blocks groups voice_settings_map template_type video_type smart_groups faceless_scenes faceless_script faceless_voice_settings faceless_ai_model faceless_image_agent faceless_image_settings faceless_video_agent faceless_language faceless_style faceless_target_duration faceless_story_prompt faceless_dialogue_prompt faceless_script_prompt_image faceless_script_prompt_video faceless_status faceless_metadata faceless_output_url faceless_thumbnail_url faceless_scene_groups faceless_scene_types faceless_general_prompt faceless_detected_characters faceless_character_style_prompt faceless_selected_characters faceless_template |
POST videos.generate_layer_audio
credits
|
videos:execute |
Generates the audio of one timeline layer and syncs its timing.
input:
videoId layer_name text voice_settings |
POST videos.launch_flow
credits
|
videos:execute |
Runs the end-to-end video flow (generation pipeline) for a video.
input:
video_id section_id keywords_search video_keyword video_flow_type video_type_id |
timeline
| Action | Scopes | What it does |
|---|---|---|
POST timeline.generate_audio
credits
|
videos:execute |
Generates the voice-over of a block/group and runs Whisper alignment.
input:
video_id scope group_id block_id layer_id force_regenerate blocks groups voice_settings_map video_lang |
POST timeline.generate_questions
credits
|
videos:execute |
Writes quiz questions for a video from a topic or keyword.
input:
topic count lang preset |
POST timeline.sync_voice
|
videos:write |
Recomputes block durations from the audio sequence.
input:
video_id scope group_id block_id blocks groups |
POST timeline.whisper_analyze
credits
|
videos:execute |
Word-level transcription and timing for an audio file.
input:
audio_url |
POST timeline.analyze_subtitles
credits
|
videos:execute |
Builds subtitle groups from a transcription.
input:
video_id audio_url |
POST timeline.translate_subtitles
credits
|
videos:execute |
Translates a subtitle track into a target language.
input:
subtitles target_lang |
POST timeline.hydrate_tu_preferes
credits
|
videos:execute |
Fills a would-you-rather timeline with generated questions and media.
input:
video_id count |
subtitles
| Action | Scopes | What it does |
|---|---|---|
POST subtitles.generate
credits
|
videos:execute |
Transcribes a media URL with Whisper, marks the words to emphasise and writes the subtitle track + layer onto the video. One call between `videos.create` and `renders.create`.
input:
video_id media_url style_preset words_per_group enable_emphasis enable_diarization video_format restyle_only |
images
| Action | Scopes | What it does |
|---|---|---|
POST images.generate
credits
emits
generation.completed |
generations:execute |
Runs an image agent and bills the workspace.
input:
agent_id variables custom_prompt use_ai_optimization preprompt_context video_id count reference_image_url variation_strength temperature settings_overrides folder_id source_generation_id source_image_url action_type |
videos_ai
| Action | Scopes | What it does |
|---|---|---|
POST videos_ai.generate
credits
|
generations:execute |
Runs a video agent (Veo, Kling, Sora, Seedance...) and bills the workspace.
input:
agent_id custom_prompt variables video_id count use_ai_optimization preprompt_context image_url end_image_url duration aspectRatio nativeAudio tier source folder_id |
POST videos_ai.extend
credits
|
generations:execute |
Extends a generated video by another segment.
input:
task_id prompt model agent_model agent_name agent_id source folder_id |
agents
| Action | Scopes | What it does |
|---|---|---|
POST agents.execute
credits
emits
agent_execution.completed |
agents:execute |
Runs any configured agent with its prompt template and variables.
input:
agent_id variables custom_prompt stream metadata |
GET agents.executions
|
agents:read |
History of agent runs with their outputs.
input:
agent_id limit |
generations
| Action | Scopes | What it does |
|---|---|---|
POST generations.launch
credits
|
generations:execute |
The playground pipeline: prompt in, image or video out, billed and stored.
input:
video_keyword video_id ai_generation_type ai_questions_number video_lang quiz_level_number quiz_questions_number generation_instructions generation_number generation_original_lang generation_translated_lang hooks_number all_questions ai_model_id search_web generation_type number_speakers add_intro_hook text |
music
| Action | Scopes | What it does |
|---|---|---|
POST music.generate
credits
|
music:execute |
Creates a Suno song from a prompt, lyrics or a style brief.
input:
prompt lyrics style title instrumental |
GET music.get_task
|
music:read |
Status and tracks of a music generation.
input:
generation_id |
characters
| Action | Scopes | What it does |
|---|---|---|
POST characters.generate_image
credits
|
characters:execute |
Generates a consistent image of a character.
input:
character_id model prompt agent_id reference_image_url variation_strength settings |
POST characters.smart_variants
credits
|
characters:execute |
Generates a batch of consistent variants for a character.
input:
character_id image_url model num_variants variation_strength analyze_only |
faceless
| Action | Scopes | What it does |
|---|---|---|
POST faceless.generate_story
credits
|
videos:execute |
Writes the script of a faceless video from a prompt.
input:
prompt language target_duration |
POST faceless.generate_outline
credits
|
videos:execute |
Produces the act/scene outline before the full script.
input:
prompt language |
POST faceless.parse_script
credits
|
videos:execute |
Splits a script into scenes ready for the faceless engine.
input:
script video_id |
POST faceless.generate_audio
credits
|
videos:execute |
Generates the voice-over of one or more scenes.
input:
video_id scene_id voice_settings |
POST faceless.generate_video
credits
|
videos:execute |
Generates the visual of a faceless scene (image or video model).
input:
video_id scene_id prompt |
POST faceless.check_status
|
videos:read |
Status of a running faceless scene generation.
input:
task_id video_id |
POST faceless.analyze_url
credits
|
videos:execute |
Extracts a usable brief from an article or video URL.
input:
url |
POST faceless.detect_characters
credits
|
videos:execute |
Finds recurring characters and proposes consistent prompts.
input:
script video_id |
smart_groups
| Action | Scopes | What it does |
|---|---|---|
POST smart_groups.generate
credits
|
videos:execute |
Builds a smart group (quiz, ranking, dialogue...) inside a video.
input:
video_id type topic |
POST smart_groups.action
credits
|
videos:execute |
Executes one of a smart group's declared actions.
input:
video_id group_id action |
POST smart_groups.create_qcm
|
videos:write |
Creates a multiple-choice question group from a question set.
input:
video_id questions |
tools
| Action | Scopes | What it does |
|---|---|---|
POST tools.generate_from_prompt
credits
|
videos:executegenerations:execute |
Draws a single image from a text prompt and stores it on the CDN. This is an image tool, not a video one. `videos.create` and the `*_to_video` actions build videos.
input:
prompt model size quality |
POST tools.image_to_prompt
credits
|
generations:execute |
Turns an image into a reusable generation prompt.
input:
image_base64 media_type image_url |
POST tools.remove_background
credits
|
generations:execute |
Returns a cut-out version of the image.
input:
image_url source_generation_id folder_id action_type |
POST tools.image_modification
credits
|
generations:execute |
Applies an instruction-based edit to an image.
input:
image_base64 image_url media_type prompt size quality input_fidelity agent_id |
POST tools.image_crop
|
assets:write |
Crops or reframes an image supplied inline as base64. It does not fetch a URL, upload the bytes or crop from an asset you already hold.
input:
image_base64 content_type metadata |
POST tools.reddit_post_to_video
credits
|
videos:execute |
Fetches a Reddit post and turns it into a video project.
input:
reddit_url comment_limit |
POST tools.youtube_to_karaoke
credits
|
videos:execute |
Builds a karaoke video composition from a YouTube URL.
input:
youtube_url |
POST tools.read_comments
credits
|
generations:execute |
Fetches the comments of a TikTok or Instagram post.
input:
post_url comment_limit |
POST tools.write_lyrics
credits
|
generations:execute |
Turns comments into song lyrics, each line keeping its comment id.
input:
post comments genre language target_seconds extra_direction clean ai_model_id model_ref workspace_id |
POST tools.variant_analyze
credits
|
generations:execute |
Analyses a media variant and reports what it contains.
input:
image_url original_prompt |
slideshows
| Action | Scopes | What it does |
|---|---|---|
POST slideshows.generate
credits
|
videos:execute |
Generates the visuals of every pending slide.
input:
slideshowId |
POST slideshows.export
credits
|
videos:execute |
Exports the finished slideshow.
input:
slideshowId format |
flows
| Action | Scopes | What it does |
|---|---|---|
POST flows.execute
credits
|
flows:execute |
Runs an automation flow and returns its execution.
input:
flowId variables |
GET flows.executions
|
flows:read |
Execution history of a flow.
input:
flowId |
publishing
| Action | Scopes | What it does |
|---|---|---|
POST publishing.schedule_post
credits
|
publishing:executepublishing:write |
Queues a video for publication on a connected account.
input:
video_id account_id scheduled_at caption |
GET publishing.calendar
|
publishing:read |
Scheduled posts grouped by day.
input:
from to |
account
| Action | Scopes | What it does |
|---|---|---|
GET account.credits
|
usage:read |
Current credit balance of the workspace. |
GET account.limits
|
usage:read |
Plan limits and current consumption. |
GET account.credit_history
|
usage:read |
Credit movements, newest first.
input:
page limit |
image_agents
| Action | Scopes | What it does |
|---|---|---|
GET image_agents.list
|
agents:read |
Image agents configured for the workspace. |
POST image_agents.create
|
agents:write |
Creates an image agent with its model and default parameters.
input:
agent_name agent_description agent_model agent_prompt_template agent_settings |
POST image_agents.update
|
agents:write |
Updates an image agent.
input:
agent_id agent_name agent_description agent_model agent_prompt_template agent_settings |
POST image_agents.delete
|
agents:delete |
Deletes an image agent.
input:
agent_id |
POST image_agents.generate
credits
|
generations:execute |
Starts an image generation job. Poll `image_agents.status` for the result.
input:
agent_id variables custom_prompt video_id count quality aspect_ratio source reference_image_url additional_reference_image_urls variation_strength folder_id source_generation_id source_image_url action_type |
POST image_agents.status
|
generations:read |
Status of one or more image generation jobs.
input:
generation_ids |
app_screenshots
| Action | Scopes | What it does |
|---|---|---|
GET app_screenshots.list
|
videos:read |
App-screenshot projects of the workspace. |
POST app_screenshots.create
|
videos:write |
Creates an app-screenshot project.
input:
project_name |
POST app_screenshots.update
|
videos:write |
Updates an app-screenshot project.
input:
project_id project_name global_prompt global_references agent_id screens |
POST app_screenshots.delete
|
videos:delete |
Deletes an app-screenshot project.
input:
project_id |
POST app_screenshots.generate
credits
|
generations:execute |
Generates one app-store screenshot from a project.
input:
project_id screen_id size quality |
voice_projects
| Action | Scopes | What it does |
|---|---|---|
GET voice_projects.list
|
voices:read |
Bulk text-to-voice projects.
input:
project_id |
POST voice_projects.create
|
voices:write |
Creates a bulk text-to-voice project.
input:
project_id project_name project_status project_original_language project_target_language project_main_voice_details original_text_value original_text_last_edit text_segmented_value translated_text_value translated_text_last_edit project_published |
POST voice_projects.update
|
voices:write |
Updates a bulk text-to-voice project.
input:
project_id project_name project_status project_original_language project_target_language project_main_voice_details original_text_value original_text_last_edit text_segmented_value translated_text_value translated_text_last_edit project_published |
POST voice_projects.delete
|
voices:delete |
Deletes a bulk text-to-voice project.
input:
project_id |
playground
| Action | Scopes | What it does |
|---|---|---|
POST playground.random_prompt
credits
|
generations:execute |
Generates an image prompt from a theme, for exploration.
input:
theme style |
POST playground.image_to_prompt
credits
|
generations:execute |
Turns an image into the prompt that would produce it.
input:
image_url |
instructions
| Action | Scopes | What it does |
|---|---|---|
POST instructions.generate
credits
|
generations:execute |
Writes the instruction set driving a generation from a brief.
input:
topic lang |
POST instructions.generate_with_news
credits
|
generations:execute |
Same, seeded with fresh news on the topic.
input:
topic lang |
assets
| Action | Scopes | What it does |
|---|---|---|
GET assets.static_library
|
assets:read |
The platform-provided asset library (backgrounds, overlays, sounds).
input:
category |
scripts
| Action | Scopes | What it does |
|---|---|---|
GET scripts.list
|
scripts:read |
The scripts in this workspace, newest edit first, with their id, length, runtime and how much of them has been voiced. Start here: every other script tool takes the `script_id` this returns.
input:
search limit skip |
POST scripts.create
|
scripts:write |
Start a new, empty script and return its id. Nothing is written yet: fill it with `script_draft_paragraphs` (from the subject), `script_write_from_source` (from text or a page) or `script_insert_paragraph` (by hand). A script always has one speaker, and every generated line is assigned to it, so `speaker` names the narrator.
input:
title topic language format structure target_duration_ms tone audience speaker voice_ref model_ref |
POST scripts.duplicate
|
scripts:write |
Copy an existing script into a new one: words, structure, cast and the recordings, which travel because they were paid for and the words they belong to are about to be identical. The credit meter does not: a copy has cost nothing yet. Use it before a rewrite somebody may want to compare against, or to keep a short cut and a long cut of the same video.
input:
script_id title |
POST scripts.delete
|
scripts:delete |
Delete a script and everything on it: paragraphs, restore points, review notes and the take history. Not reversible. Confirm with the person before calling it.
input:
script_id |
GET scripts.models
|
scripts:read |
The priced model catalog, as `model_ref` values. A script names one for its whole-document passes and, separately, one for the per-paragraph edits somebody presses forty times an afternoon. |
GET scripts.voices
|
scripts:read |
The voices this workspace can record with, as `voice_ref` values. Filter by language before choosing: a French script read by an English voice is a take somebody has to notice to undo, and it costs credits either way.
input:
language provider search limit |
POST scripts.ideas
credits
|
scripts:execute |
Subjects for a script that does not exist yet, each with the angle that makes it worth watching. The one writing pass that runs before there is a document, so it does not need a `script_id`. Spends credits.
input:
topic language format count idempotency_key |
GET scripts.open
|
scripts:read |
The whole document: every paragraph with its id, role, speaker, lock, recording and words, plus the chapters, the cast, the sources, the research and where the runtime stands against the target. Call this before you change anything -- every other tool addresses paragraphs by the `paragraph_id` it returns.
input:
script_id include_text digest request |
GET scripts.timing
|
scripts:read |
The running order with a timecode on every paragraph: where it starts, how long it runs, and whether that is measured from a real recording or estimated from the word count. Free, no model. This is how you answer "what is at 0:22" and "how much has to come out to make thirty seconds".
input:
script_id request |
GET scripts.speech_check
|
scripts:read |
The mechanical read a voice tool can make and a writing tool cannot: digits, symbols, acronyms, URLs, abbreviations and sibilant runs, per paragraph, with what the voice will actually be handed. Free, no model. Run it before spending credits on takes -- "40%" reaches the provider verbatim.
input:
script_id request |
POST scripts.settings
|
scripts:write |
What the document is written and read BY: the model for whole-document passes, the cheaper model for per-paragraph edits, the default voice, and the structure it is written to. Title, subject, tone, audience, language and target runtime are `script_set_meta`.
input:
script_id model_ref quick_model_ref default_voice_ref default_voice_name structure request |
POST scripts.write_from_source
credits
|
scripts:execute |
Turn material into a script: paste the text, or give a URL and it is fetched. REPLACES the paragraphs, so it is the way to start a document from something that already exists, not the way to add to one. `close` keeps the source order and its claims, `loose` takes the substance and rewrites it as a video. Spends credits.
input:
script_id source_url source_text source_title fidelity instruction paragraph_count replace request idempotency_key |
POST scripts.write_from_link
credits
|
scripts:execute |
Read one to four pages and write a whole script WITH A STANCE: a review, a comparison, an explainer, a hot take. Unlike `script_write_from_source` this one is fenced -- no number, price, date or version may come from anywhere but the pages, and whatever the model wanted to say and could not verify comes back in `unverified` for somebody to check. Answer the angle honestly: `experience` decides whether the script is allowed a single first-person claim. REPLACES the paragraphs. Spends credits.
input:
script_id urls angle answers instruction paragraph_count request idempotency_key |
GET scripts.export
|
scripts:read |
The document rendered as text, in one of: txt, markdown, srt, vtt, teleprompter, csv, shotlist, json. `srt` and `vtt` are timed from the real takes where they exist, `shotlist` carries the visual direction, `csv` is one row per paragraph. Returns the content itself, not a download link.
input:
script_id format request |
POST scripts.to_video
credits
|
scripts:execute |
Create a video from the script: one scene per paragraph, each carrying its recording where one exists. This is the hand-over to the video side of the platform -- from there it is `videos` and `renders` like any other project. Voice the script first, or the scenes come out timed by an estimate rather than by the real audio.
input:
script_id format request idempotency_key |
GET scripts.snapshots
|
scripts:read |
The restore points on this script, newest first, with what each one was taken before. One is taken automatically before the first write of any call, so there is always something to go back to. Restore one with `script_restore_snapshot`.
input:
script_id request |
GET scripts.changes
|
scripts:read |
The edit log: every change to this document, newest first, with the tool that made it and the sentence that asked for it. Filter by `paragraph_id` to see the history of one line.
input:
script_id paragraph_id limit before request |
POST scripts.share
|
scripts:write |
Turn the read-only review link on or off, decide whether reviewers may leave notes, or rotate the token so the old URL stops working. The token is minted here and can never be chosen: it is a capability.
input:
script_id enabled allow_notes rotate request |
GET scripts.notes
|
scripts:read |
The notes left through the review link, resolved ones included, each pointing at the paragraph it is about. Posting a note is the reviewer door and is not reachable from here.
input:
script_id open_only request |
POST scripts.resolve_note
|
scripts:write |
Mark a reviewer note resolved, reopen it, or delete it. Resolve it once the change it asked for is actually in the document, never to tidy the list.
input:
script_id note_id resolved delete request |
GET scripts.snippets
|
scripts:read |
Reusable lines this workspace keeps: sign-offs, disclaimers, calls to action. Drop one into the document with `script_insert_snippet`, save a new one with `script_save_snippet`.
input:
script_id search limit request |
GET scripts.style_guide
|
scripts:read |
This workspace's own rules, in the three kinds that act in three places: `banned` and `preferred` shape the WRITING and are injected into every prompt, `spoken` acts on the VOICE only and is applied between the paragraph and the provider. Add one with `script_add_style_rule`.
input:
script_id request |
GET scripts.speakers
|
scripts:read |
The cast: every named speaker, the voice they are recorded in, who they are, and how many lines and words are theirs. The FIRST one is the base speaker -- every line written lands on them and they can never be removed. A speaker with no voice of their own is read in the document default, which is what makes a two-hander come back in one voice.
input:
script_id with_lines request |
POST scripts.set_speaker_voice
|
scripts:write |
Record everything this person says in a voice of their own, whatever the document default is. This is what makes a dialogue sound like two people: without it every speaker is read by the same voice and the script is a monologue with names on it. Pass `voice_ref` from `script_voices`, or null to put them back on the default. Changes nothing already recorded -- re-record those with `script_voice` and `force`.
input:
script_id speaker_id name voice_ref request |
POST scripts.update_speaker
|
scripts:write |
Change a speaker name or their persona -- the line describing how they talk, which every later AI pass reads before writing for them. Their lines, their voice and their id are untouched, so renaming is safe.
input:
script_id speaker_id name new_name persona request |
POST scripts.remove_speaker
|
scripts:write |
Take somebody out of the cast. Their lines are NOT deleted: they go back to the base speaker, which is where an unassigned line belongs. The base speaker themself cannot be removed -- a script is somebody talking, so the list never reaches zero. Rename them instead.
input:
script_id speaker_id name request |
POST scripts.voice
credits
|
scripts:execute |
Record paragraphs with a real voice. SPENDS CREDITS, one synthesis per paragraph per take. With no `paragraph_ids` it does the whole script, and it is safe to run twice: a take whose words have not changed is left alone, so it fills the gaps and re-does the stale ones rather than re-buying what is there. `takes` (1 to 3) buys several readings of each paragraph and KEEPS them all -- that is how you compare two performances. The voice is the most specific one that applies: the paragraph, then its speaker, then `voice_ref` here, then the document default. A paragraph with no voice anywhere in that chain is skipped rather than read by an arbitrary one. Listen to the result with `script_listen`.
input:
script_id paragraph_ids voice_ref voice_name takes force dry_run request idempotency_key |
GET scripts.takes
|
scripts:read |
What has been recorded: per paragraph, every take that was kept, which one is CHOSEN, how long it runs, what voice read it, and whether the words have moved since (a stale take is one somebody has to re-buy). Free.
input:
script_id paragraph_ids stale_only missing_only request |
GET scripts.listen
|
scripts:read |
Play a recording back. Returns the audio itself, not a link to it, so you can hear what was bought and say whether it is any good. Name a paragraph for its chosen take, add `take_id` for a particular one, or pass several paragraphs (up to 4) to compare readings. Free: the take already exists.
input:
script_id paragraph_id take_id paragraph_ids all_takes request |
POST scripts.choose_take
|
scripts:write |
Make one of a paragraph recordings THE one: the take the player, the export, the subtitle timing and the hand-over to a video all read. The others are kept beside it. Listen before you choose.
input:
script_id paragraph_id take_id request |
POST scripts.delete_take
|
scripts:write |
Drop a recording you are not going to use. A paragraph keeps a handful of takes and the oldest falls off the end, so clearing out the readings nobody wants is what makes room to try again. If the one you delete was the CHOSEN take, the newest of the rest takes its place; delete the last one and the paragraph is unvoiced again. The credits are already spent either way -- this frees nothing but the list.
input:
script_id paragraph_id take_id take_ids keep_chosen request |
POST scripts.pace
|
scripts:write |
A paragraph own playback rate, 0.75 to 3, stored ON the document: it is what the page plays it at AND what the audio export writes it out at, so it survives a reload and a re-recording. `null` means FOLLOW whatever the listener or the export is set to, and 1 is NOT null: "hold this one at 1x while the rest go to 2x" is a real instruction. Speeding up never raises the pitch.
input:
script_id paragraph_ids rate clear request |
GET scripts.narration
|
scripts:read |
Every chosen take in document order, with the file it lives in, the pace it is written out at and how long the finished narration runs. What is NOT recorded is reported rather than padded with silence: an export that quietly skips half a script is an export that lies about being finished. The joining into one file happens in the browser (it is where the time-stretch runs); this is the plan and the pieces.
input:
script_id speed container request |
GET scripts.read
|
scripts:read |
Read the FULL text of paragraphs. The document block above only shows the first line of each one, so call this before you rewrite, quote, judge or continue anything. Give paragraph_ids, or a from/to range, or a role, or a search term. With no arguments it reads the whole script.
input:
script_id paragraph_ids from to role query request |
GET scripts.report
|
scripts:read |
Measure the script: runtime against its target and how many words that is, the shape of it by role, house-style rules it breaks, paragraphs that repeat an opening, recordings that no longer match their words. Costs nothing and runs no model. Call it before and after any length work.
input:
script_id request |
POST scripts.replace_everywhere
|
scripts:write |
Swap one string for another across the whole document, changing nothing else. This is the ONLY correct answer to "replace X with Y", "rename X", "we say Y now". Never rewrite a paragraph to perform a swap.
input:
script_id find replace match_case whole_word request |
POST scripts.set_paragraph_text
|
scripts:write |
Write exact words into one paragraph, keeping its role, its visual, its recording and its history. Use this when YOU wrote the sentence (a hook you drafted in the chat, a typo you fixed). Use rewrite_paragraphs instead when the writing model should do the work.
input:
script_id paragraph_id text request |
POST scripts.rewrite_paragraphs
credits
|
scripts:execute |
Run the script own writing model over paragraphs with an instruction. This is the same writer the editor buttons use, with this script chosen model and the workspace house style, so the result sounds like the rest of the tool. One model call per paragraph, so keep the list to what the user actually asked for.
input:
script_id paragraph_ids instruction operation target_words target_language request idempotency_key |
POST scripts.insert_paragraph
|
scripts:write |
Add a new paragraph, with your own words, at a chosen place in the document.
input:
script_id text role after_paragraph_id chapter_id request |
POST scripts.delete_paragraphs
|
scripts:write |
Remove paragraphs from the script. The restore point taken at the start of this turn is the way back, so say what you deleted clearly enough for the user to ask for it back.
input:
script_id paragraph_ids request |
POST scripts.move_paragraph
|
scripts:write |
Move one paragraph to another position. `to_index` counts the document AFTER the paragraph is lifted out, so moving index 0 to index 3 puts it fourth. Move one at a time and re-read the map between moves.
input:
script_id paragraph_id to_index request |
POST scripts.set_paragraph_role
|
scripts:write |
Label what a paragraph IS: hook, intro, body, transition, example, punchline, cta, outro. The role drives the colour in the outline and the way later AI passes treat it.
input:
script_id paragraph_ids role request |
POST scripts.lock_paragraphs
|
scripts:write |
Lock or unlock paragraphs. A locked paragraph is never touched by any document-wide pass, yours included. Lock freely when the user says a line is final. NEVER unlock without being asked in so many words.
input:
script_id paragraph_ids locked request |
POST scripts.annotate_paragraph
|
scripts:write |
Leave a note in the margin of a paragraph. The editor shows it, and every later AI pass on that paragraph is given it as an instruction. Use it to park an idea the user has not asked you to act on yet. Pass an empty string to clear one.
input:
script_id paragraph_id note request |
POST scripts.set_paragraph_visual
|
scripts:write |
Say what is on screen while a paragraph is spoken: one line of direction, what KIND of shot it is, and the keywords somebody would type into a stock library.
input:
script_id paragraph_id direction kind broll_keywords image_prompt request |
POST scripts.set_meta
|
scripts:write |
Change what the document IS rather than what it says: its title, subject, tone, audience, language code, short or long form, and the runtime it is written to. Send only the fields you are changing.
input:
script_id title topic tone audience language format target_duration_ms request |
POST scripts.translate
credits
|
scripts:execute |
Translate the WHOLE document in one call: every unlocked paragraph, the title with it, and the document language field. Use this rather than looping rewrite_paragraphs over forty paragraphs.
input:
script_id target_language language_code request idempotency_key |
POST scripts.snapshot
|
scripts:write |
Take a named restore point before something large. One is taken automatically before your first edit of a turn, so only call this when the user asks for a marker, or before a pass you expect them to regret.
input:
script_id label request |
POST scripts.restore_snapshot
|
scripts:write |
Put the words back to a restore point. Takes its own safety point first, so restoring is itself undoable. The restore points are listed in the document block, newest first.
input:
script_id snapshot_id request |
GET scripts.history
|
scripts:read |
Read what has been done to this script: who changed what, when, and what they were asked for. Newest first. Pass a paragraph_id to see only one paragraph's history. Each entry has an edit_id, which `revert_change` takes. Free, no model.
input:
script_id paragraph_id limit request |
POST scripts.undo_change
|
scripts:write |
Undo ONE entry from the change log, by edit_id: the paragraphs it rewrote go back to the words they had. Everything done since is left alone (use `restore_snapshot` to go back to a whole earlier state). Locked paragraphs are skipped. Read `read_change_log` first to get the edit_id.
input:
script_id edit_id request |
GET scripts.check_flow
credits
|
scripts:execute |
Read the SEAMS between paragraphs: abrupt cuts, things said twice, contradictions, pacing, paragraphs that wandered off topic. Returns a score and a list of issues, each with a ready replacement where there is one. Changes nothing. The right first move for "does this flow?", "it feels off", "it repeats itself".
input:
script_id request idempotency_key |
POST scripts.research_all
credits
|
scripts:execute |
Fact-check the WHOLE script. It works out which separate subjects the script stands on, searches each one on its own, and files every source on the document along with what could NOT be confirmed. This is the tool for "research this script", "check my facts", "is any of this out of date" and for a script the user has just pasted in. It searches at most 4 subjects and costs one web search per subject. For ONE specific question, use research_topic instead: it is a fraction of the price.
input:
script_id question request idempotency_key |
GET scripts.analyze
credits
|
scripts:execute |
Numbers about the script as an object: how dense it is, how strong the opening is, where a listener is most likely to leave, reading level, pace, and the single highest-leverage change. Changes nothing. Use it when asked whether the script is any good, before offering an opinion of your own.
input:
script_id request idempotency_key |
GET scripts.suggest_hooks
credits
|
scripts:execute |
Write several openers for this script, each pulling a different lever, with the angle named. Writes NOTHING: read them out and let the user choose, then apply the chosen one with set_paragraph_text.
input:
script_id count request idempotency_key |
GET scripts.suggest_titles
credits
|
scripts:execute |
Write several titles for this script. Writes NOTHING: offer them, then set the chosen one with set_script_meta.
input:
script_id count request idempotency_key |
GET scripts.write_variants
credits
|
scripts:execute |
Write several alternative versions of ONE paragraph, so the user can compare them side by side. Writes NOTHING. This is the right answer to "give me options", "a few versions", "another way of saying it" -- better than rewriting once and hoping.
input:
script_id paragraph_id count instruction request idempotency_key |
POST scripts.outline
credits
|
scripts:execute |
Plan the script before any of it is written: chapters, and the beats each one needs. Writes the CHAPTERS and returns the beats for you to read out. It does not write a single paragraph -- call draft_paragraphs for that, once the user has agreed to the plan.
input:
script_id instruction chapter_count request idempotency_key |
POST scripts.draft_paragraphs
credits
|
scripts:execute |
Write the actual script, from its topic, its structure and whatever chapters exist. EXPENSIVE: this is one large model call and it appends a whole draft. Say what you are about to do first. On a document that already has paragraphs it ADDS to them, so outline first, or the user ends up with two scripts in one.
input:
script_id instruction paragraph_count request idempotency_key |
POST scripts.group_into_chapters
credits
|
scripts:execute |
Read the script as it stands and group it into chapters, assigning every paragraph to one. Use it on a document that was written straight through and has grown a shape nobody named.
input:
script_id request idempotency_key |
POST scripts.split_paragraph
credits
|
scripts:execute |
Break one paragraph that is doing too much into two or three that each do one thing. The first part KEEPS the original id, its history and its recording; the rest are new. Better than deleting and re-adding.
input:
script_id paragraph_id request idempotency_key |
POST scripts.direct_visuals
credits
|
scripts:execute |
Write a shot direction for EVERY paragraph in one pass: the kind of shot, the direction, the stock keywords. Use set_paragraph_visual instead when it is one or two paragraphs.
input:
script_id instruction request idempotency_key |
POST scripts.polish
credits
|
scripts:execute |
One editing pass over the WHOLE document at once: tightening, the same word used twice in a row, sentences that do not say themselves out loud. EXPENSIVE, and it touches many paragraphs, so say what it will do first. Locked paragraphs are skipped and counted.
input:
script_id instruction request idempotency_key |
POST scripts.research_topic
credits
|
scripts:execute |
Look up facts for this script and store them as SOURCES on the document, so every later pass can cite them instead of guessing. Use it when the user asks whether something is true, asks for numbers, or asks you to back a claim up. It adds sources, it does not edit a paragraph.
input:
script_id question request idempotency_key |
GET scripts.retention
|
scripts:read |
Where a viewer is most likely to stop watching, paragraph by paragraph, with a timecode and the reason. Also finds phrases the script repeats to itself. Costs NOTHING and runs no model, so call it freely: before any length work, and any time the user asks why a video is not being watched to the end.
input:
script_id request |
GET scripts.add_style_rule
|
scripts:read |
Write a rule into the WORKSPACE house style guide. It applies to every script anybody writes here, from now on, and it is attached to every AI pass automatically. Use it when the user states a standing preference ("we never say X", "always write it Y"), never for a one-off edit. Ask before adding one they only implied.
input:
script_id kind term replacement note brief request |
GET scripts.save_snippet
|
scripts:read |
Put a paragraph in the workspace snippet library, so it can be dropped into any future script unchanged. The right answer to "keep this one", "save that line", "I always open like this".
input:
script_id paragraph_id text title role tags request |
POST scripts.insert_snippet
|
scripts:write |
Drop a saved snippet into this script, word for word. Call it with no `snippet_id` to LIST what is on the shelf first, then again with the one the user picked. The whole value of a snippet is that nothing rewrites it, so never paraphrase one.
input:
script_id snippet_id after_paragraph_id query request |
POST scripts.add_source
|
scripts:write |
Read a URL and attach it to the script as a source, with whatever the page declares about itself. Every later AI pass gets those facts, so a rewrite six paragraphs later still knows the number. Use it whenever the user pastes a link or asks where a claim comes from.
input:
script_id url snippet request |
POST scripts.add_speaker
|
scripts:write |
Add a named speaker to the script, so a paragraph can say who is talking and get its own voice. Use it for a dialogue, an interview, a two-hander. Adding one changes nothing on its own: assign paragraphs to it with assign_speaker.
input:
script_id name persona request |
POST scripts.assign_speaker
|
scripts:write |
Say which speaker says which paragraphs. Send speaker_id as an empty string to give them back to the base speaker (the first one), which is who says every line nobody else was given.
input:
script_id paragraph_ids speaker_id request |
POST scripts.film
credits
|
scripts:execute |
Film paragraphs: the speaker's photo saying the line, lip-synced to the recording already on it. SPENDS CREDITS, billed per second of clip, so say what it will cost before calling it. It STARTS the jobs and returns AT ONCE: one clip takes one to three minutes, the pending clips are written onto the document, and they finish whether or not anybody is watching. Do NOT poll and do not promise a link in this turn. Each paragraph needs two things, and this reports the ones missing rather than buying them: a face on the speaker (set_speaker_face) and a voice recording (script_voice, or the editor Voice tab). Safe to run twice, a paragraph already filmed against the same recording is left alone unless `force`. Call it with `dry_run` first whenever the user has not agreed to a number.
input:
script_id paragraph_ids model_id prompt force dry_run request idempotency_key |
POST scripts.set_speaker_face
|
scripts:write |
Give a speaker the photo they are filmed from, or clear it. One face per PERSON rather than per paragraph: it is a property of whoever is talking, so every line they say is filmed from it, including lines written later. The url has to be a public image the lip-sync provider can fetch, which anything in the user own file library is. With no speaker_id it sets the base speaker, who says every line nobody else was given. Free, and nothing is filmed by setting it.
input:
script_id image_url speaker_id aspect clear request |
GET scripts.clips
|
scripts:read |
What has been filmed, and what stands in the way. Per paragraph: whether it has a clip, whether the clip is still running, WHICH MODEL made it, how long it runs and what it cost, plus how many attempts are kept. Also which speakers have a face and which lip-sync models this deployment can run, with the price per second. Free, no model call, so read it before quoting a cost and before saying a clip is late.
input:
script_id paragraph_ids pending_only request |
POST scripts.choose_clip
|
scripts:write |
Keep one filmed attempt as the paragraph clip: the one the export and the video editor use. The others stay in the list, so this is reversible and costs nothing. read_film_status names the attempts.
input:
script_id paragraph_id clip_id request |
POST scripts.delete_clip
|
scripts:write |
Drop one filmed attempt from a paragraph list. The mp4 itself is LEFT on storage, exactly as a deleted take leaves its audio: the same url may already sit in a timeline layer, and what this shortens is the list, not the bucket. Deleting the chosen clip promotes the newest survivor rather than leaving the paragraph blank. Nothing is refunded, the clip was already paid for.
input:
script_id paragraph_id clip_id request |
Machine-readable spec: /api/v1/openapi.json, import it into Postman, Insomnia or an SDK generator.