Skip to content

renders.createFromStoryboard

Make a video from a storyboard you approved

POST/storyboards/{id}/renders

Operation
renders.createFromStoryboard
MCP tool
renders_createFromStoryboard

Turn a finished storyboard into a video: the beats it proved, in order, with the words you approve. Each beat may have its narration rewritten or be hidden altogether; everything else about a beat — what it clicks, where it goes, what it types — is the plan's own and is not accepted from a caller. The render inherits the storyboard's options, so the video matches what was planned. Answers 202 with a Location; the cost and the settlement are renders.create's.

curl -X POST "https://api.gogoscreen.com/api/v1/storyboards/<id>/renders" \
  -H "Authorization: Bearer $GOGOSCREEN_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
A request to this operation.

At a glance

FactDetail
Scopesrenders:write
Credentialsa signed in dashboard session or an API key.
Rate limit20 requests a minute per credential, in the charge class.
IdempotencyRequired. Send an Idempotency-Key header; a retry with the same key and the same body returns the stored answer with Idempotent-Replayed: true. The key is also written to the record this creates, so a retry cannot leave two behind. How idempotency works.
Long workAnswers 202 and queues a walkthrough.render. Poll /renders/{id} until state is terminal, or subscribe to a webhook. This kind can be cancelled while it runs. Jobs and polling.
MCP toolrenders_createFromStoryboard
Always setsCache-Control: private, no-store
Verified emailRequired. An account whose email address is not verified meets 403 email_unverified before the operation runs.

Path parameters

NameTypeRequiredDescription
idstringRequiredThe storyboard to render.format: uuid

Request body

A JSON body is optional. Unknown fields are refused rather than ignored, so a typo is a 400 validation_failed rather than a setting that silently did nothing.

NameTypeRequiredDescription
beatsobject[]OptionalThe beats to keep and what they say. Absent renders the plan as it stands.max 60 items
beatIdstringRequired1–64 characters
hiddenbooleanOptionalLeave this beat out of the video.
narrationstring | nullOptionalReplace this beat's line. Null or absent keeps the planned one.max 2000 characters
credentialsobjectOptionalOnly needed when the storyboard's own session credential has expired. They replace it: kept encrypted for that storyboard's session and deleted at most two hours after their last use.
additionalLoginOriginsstring[]OptionalOther exact https origins this account's sign-in may pass through, e.g. ["https://login.example.com"] for a single sign-on provider on its own host. At most 5. Nothing else — not a redirect, a link or a sibling subdomain — is ever approved on its own: a sign-in that moves to an origin not listed here stops before the account is typed. Ordinary same-origin sign-ins need none.max 5 items
loginUrlstringRequiredREQUIRED whenever credentials are given: the page this demo account signs in on, e.g. https://app.example.com/login. Any origin — a product commonly signs in on a different host from its marketing site, and this is what tells the run where the app itself lives. Must be https; checked by the same guard as the site address. Its origin is the only place the account is typed unless additionalLoginOrigins names others.max 2000 characters
passwordstringRequired1–200 characters
usernamestringRequired1–200 characters

Response

This operation documents 2 success statuses. A first call answers 202. Every answer also carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset, and every error body carries a requestId.

200 OK

The work was already known — the service recognised this key on its own record and queued nothing new. Answered when the 24-hour idempotency claim has expired but the work has not.

