Skip to content

SC APIReference · v1

Videos from your code.

Create on-brand videos, patch their fields, render every format and get told when they are ready. JSON over HTTPS, one API key per integration, available on the Agency plan.

Basics

Authentication

Send Authorization: Bearer ls_live_…. Keys belong to one workspace; create and revoke them in Settings → API keys. We store only a hash, so a lost key can't be shown again.

Rate limits

120 requests a minute per key. Every answer carries RateLimit-Limit, -Remaining and -Reset; a 429 adds Retry-After.

Idempotency

Add Idempotency-Key: <uuid> to any POST. Retries with the same key and body get the first answer back for 24 hours; the same key with a different body is a 422.

Errors

Every error has the same shape: {"error":{"code","message","details","request_id"}}. Messages say what to change; quote the request id to support.
Quick start
curl https://video-saas-theta.vercel.app/api/v1/videos \
  -H "Authorization: Bearer $LOUDSCENE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"url":"https://yourbrand.com","brief":"We just launched v2: twice as fast.","format":"9:16"}'

Webhooks

Add an https endpoint in Settings → Webhooks and pick events: video.generated, render.progress (start and every 25 %), render.completed (with the MP4 URL), render.failed and comment.created. Answer 2xx within 8 seconds. Failures are retried after 30 s, 2 min, 10 min, 30 min, 1 h, 3 h and 6 h; a 410 stops retries. The event id stays the same across retries, so deduplicate on it.

Headers
X-Loudscene-Event: render.completed
X-Loudscene-Delivery: 5b0c…
X-Loudscene-Timestamp: 1790000000
X-Loudscene-Signature: t=1790000000,v1=5f2a…
Verify (Node)
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(secret, rawBody, header) {
  const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const want = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  const got = Buffer.from(v1, "hex");
  return got.length === want.length && timingSafeEqual(got, want);
}

Brand

POST/v1/brand/extractIdempotency-Key supported

Extract a brand from a website

Starts a brand_extract job. Poll GET /v1/jobs/{id}; when it succeeds the new brand kit appears in GET /v1/brand-kits.

Body

FieldTypeDescription
urlrequiredstringWebsite address, e.g. https://example.com (https is added when missing)
Request
curl -X POST https://video-saas-theta.vercel.app/api/v1/brand/extract \
  -H "Authorization: Bearer $LOUDSCENE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://yourbrand.com"}'

Response · 202 The extraction job

FieldTypeDescription
idrequiredstring (uuid)
kindrequiredstring
statusrequired"queued" | "running" | "succeeded" | "failed" | "canceled"
stagerequiredstring | null
progressrequirednumber
errorrequiredstring | null
resultrequiredobject | nullKind-specific result (stills: signed PNG URLs).
created_atrequiredstring (date-time)
finished_atrequiredstring (date-time) | null
GET/v1/brand-kits

List brand kits

Every brand kit of the key's workspace, default first.

Request
curl -X GET https://video-saas-theta.vercel.app/api/v1/brand-kits \
  -H "Authorization: Bearer $LOUDSCENE_API_KEY"

Response · 200 Brand kits

FieldTypeDescription
datarequiredBrandKit[]

Stories

GET/v1/stories

List stories

Built-in stories (templates) with their editable fields, usable as storyId in POST /v1/videos.

Request
curl -X GET https://video-saas-theta.vercel.app/api/v1/stories \
  -H "Authorization: Bearer $LOUDSCENE_API_KEY"

Response · 200 Stories

FieldTypeDescription
datarequiredobject[]

Videos

POST/v1/videosIdempotency-Key supported

Create a video

With storyId + fields the video is built instantly from the story (no AI, status ready). Otherwise a generate job writes it from the brief (status generating; subscribe to the video.generated webhook or poll GET /v1/videos/{id}).

Body

