Skip to main content
A video’s settings are around three dozen JSON option groups: style_options, playback_options, autoplay_options, captions_options, chapters_options, quiz_options, and so on, the same groups the dashboard editor writes to. This page covers the four calls that manage them through the API: save a draft, deploy, list past deploys, and restore one. Both need a token with videos:write.
Two things about this API surprise every integration that has not read this page first: the payload you send is a partial update, not a full replacement, and deploy is synchronous. Read both sections below before you wire this up.

The payload is partial, and merges by key

Send only the option groups, or the fields inside them, that you want to change. Whatever you omit is left exactly as it already is, because the server takes your payload and merges it recursively over the current settings with PHP’s array_replace_recursive, key by key, all the way down.
background_color was never in your payload, so it survives untouched.
This recursive merge works key by key, which is exactly what you want for an object like style_options. It does not truncate a list. An option group that holds an array, chapters_options.chapters, cta_cards_options.cards, works by numeric index: sending three items over an existing five leaves indexes 3 and 4 in place from before, it does not shrink the list to three. If you are changing a list-shaped option group, send the complete list, not a partial one.
Your payload is filtered to the option groups the video already exposes before anything else happens. A top-level group outside that set is dropped silently: no error, no 422, and a 200 response that looks like it worked.Five groups behave this way today and cannot be set through this API at all, even though the validation schema accepts them: actions_options, cloak_options, headline_options, pitch_time_options and share_options. Send any of them and you get a 200 with nothing changed. Use the dashboard editor for those.Nested keys are not filtered. Only the top-level group name has to be one the video already carries, which is why an option with no dashboard toggle can still be set here as long as its group is a real one.
Validation runs on the merged result, through the same rules the dashboard editor uses, not on your payload alone. Most fields are optional, but a few (style_options. main_color, style_options.background_color, progress_options.background_color and progress_options.bar_color among them) are hard-required in the schema. In practice this only bites if your existing settings somehow do not already satisfy them; a normal partial update to unrelated fields will not trip it. Any top-level key you send that is not a real option group is dropped silently rather than rejected: only the option groups the video already carries are read out of your request body, which is the same filter that drops the five groups listed above.

Save a draft

integer
required
The video’s numeric id.
object
Any subset of the option-group columns. See Get settings for the full shape.
curl
200 OK
A draft is stored, not published. It does not change what the live embed serves, it is not compiled, and it fires no webhook. version is always null here: a draft never gets a version id, only a deploy does.
Saving a draft does not queue it to go live on the next deploy call. Deploy reads from your live settings, not from the draft row. See the next section.

Deploy

integer
required
The video’s numeric id.
object
Any subset of the option-group columns, merged the same way as /draft, over the video’s current live settings.
string
An optional label stored on the version this call creates. Shows up in the deploys list.
curl
200 OK
Deploy merges your payload over the live settings, not over your saved draft. If you called /draft earlier and now call /deploy with an empty or different body, the draft’s changes are not applied. To publish what you drafted, send the same payload to /deploy that you sent to /draft. Either way, deploy deletes the pending draft row afterward, whether or not it matched what you deployed.
Deploy does four things, in order, and they are why the call is slow:
1

Merge and validate

Same partial-merge-and-validate as /draft, but against live settings.
2

Recompile the player bundle

Synchronously. This calls out to build the bundle and push it to two CDNs. It is not queued and there is no background job id to poll: the HTTP request itself does not return until this finishes.
3

Snapshot a version

Only after the compile and CDN push both succeed. This is what makes deploy safe: if the bundle build or the CDN push fails, the call throws before any version is snapshotted or marked live, and the previously live settings keep serving. You get a 500 and nothing changed.
4

Fan out

Busts the AI-segment settings cache and fires the video.settings.deployed webhook, {"video_id": <id>, "version_id": <id>}.
Because deploy compiles and pushes to a CDN synchronously, it is slow relative to every other call on this API, and it is not built to be called in a tight loop. If you are applying several changes, batch them into one /deploy call rather than calling it once per field.
The response’s embed_url is the public URL for the video’s embed page on the version this call published.

List past deploys

integer
required
The video’s numeric id.
curl
200 OK
Every row carries its full settings_snapshot, the complete option-group object at that point in time, not a diff. A workspace with a long deploy history will get a large payload back; page through it with ?page=. Only deploys and restores create a version. Draft saves never appear here.

Restore a version

integer
required
The video’s numeric id.
integer
required
A version id from the deploys list above.
curl
200 OK
Restore is not a partial merge. It writes the chosen version’s settings_snapshot back onto the video wholesale, replacing the live settings entirely, then recompiles synchronously (same throw-before-live-marking guarantee as deploy) and snapshots a new version labeled Restored from version 104. It does not fire a webhook.

Errors

The token is wrong, expired, or revoked.
All four calls need videos:write.
No video with that id (or, for restore, no version with that id on this video) in your workspace. Laravel’s default body: {"message": "No query results for model [App\\Models\\WorkspaceVideo] 512"}.
The merged settings failed validation. {"message": "The given data was invalid.", "errors": {"style_options.main_color": ["The style options.main color field is required."]}}. This can fire even on a small partial payload, since validation runs on the full merged object.
Rate limited. 60 writes per minute per token. Deploy’s own compile time adds to this, so a burst of deploys is slower to clear than a burst of any other write.
Deploy or restore failed to compile or push to the CDN. No custom error code: this is an uncaught server error. Nothing was published, the video is still serving its previous live settings, and no version was created. Retry the call.