200 OK body
NameTypeDescription
createdboolean
credentialSourcestring | null
lengthFitobject | nullWHAT THIS RENDER DID ABOUT THE LENGTH. The storyboard's own lengthFit is about the PLAN; this one is about the video just queued, and the two differ exactly when the chosen length cannot hold every beat the plan proved. Null when the storyboard names no length at all, and null on a replayed request, which measured nothing.
11 fields inside lengthFit
NameTypeDescription
adjustedbooleanWhether this video is shorter than the storyboard's plan. Render at a longer length, or hide beats yourself, to choose what goes.
beatBudgetnumber | nullThe steps that length can hold.
beatsDroppednumberSteps this render leaves out because the length cannot hold them. Taught extras yield their slot first; only then is the walkthrough itself cut, and always off the end.
beatsKeptnumberSteps the video will actually show.
beatsPlannednumberSteps the storyboard offered this render, after your own hidden beats were taken out and any taught steps put back.
cappedFromSecondsnumber | nullThe length asked for, when a shorter one was used instead. Null on this operation.
maxSecondsnumber | nullThe ceiling an Auto render is sized against, or null when a fixed length was chosen.
notestring | nullThe sentence to show, e.g. "Plan adjusted to fit 60 seconds: 10 of 13 steps kept." Null when nothing was adjusted.
plannedSecondsnumber | nullWhat the kept steps are worth in seconds — what the video will run to, before the read is measured.
secondsnumber | nullThe length everything below was decided from: the fixed length, or the Auto ceiling.
targetSecondsnumber | nullThe fixed length this render holds, or null when the storyboard was planned as Auto.
resourceobject
35 fields inside resource
NameTypeDescription
artifactsExpiredbooleanThe work succeeded but its files were deleted at the end of the retention period.
cancellableboolean
costobject
secondsChargednumber | null
secondsReservednumber
watermarkboolean
createdAtstring | nullformat: date-time
errorobject | null
codestring
reasonstring | null
retryableboolean
terminalboolean
expiresAtstring | nullformat: date-time
finishedAtstring | nullformat: date-time
idstring
idempotencyKeystring | null
internalStatusstringA finer-grained status word. Informational; branch on state.
kindstringwalkthrough.render | walkthrough.storyboard | marketing.export | marketing.generation | marketing.source_scan | marketing.source_operation
pollAfterMsnumber | nullWait this long before polling again; null once the state is terminal.
progressobject | nullHOW FAR THE WORK HAS GOT, as last reported, or null — null while nothing has been recorded yet and null for every terminal state, because a finished job is 100 by definition and a failed one has no bar to draw.
atstring | nullWhen it was reported. A reading that has stopped moving means the work has, not that the bar is broken.
percentnumber0 to 100, as last reported for this job. It never goes backwards. Draw exactly this number: nothing here is interpolated and a client that animates between readings is inventing progress.
stagestringA short label for the current step. Show it; do not branch on it.
requestIdstring | null
resultobject | null
7 fields inside result
NameTypeDescription
artifactsobject[]
bytesnumber | null
expiresAtstring | nullformat: date-time
sha256string | null
typestring
urlstring | null
durationSecondsnumber | null
stagestring | null
startedAtstring | nullformat: date-time
statestringThe five public states. Poll until one of succeeded/failed/cancelled.one of: queued, running, succeeded, failed, cancelled
200 OK headers
HeaderMeaning
Cache-ControlAlways private, no-store.
Idempotent-ReplayedPresent only when this answer was stored by an earlier call with the same Idempotency-Key. Nothing new was done.

202 Accepted

The work was queued. Poll the resource at Location. A retry with the same Idempotency-Key answers this again, with the same body and the same Location, plus Idempotent-Replayed: true.