FieldTypeDescription
titlestring
briefstringWhat the video should say. Moderated before generation.
storyIdstringA story id from GET /v1/stories. With `fields`, the video is built instantly without AI.
fieldsobjectField values for the story (requires storyId). Unknown keys are rejected.
brandKitIdstring (uuid)Brand kit to use. Defaults to the workspace's default kit when neither this nor url is given.
urlstringExtract the brand from this site first (when no brand kit is given).
format"16:9" | "9:16" | "1:1" | "4:5"Main format. Default 16:9.
durationSecnumber6 to 90
localestring
Request
curl -X POST https://video-saas-theta.vercel.app/api/v1/videos \
  -H "Authorization: Bearer $LOUDSCENE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"storyId":"product-launch","brandKitId":"8d3f…","fields":{"headline":"Launch day"},"format":"9:16"}'

Response · 201 The new video

FieldTypeDescription
idrequiredstring (uuid)
titlerequiredstring
statusrequired"draft" | "generating" | "ready" | "failed" | "archived"
story_idrequiredstring | null
brand_kit_idrequiredstring (uuid) | null
default_formatrequired"16:9" | "9:16" | "1:1" | "4:5"Aspect ratio
duration_secrequirednumber | null
current_versionrequiredobject | null
jobrequiredobject | nullThe generation job, while one is running
created_atrequiredstring (date-time)
updated_atrequiredstring (date-time)
GET/v1/videos/:id

Get a video

The video and its current version (the full VideoDoc).

Request
curl -X GET https://video-saas-theta.vercel.app/api/v1/videos/<id> \
  -H "Authorization: Bearer $LOUDSCENE_API_KEY"

Response · 200 The video

FieldTypeDescription
idrequiredstring (uuid)
titlerequiredstring
statusrequired"draft" | "generating" | "ready" | "failed" | "archived"
story_idrequiredstring | null
brand_kit_idrequiredstring (uuid) | null
default_formatrequired"16:9" | "9:16" | "1:1" | "4:5"Aspect ratio
duration_secrequirednumber | null
current_versionrequiredobject | null
jobrequiredobject | nullThe generation job, while one is running
created_atrequiredstring (date-time)
updated_atrequiredstring (date-time)
PATCH/v1/videos/:id

Update a video

Send a complete `doc` (linted server-side) or a `fields` patch; either way the result is saved as a new immutable version. `title` renames the video.

Body

FieldTypeDescription
docVideoDocA complete VideoDoc. Its code is linted server-side; the result becomes a new version.
fieldsobjectField values to change in the current version.
titlestring
notestringShown in the version history.
Request
curl -X PATCH https://video-saas-theta.vercel.app/api/v1/videos/<id> \
  -H "Authorization: Bearer $LOUDSCENE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"fields":{"headline":"Now in 12 countries"},"note":"Weekly refresh"}'

Response · 200 The updated video

FieldTypeDescription
idrequiredstring (uuid)
titlerequiredstring
statusrequired"draft" | "generating" | "ready" | "failed" | "archived"
story_idrequiredstring | null
brand_kit_idrequiredstring (uuid) | null
default_formatrequired"16:9" | "9:16" | "1:1" | "4:5"Aspect ratio
duration_secrequirednumber | null
current_versionrequiredobject | null
jobrequiredobject | nullThe generation job, while one is running
created_atrequiredstring (date-time)
updated_atrequiredstring (date-time)
POST/v1/videos/:id/stillsIdempotency-Key supported

Render still frames

Starts a stills job for the current version. When the job succeeds, GET /v1/jobs/{id} returns signed PNG URLs (valid 10 minutes).

Body

FieldTypeDescription
timesrequirednumber[]Seconds into the video
format"16:9" | "9:16" | "1:1" | "4:5"Aspect ratio
Request
curl -X POST https://video-saas-theta.vercel.app/api/v1/videos/<id>/stills \
  -H "Authorization: Bearer $LOUDSCENE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"times":[1,4.5,9],"format":"1:1"}'

Response · 202 The stills job

FieldTypeDescription
idrequiredstring (uuid)
kindrequiredstring
statusrequired"queued" | "running" | "succeeded" | "failed" | "canceled"
stagerequiredstring | null
progressrequirednumber
errorrequiredstring | null
resultrequiredobject | nullKind-specific result (stills: signed PNG URLs).
created_atrequiredstring (date-time)
finished_atrequiredstring (date-time) | null

