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.

ResourceActionsWhat it covers
videosreadwritedeleteexecuteVideo projects: timeline, blocks, metadata.
scriptsreadwritedeleteexecuteThe script writer: documents, paragraphs, the AI passes and the voice-over takes.
rendersreadwritedeleteexecuteRender jobs and their output files.
generationsreadwritedeleteexecuteImage, video, audio and music generations.
assetsreadwritedeleteUploaded files and generated media.
foldersreadwritedeleteFolder tree used to organise videos and assets.
charactersreadwritedeleteexecuteRecurring characters and their reference images.
backgroundsreadThe curated shelf of background stills and loops.
voicesreadwritedeleteexecuteVoice catalog, cloned voices and TTS connections.
templatesreadwritedeleteVideo templates, styles and smart-group configurations.
musicreadwritedeleteexecuteMusic library and AI song generation.
agentsreadwritedeleteexecuteImage/video agents and their executions.
flowsreadwritedeleteexecuteAutomation flows and their runs.
publishingreadwritedeleteexecuteSocial accounts and scheduled posts.
webhooksreadwritedeleteWebhook endpoints and their delivery history.
workspacereadwriteWorkspace profile, members and settings.
usagereadCredit balance, API usage and request logs.
api_keysreadwritedeleteManage API keys themselves. Grant with care.

Presets

PresetScopes
read_only
Fetch everything, change nothing. Safe for dashboards and exports.
*:read
content_editor
Create 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
automation
Full 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_access
Everything 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:.

FormatUse
jsonThe envelope above. The default.
ndjsonOne object per line, for streams, jq and pipelines.
csvFlattened 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 (*).

EventMeaning
render.startedA worker picked a queued render up and started working on it.
render.completedA render finished and its output URL is available.
render.failedA render stopped on an error.
generation.completedAn AI generation (image, video, audio) produced its result.
generation.failedAn AI generation failed. Credits already spent are reported in the payload.
music.completedA music generation finished and its tracks are hosted.
music.failedA music generation failed. The credits it reserved were refunded.
post.publishedA scheduled post went out. Also fired on a partial publish — read `target_accounts` for the per-account outcome.
post.failedA scheduled post could not be published.
flow.completedA flow execution reached its end.
flow.failedA flow execution stopped on an error.
agent_execution.completedAn agent finished running.
action.completedAny `/v1/actions/*` call that succeeded. Useful as a catch-all audit feed.
action.failedAny `/v1/actions/*` call that returned an error.
credits.lowA call was refused for lack of credits. The payload carries what it needed, what was left and the shortfall.
webhook.testSent by `POST /v1/webhooks/{id}/test`. Never emitted by the platform.
video.createdA video was created through the API or the dashboard.
video.updatedA video was updated through the API or the dashboard.
video.deletedA video was deleted through the API or the dashboard.
folder.createdA folder was created through the API or the dashboard.
folder.updatedA folder was updated through the API or the dashboard.
folder.deletedA folder was deleted through the API or the dashboard.
asset.createdA asset was created through the API or the dashboard.
asset.deletedA asset was deleted through the API or the dashboard.
character.createdA character was created through the API or the dashboard.
character.updatedA character was updated through the API or the dashboard.
character.deletedA character was deleted through the API or the dashboard.
agent.createdA agent was created through the API or the dashboard.
agent.updatedA agent was updated through the API or the dashboard.
agent.deletedA agent was deleted through the API or the dashboard.
flow.createdA flow was created through the API or the dashboard.
flow.updatedA flow was updated through the API or the dashboard.
flow.deletedA flow was deleted through the API or the dashboard.
slideshow.createdA slideshow was created through the API or the dashboard.
slideshow.updatedA slideshow was updated through the API or the dashboard.
slideshow.deletedA slideshow was deleted through the API or the dashboard.
template.createdA template was created through the API or the dashboard.
template.updatedA template was updated through the API or the dashboard.
template.deletedA template was deleted through the API or the dashboard.
video_style.createdA video_style was created through the API or the dashboard.
video_style.updatedA video_style was updated through the API or the dashboard.
video_style.deletedA video_style was deleted through the API or the dashboard.
smart_group_config.createdA smart_group_config was created through the API or the dashboard.
smart_group_config.updatedA smart_group_config was updated through the API or the dashboard.
smart_group_config.deletedA smart_group_config was deleted through the API or the dashboard.
custom_video_type.createdA custom_video_type was created through the API or the dashboard.
custom_video_type.updatedA custom_video_type was updated through the API or the dashboard.
custom_video_type.deletedA custom_video_type was deleted through the API or the dashboard.
scheduled_post.createdA scheduled_post was created through the API or the dashboard.
scheduled_post.updatedA scheduled_post was updated through the API or the dashboard.
scheduled_post.deletedA scheduled_post was deleted through the API or the dashboard.
character_tag.createdA character_tag was created through the API or the dashboard.
character_tag.updatedA character_tag was updated through the API or the dashboard.
character_tag.deletedA character_tag was deleted through the API or the dashboard.
playground_generation.updatedA playground_generation was updated through the API or the dashboard.
playground_generation.deletedA playground_generation was deleted through the API or the dashboard.

Resources

Videos videos

Video projects with their timeline, blocks and generation settings.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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.

MethodPath
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

ActionScopesWhat it does
POST renders.create credits
emits render.started
renders:execute
renders: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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat it does
POST tools.generate_from_prompt credits videos:execute
generations: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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat it does
POST publishing.schedule_post credits publishing:execute
publishing: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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat 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

ActionScopesWhat it does
GET assets.static_library assets:read The platform-provided asset library (backgrounds, overlays, sounds).
input: category

scripts

ActionScopesWhat 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.