202 Accepted body
NameTypeDescription
createdboolean
credentialSourcestring | null
lengthFitobject | nullWHAT THIS RENDER DID ABOUT THE LENGTH. The storyboard's own lengthFit is about the PLAN; this one is about the video just queued, and the two differ exactly when the chosen length cannot hold every beat the plan proved. Null when the storyboard names no length at all, and null on a replayed request, which measured nothing.
11 fields inside lengthFit
NameTypeDescription
adjustedbooleanWhether this video is shorter than the storyboard's plan. Render at a longer length, or hide beats yourself, to choose what goes.
beatBudgetnumber | nullThe steps that length can hold.
beatsDroppednumberSteps this render leaves out because the length cannot hold them. Taught extras yield their slot first; only then is the walkthrough itself cut, and always off the end.
beatsKeptnumberSteps the video will actually show.
beatsPlannednumberSteps the storyboard offered this render, after your own hidden beats were taken out and any taught steps put back.
cappedFromSecondsnumber | nullThe length asked for, when a shorter one was used instead. Null on this operation.
maxSecondsnumber | nullThe ceiling an Auto render is sized against, or null when a fixed length was chosen.
notestring | nullThe sentence to show, e.g. "Plan adjusted to fit 60 seconds: 10 of 13 steps kept." Null when nothing was adjusted.
plannedSecondsnumber | nullWhat the kept steps are worth in seconds — what the video will run to, before the read is measured.
secondsnumber | nullThe length everything below was decided from: the fixed length, or the Auto ceiling.
targetSecondsnumber | nullThe fixed length this render holds, or null when the storyboard was planned as Auto.
resourceobject
35 fields inside resource
NameTypeDescription
artifactsExpiredbooleanThe work succeeded but its files were deleted at the end of the retention period.
cancellableboolean
costobject
secondsChargednumber | null
secondsReservednumber
watermarkboolean
createdAtstring | nullformat: date-time
errorobject | null
codestring
reasonstring | null
retryableboolean
terminalboolean
expiresAtstring | nullformat: date-time
finishedAtstring | nullformat: date-time
idstring
idempotencyKeystring | null
internalStatusstringA finer-grained status word. Informational; branch on state.
kindstringwalkthrough.render | walkthrough.storyboard | marketing.export | marketing.generation | marketing.source_scan | marketing.source_operation
pollAfterMsnumber | nullWait this long before polling again; null once the state is terminal.
progressobject | nullHOW FAR THE WORK HAS GOT, as last reported, or null — null while nothing has been recorded yet and null for every terminal state, because a finished job is 100 by definition and a failed one has no bar to draw.
atstring | nullWhen it was reported. A reading that has stopped moving means the work has, not that the bar is broken.
percentnumber0 to 100, as last reported for this job. It never goes backwards. Draw exactly this number: nothing here is interpolated and a client that animates between readings is inventing progress.
stagestringA short label for the current step. Show it; do not branch on it.
requestIdstring | null
resultobject | null
7 fields inside result
NameTypeDescription
artifactsobject[]
bytesnumber | null
expiresAtstring | nullformat: date-time
sha256string | null
typestring
urlstring | null
durationSecondsnumber | null
stagestring | null
startedAtstring | nullformat: date-time
statestringThe five public states. Poll until one of succeeded/failed/cancelled.one of: queued, running, succeeded, failed, cancelled
202 Accepted headers
HeaderMeaning
Cache-ControlAlways private, no-store.
Idempotent-ReplayedPresent only when this answer was stored by an earlier call with the same Idempotency-Key. Nothing new was done.
LocationWhere to read this walkthrough.render, e.g. /api/v1/renders/{id}. Present on the replay too.

Errors

Every refusal is { error: { code, message, details?, requestId } }. Branch on code, never on the sentence. The full catalogue is at Errors.