Renders

POST/v1/videos/:id/rendersIdempotency-Key supported

Render an MP4

Renders the current version in one format. One credit covers every format of a version up to 60 s. An identical finished render is returned instead of rendering again.

Body

FieldTypeDescription
formatrequired"16:9" | "9:16" | "1:1" | "4:5"Aspect ratio
resolution720 | 1080 | 2160Default 1080
fps24 | 30 | 60Default: the video's fps
Request
curl -X POST https://video-saas-theta.vercel.app/api/v1/videos/<id>/renders \
  -H "Authorization: Bearer $LOUDSCENE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"format":"16:9","resolution":1080,"fps":30}'

Response · 202 The render

FieldTypeDescription
idrequiredstring (uuid)
video_idrequiredstring (uuid)
version_idrequiredstring (uuid)
formatrequired"16:9" | "9:16" | "1:1" | "4:5"Aspect ratio
resolutionrequiredinteger-9007199254740991 to 9007199254740991
fpsrequiredinteger-9007199254740991 to 9007199254740991
statusrequired"queued" | "running" | "succeeded" | "failed" | "canceled"
progressrequirednumber
urlrequiredstring | nullMP4 URL once succeeded
poster_urlrequiredstring | null
duration_secrequirednumber | null
bytesrequiredinteger | null
errorrequiredstring | null
created_atrequiredstring (date-time)
finished_atrequiredstring (date-time) | null
GET/v1/renders/:id

Get a render

Status, progress and, once succeeded, the MP4 and poster URLs.

Request
curl -X GET https://video-saas-theta.vercel.app/api/v1/renders/<id> \
  -H "Authorization: Bearer $LOUDSCENE_API_KEY"

Response · 200 The render

FieldTypeDescription
idrequiredstring (uuid)
video_idrequiredstring (uuid)
version_idrequiredstring (uuid)
formatrequired"16:9" | "9:16" | "1:1" | "4:5"Aspect ratio
resolutionrequiredinteger-9007199254740991 to 9007199254740991
fpsrequiredinteger-9007199254740991 to 9007199254740991
statusrequired"queued" | "running" | "succeeded" | "failed" | "canceled"
progressrequirednumber
urlrequiredstring | nullMP4 URL once succeeded
poster_urlrequiredstring | null
duration_secrequirednumber | null
bytesrequiredinteger | null
errorrequiredstring | null
created_atrequiredstring (date-time)
finished_atrequiredstring (date-time) | null
POST/v1/videos/:id/variantsIdempotency-Key supported

Render variants

Renders several formats at once, and optionally starts translated copies of the video (one new video per extra locale, each with its own translation job).

Body

FieldTypeDescription
formatsrequired"16:9" | "9:16" | "1:1" | "4:5"[]
localesstring[]Extra languages: each becomes a translated copy of the video (Pro and Agency).
resolution720 | 1080 | 2160Short side in pixels (2160 = 4K)
Request
curl -X POST https://video-saas-theta.vercel.app/api/v1/videos/<id>/variants \
  -H "Authorization: Bearer $LOUDSCENE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"formats":["16:9","9:16","1:1","4:5"],"locales":["de","fr"]}'

Response · 202 Renders and translated copies

FieldTypeDescription
rendersrequiredRender[]
translationsrequiredVideo[]

Jobs

GET/v1/jobs/:id

Get a job

Progress and result of an asynchronous job (brand extraction, stills, generation).

Request
curl -X GET https://video-saas-theta.vercel.app/api/v1/jobs/<id> \
  -H "Authorization: Bearer $LOUDSCENE_API_KEY"

Response · 200 The job

FieldTypeDescription
idrequiredstring (uuid)
kindrequiredstring
statusrequired"queued" | "running" | "succeeded" | "failed" | "canceled"
stagerequiredstring | null
progressrequirednumber
errorrequiredstring | null
resultrequiredobject | nullKind-specific result (stills: signed PNG URLs).
created_atrequiredstring (date-time)
finished_atrequiredstring (date-time) | null