Skip to content

marketing.exports.create

Cut the finished video

POST/marketing/projects/{id}/exports

Operation
marketing.exports.create
MCP tool
marketing_exports_create

Render one revision into a finished file, with cover, captions and transcript. THIS IS THE MARKETING OPERATION THAT SPENDS: time is reserved at the DELIVERED length (authored ÷ the revision's speed) and settled when the file is made; a cancelled or failed export is charged nothing. expectedRevisionId must be the current revision, or 409 revision_conflict with nothing reserved. Ask marketing.exports.quote first. Answers 202; at most three exports in flight.

curl -X POST "https://api.gogoscreen.com/api/v1/marketing/projects/<id>/exports" \
  -H "Authorization: Bearer $GOGOSCREEN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{ "durationSeconds": 7, "expectedRevisionId": "<expectedRevisionId>", "format": "<format>" }'
A request to this operation.

At a glance

FactDetail
Scopesmarketing:export
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 marketing.export. Poll /marketing/exports/{id} until state is terminal, or subscribe to a webhook. This kind can be cancelled while it runs. Jobs and polling.
MCP toolmarketing_exports_create
Always setsCache-Control: private, no-store
Verified emailRequired. An account whose email address is not verified meets 403 email_unverified before the operation runs.
Marketing videosRequired. An account without marketing videos enabled meets 403 feature_disabled before the operation runs.

Path parameters

NameTypeRequiredDescription
idstringRequiredThe project to export.format: uuid

Request body

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

NameTypeRequiredDescription
durationSecondsintegerRequiredOne the design offers at this format, and the one the named revision was saved with. The six-second edition is retired and is refused here.7–600
expectedRevisionIdstringRequiredThe revision you are exporting. It must still be the project's current one.format: uuid
formatstringRequiredOne the design offers: landscape | portrait | square | four-five.1–32 characters
audioModestringOptionalDefaults to music.one of: music, silent, voice, voice-music
coverTextstring | nullOptionalWords for the cover image, for THIS export only. Blank means none; it is never written back to the revision.max 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
exportobject
22 fields inside export
NameTypeDescription
audioModestring
durationSecondsnumber
failureCodestring | null
formatstring
hasCaptionsboolean
hasCoverboolean
hasTranscriptboolean
idstring
progressobject | nullThe last progress reading while the export runs: the current step and a whole percent. null once it has settled, and null when no reading was recorded.
atstring | null
percentinteger0–100
stagestring
projectIdstring
qaobject | nullThe outcome of the automated checks, once they have run; null before. Why an export failed is failureCode.
notesstring[]What the export did that a person may want to know, as codes: font_missing, cover_drawn, cover_not_drawn, narration_fitted, transcript_written, transcript_not_written. A note is not a failure.
okbooleanWhether the automated checks on the finished file passed.
revisionIdstring
secondsChargednumber | null
secondsReservednumber
statestringone of: queued, running, succeeded, failed, cancelled
statusstring
watermarkstring
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
exportobject
22 fields inside export
NameTypeDescription
audioModestring
durationSecondsnumber
failureCodestring | null
formatstring
hasCaptionsboolean
hasCoverboolean
hasTranscriptboolean
idstring
progressobject | nullThe last progress reading while the export runs: the current step and a whole percent. null once it has settled, and null when no reading was recorded.
atstring | null
percentinteger0–100
stagestring
projectIdstring
qaobject | nullThe outcome of the automated checks, once they have run; null before. Why an export failed is failureCode.
notesstring[]What the export did that a person may want to know, as codes: font_missing, cover_drawn, cover_not_drawn, narration_fitted, transcript_written, transcript_not_written. A note is not a failure.
okbooleanWhether the automated checks on the finished file passed.
revisionIdstring
secondsChargednumber | null
secondsReservednumber
statestringone of: queued, running, succeeded, failed, cancelled
statusstring
watermarkstring
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 marketing.export, e.g. /api/v1/marketing/exports/{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
expected_revision_required400A save arrived without expectedRevisionId, so it could have landed on words the caller never saw.Read the project, send its current revision id as expectedRevisionId, and handle revision_conflict when somebody else saved first.
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.
invalid_cover_text400The cover words are not valid for this design at this shape, usually because they do not fit.Shorten the cover text, or choose a format with more room.
unknown_template400The marketing design named does not exist.Read GET /catalog/marketing/templates and send an id it lists.
unsupported_audio_mode400The design does not offer that audio mode.details.audioModes lists what it does offer. Pick one of those.
unsupported_duration400The design does not offer that length.details.durations lists what it does offer. Pick one of those.
unsupported_format400The design does not offer that shape.details.formats lists what it does offer. Pick one of those.
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.
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.
already_settled409A cancel arrived for an export that had already succeeded, failed or been cancelled. The settlement is one conditional update, so a cancel racing a finish never moves the money twice.Read the resource and take its current state as the answer. Nothing changed.
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.
no_revision409An export was asked for on a project that has no revision yet.Create a revision first, or wait for the generation that is making one to finish.
revision_configuration_mismatch409The export asks for a shape, length or audio mode that is not the revision's own.Export the revision as it stands, or save a new revision at the configuration you want and export that.
revision_conflict409expectedRevisionId is no longer the project's current revision, so somebody else saved first. Nothing was written.details.currentRevisionId is the revision that won. Re-read the project, rebase the change on it, and save again.
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.
source_expired410The website source is past its retention window.Scan the site again. Projects already built from it keep the version they were built from.
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.
campaign_speed_breaks_music_ending422At the revision's saved playback speed the music would no longer end with the film.details.missing says what to put right. Save a new revision at a speed the music bed can end on, then export that.
campaign_speed_breaks_narration_fit422At the revision's saved playback speed a spoken line would no longer fit the shot it belongs to.details.missing says what to put right. Shorten the line or save the revision at a slower speed, then export that.
campaign_speed_not_offered422The revision is saved at a playback speed this build does not deliver, and the export judges the saved speed again before it reserves anything.details.speed is the saved speed and details.missing names the one way out, which is saving the revision at a speed the build offers. Save a new revision, then export that.
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.
reservation_failed503The seconds wallet could not hold the time for an export.Back off and retry. Nothing was reserved, so no time is stranded.