CodeStatusWhen it happensWhat to do
avatar_unavailable400A presenter circle was asked for on a deployment where the presenter is switched off.Make the video without one. While the presenter is off, avatarId is not declared on the public schemas at all, so a public caller meets validation_failed first.
avatar_unknown400The avatarId named is not in the catalogue.Read GET /catalog/render-options and send an id from avatars.
background_unknown400The background named is not in the catalogue.Read GET /catalog/render-options and send an id from backgrounds, or omit the field for the default.
body_invalid400The per beat edits sent to renders.createFromStoryboard are not a valid shape: narration that is not text, a beat entry that is not an object.Send beats as a list of objects, each naming a beat id from the storyboard, with an optional rewritten narration or a hide flag.
caption_style_unknown400The captionStyle named is not in the catalogue.Read GET /catalog/render-options and send an id from captionStyles.
capture_incompatible400A phone view capture was paired with a landscape orientation. Phone view only makes a portrait video.Pick a portrait orientation, or switch the capture to a computer window.
capture_unknown400The capture named is not in the catalogue.Read GET /catalog/render-options and send an id from captures.
credentials_required400The storyboard was planned signed in, so the render needs the same demo credentials again. The originals were deleted at settlement and are never kept.Send the credentials block, including loginUrl, on the render request.
hint_required400The hint is missing, or it is empty once trimmed. The schema requires 3 to 1000 characters and the service refuses a blank one before a browser is started.Say what to show in a sentence, for example "show the invoice creation flow". Nothing checks that a hint is specific, so a thin one is accepted and produces a poor video; storyboards.improveHint rewrites one before you spend anything.
hint_too_long400The hint is longer than 1000 characters.Shorten it. storyboards.improveHint rewrites a long brief into the form that plans most reliably, within the same bound.
idempotency_key_required400A mutating operation arrived over HTTP with no Idempotency-Key header. It is refused before the body is parsed, so a caller missing both learns about both at once.Add an Idempotency-Key header of up to 128 characters, unique to this request. Over MCP the same value goes in the idempotencyKey tool argument.
idempotency_key_unscoped400An idempotency key arrived on a request with no account to scope it to.Call with an API key or a signed in session. An unscoped key would deduplicate nothing.
language_unknown400The language named is not in the catalogue.Read GET /catalog/render-options and send a code from languages.
length_below_minimum400The length asked for is shorter than a walkthrough can be. The floor is 30 seconds.Ask for at least 30 seconds, or omit targetSeconds for Auto, which sizes itself.
login_origins_invalid400The list of additional sign-in addresses sent with the demo credentials is not valid: more than five entries, an entry that is not https, or an address this API will not fetch.Send only the https origins the sign-in flow really passes through, each a public address, and retry.
login_url_invalid400The login page address is not one this API will fetch: a private address, a name that does not resolve, or an address that is not https. The sign-in page must be https because the demo account's password is typed there.Send a public https address for the page the demo account signs in on.
login_url_required400A credentials block was sent without loginUrl. Without it the run can only infer where the app is, and a wrong inference produces a video of the wrong thing.Add credentials.loginUrl, the address of the page those credentials sign in on. The credentials block itself stays optional.
no_beats400Every beat on the storyboard was hidden, so there would be nothing to record.Keep at least one beat visible.
options_invalid400The render options do not resolve together, for a reason the per field codes do not cover.Re-read GET /catalog/render-options and send a combination it offers. renders.quote prices options without creating anything.
orientation_unknown400The orientation named is not in the catalogue.Read GET /catalog/render-options and send an id from orientations.
site_not_allowed400The website is a major public platform (a social network, search engine, marketplace or big portal) or a site about a sensitive topic (adult content, gambling, drugs, weapons, hate or violence). details.reason says which: public_platform or sensitive_topic. A sign-in address on a sensitive-topic site is refused too, and so is a sign-in that lands on a refused site.Use your own product's website. No time was used. If you think your site was refused by mistake, write to support@gogoscreen.com or open a ticket with "Talk to a person": support can allow a site by name.
storyboard_empty400The storyboard has no beats, so there is nothing to record.Plan again with a more specific hint. Planning is free.
template_unknown400The templateId named is neither a system template nor one of this account's saved templates.Read GET /catalog/render-options and send an id it lists, or a saved template id from GET /templates.
unknown_beat400One of the beat ids named in the render request is not on that storyboard.Read the storyboard and name only beats it carries.
url_invalid400The address is not one this API will fetch, for a reason the more specific codes do not cover.Send an absolute public http or https URL.
url_malformed400The address could not be parsed as a URL.Send an absolute URL including the scheme.
url_private400The address resolves to a private, loopback, link local, carrier grade NAT or IPv4 mapped IPv6 address. This API will not fetch inside a network.Point at a publicly reachable address. There is no way to opt out of this guard.
validation_failed400The merged path, query and body did not match the operation's schema. The schemas are strict, so an unknown field is a failure rather than something ignored, and a string carrying half of a character is refused before it can be stored.Read details.issues. Each entry carries a path, a message and a machine readable code. Fix every named field and send the request again; the same body always fails the same way.
voice_language400The voice chosen does not speak the language chosen.Read GET /catalog/voices and pick a voice listed for that language, or change the language.
voice_unknown400The voiceId named is not in the catalogue.Read GET /catalog/voices and send an id it lists.
unauthorized401No credential was presented, or the key is unknown, revoked or expired, or an X-API-Key header carried something that is not an API key. details.reason says which.Send Authorization: Bearer gsk_live_…. A revoked or expired key never starts working again, so issue a new one in the developer console. Put a dashboard session token in Authorization, never in X-API-Key.
insufficient_seconds402The account's seconds wallet does not hold enough time for the video that was asked for. It is the only refusal on this surface that is a 402.details.needed and details.available are the two numbers. Buy more time, or ask for a shorter video. Retrying without changing either fails the same way.
forbidden_scope403The credential does not carry every scope the operation declares, or it is the wrong kind of credential for it. keys.* and assistant.* are user only and can never be called by a key.details.requiredScopes and details.missingScopes name what is missing. A key's scopes cannot be changed after it is issued, so create a new key with them. When details.allowedActors is present, no key can call this operation at all.
not_found404No resource of that id belongs to this account. A resource that belongs to somebody else answers 404 as well, never 403.Check the id. Do not treat this as a permissions problem.
conflict409The request conflicts with the state the resource is in, and no more specific code applies.Re-read the resource and decide from its current state. Retrying the same request unchanged will not help.
idempotency_conflict409That idempotency key has already been used on this operation with a different request body.Use a fresh key for a different request. A key stands for one intent, not for one attempt.
idempotency_in_progress409The first request under that key is still being worked. Two concurrent identical requests both reach the store and one of them is told this.Wait the Retry-After seconds, which is 2, and send exactly the same request again with the same key. Do not change the body and do not mint a new key.
not_ready409A download was asked for on a render that has not finished.Poll renders.get until state is succeeded, then download.
too_many_in_flight409The account already has as many renders or exports running as it may. The default ceiling is three of each.details.limit and details.inFlight carry the numbers. Wait for one to settle, or cancel one, then retry.
storyboard_expired410The storyboard is past its retention window and its screenshots have been swept.Plan it again. Planning reserves no video time.
payload_too_large413The request body is over the transport's limit: 256 kb over HTTP, 1 mb over MCP.Send less. details.limit carries the limit. Version 1 has no upload operations, so a body this size is usually a mistake.
unsupported_media_type415The request carried a body whose Content-Type is not application/json, whose charset is not utf-8, or whose Content-Encoding this API does not decode. A body nobody can read would otherwise be silently discarded.Send Content-Type: application/json and utf-8 bytes. details names the charset, encoding or content type that was objected to.
rate_limited429The credential's per minute budget, the account's per minute ceiling, or a daily cap was exceeded. details.scope is minute, day or account, or global when the product's own daily cap on voice previews is spent.Wait the Retry-After seconds and retry. details.limit and details.resetAt say what was hit and when it reopens. The RateLimit-* headers on every answer let a client pace itself before it gets here.
internal_error500An unhandled failure inside this API. The original message is logged and never answered.Retry once, then quote requestId to support. A 5xx deletes the idempotency claim rather than storing it, so retrying under the same key is safe.
queue_unavailable503The work could not be started because a service it needs was briefly unavailable.Back off and retry. Nothing was created and nothing was charged.
vault_unavailable503The demo credentials could not be stored securely, so the run was refused rather than started without them.Back off and retry. Nothing was created and nothing was charged.
wallet_unavailable503The seconds wallet could not be reached to hold the time, so nothing was started.Back off and retry. No time was reserved, so nothing is stranded.