Skip to content

Ask AI

Ask anything about Destesi — setup, products, APIs.

AI answers may be wrong — always verify against the docs.

Studio reference

This page documents Studio’s real surface: the HTTP endpoints the web app calls, the run lifecycle, and the environment that configures the service.

The public API is session-authenticated through suite single sign-on. Browser requests carry the Studio session cookie established at GET /v1/sso/callback; the server validates it against identity and scopes every query to your workspace.

Endpoint Purpose
GET /v1/sso/callback Redeems the SSO grant and sets the Studio session.
GET /v1/auth/me Returns the signed-in account (id, email, name).
POST /v1/auth/logout Clears the Studio session.

Service-to-service callbacks live under /v1/controller/* and are gated by a shared controller secret — when that secret is unset, those routes return 404 and are invisible. They are used by Studio’s worker, not by you.

Endpoint Purpose
GET /healthz Cheap liveness check. Returns {"status":"ok"}.
GET /readyz Dependency check (database + queue). Returns 200 when healthy, 503 (status: degraded) with per-check detail when not.
Endpoint Purpose
GET /v1/setup/readiness What still needs connecting before Studio is fully usable in this workspace.
GET /v1/features Which optional capabilities this deployment has enabled.
GET /v1/blocks Browse-only catalog of text effects, transitions, VFX, stickers, and audio-reactive blocks.
GET /v1/worker/status Whether the build queue is being drained — what the worker banner reads.

Everything above is readable by any member. Changing the workspace brand (look, fonts, logo, voice, house format) and linking or unlinking the accounts Studio publishes with are workspace configuration and need the role owner or admin (admin_required). Creating and editing a template is a member’s work. See Roles and permissions.

Method & path Purpose
POST /v1/projects Create a project from a prompt. Creating it also enqueues the first build.
GET /v1/projects List your workspace’s projects.
GET /v1/projects/{id} Fetch one project.
GET /v1/projects/{id}/current Fetch the project’s current version — the project-first read the editor opens with.
PATCH /v1/projects/{id} Update a project.
DELETE /v1/projects/{id} Delete a project.
GET /v1/projects/{id}/variants List a project’s language variants.
POST /v1/projects/{id}/variants Create a variant.

One brand per workspace, used by every project and frozen onto each new run. Reading is open to members; every write is owner or admin (admin_required).

Method & path Purpose
GET /v1/brand The brand: configured, theme or colours, fonts, voice, format (with format_detail), and the logo (logo_drive_file_id, a short-lived logo_url, logo_position, logo_scale). configured: false means the workspace never set one up.
PATCH /v1/brand A merging patch: a key you send is set, a key you omit is left alone, null clears. Keys: theme, colours, fonts, voice, format, logo_position, logo_scale. A starter theme clears custom colours and vice versa; both in one patch is 422 brand_kit_invalid. Colours below the contrast bars are 422 brand_kit_illegible, naming the pair and ratio. {} records a configured brand with the stock look.
POST / DELETE /v1/brand/logo Upload (multipart file, PNG/JPG/WebP/GIF up to 5 MB) or clear the watermark.
Method & path Purpose
POST / GET /v1/templates Create or list templates. Each row carries its kind (visual or images) and project_count, the live number of projects using it; an images template also carries image_count and, when a picture is pinned, a short-lived preview_url. format is the kind of film the template makes (a house format key such as devotional); a template that sets a format or a style_preset beats the project’s own format, which beats the brand’s house format. format, style_preset and caption_style may be "" — follow the brand’s house format. caption_style is subtle, bold, pop, typewriter, typewriter-elegant or typewriter-large. verse_card is the card a typewriter caption quotes a verse on: light, glass, overlay, quote or lower; "" follows the format (devotional: glass).
GET / PATCH / DELETE /v1/templates/{id} Fetch, update, or delete one. A template is a live preset and carries no brand: each new run reads it as it is then and freezes what it used, so an edit reaches every project on it from its next run. Deleting it leaves those projects with no template from their next run.
POST /v1/templates/{id}/duplicate Copy a template as a starting point.
POST / DELETE /v1/templates/{id}/background Upload or clear the template’s background (PNG, JPG, WebP or GIF, up to 5 MB). An images template answers 422 wrapper_not_supported here, and for intro/outro images and music.
GET / POST /v1/templates/{id}/rules List or add rules that decide when parts of the template apply. A rule created without is_enabled is enabled.
PATCH / DELETE /v1/templates/{id}/rules/{ruleId} Update or remove a rule.
POST / GET /v1/assets Upload to, or list, the workspace asset library (images and audio, including music tracks).
GET / DELETE /v1/assets/{id} Fetch or delete one asset.
Method & path Purpose
POST /v1/projects/{id}/runs Start a run. Accepts an optional language_codes array to fan out one run per language. Answers 422 conflicting_source_selectors when the project’s template sets a scene’s picture both in its pictures and in a rule.
GET /v1/projects/{id}/runs List a project’s runs.
GET /v1/projects/{id}/runs/{run_id} Fetch one run.
POST /v1/projects/{id}/runs/{run_id}/cancel Cancel an in-flight run.
POST /v1/projects/{id}/runs/{run_id}/retry Re-queue a failed or cancelled run.
POST /v1/projects/{id}/runs/{run_id}/approve Approve a run — registers the video + thumbnail in Drive.
GET /v1/runs/{run_id}/production Fetch the editable production document (scenes, image and audio URLs, render link, stale flag).
POST /v1/runs/{run_id}/duplicate Clone a run into a different aspect ratio (reuses images, re-renders).
PATCH /v1/runs/{run_id} Update a run’s label.
DELETE /v1/runs/{run_id} Delete a run and its scenes, renders, and publishes.
GET /v1/projects/{id}/artifacts/{run_id}/{step_index} Signed link to one pipeline step’s artifact.

Changing a finished video’s shape is a three-call flow, because a re-render is not free and you should see what it will cost before it starts.

Method & path Purpose
POST /v1/runs/{run_id}/aspect-change/preflight What changing the aspect would do — the check the web app shows before you confirm.
POST /v1/runs/{run_id}/aspect-change Start the change.
POST /v1/runs/{run_id}/aspect-change/{op_id}/retry-preview Retry the preview render for an in-flight change.

POST /v1/projects/{id}/runs with a language_codes array creates a variant per code (the first becomes primary) and enqueues a run per variant in parallel. The response carries the created ids:

{
"run_ids": ["…", "…"],
"variant_ids": ["…", "…"],
"language_codes": ["en", "es"]
}

Omit language_codes and the single-run shape ({ "run_id": "…" }) is returned.

A run advances through the pipeline and ends awaiting your approval:

queued → running → awaiting_approval → approved

A run can also reach failed or cancelled; both can be retried, which resets the run to queued and re-queues it.

Once a run reaches awaiting_approval, its scenes are editable. The web app’s chat-driven editor calls these; each tool endpoint records an asynchronous operation you can poll.

Method & path Purpose
POST /v1/runs/{run_id}/tools/regen_scene_image Regenerate one scene’s image.
POST /v1/runs/{run_id}/tools/regen_all_images Regenerate every scene’s image.
POST /v1/runs/{run_id}/tools/regen_scene_narration Re-voice one scene.
POST /v1/runs/{run_id}/tools/regen_all_narration Re-voice every scene with a new voice.
POST /v1/runs/{run_id}/tools/swap_voice Swap the narration voice.
POST /v1/runs/{run_id}/tools/edit_script Edit the script.
POST /v1/runs/{run_id}/tools/render_preview Render a fresh preview.
POST /v1/runs/{run_id}/tools/bundle_render Render every aspect ratio at once as a bundle.
POST /v1/runs/{run_id}/tools/set_video_style_preset Set the video’s style preset.
POST /v1/runs/{run_id}/tools/set_video_vibe Apply a vibe bundle (style, transitions, effects) across the video.
POST /v1/runs/{run_id}/tools/set_scene_vibe Apply a vibe bundle to one scene.
GET /v1/tools/ops/{op_id} Poll an operation’s state.
POST /v1/runs/{run_id}/tools/ops/{op_id}/retry Retry a failed operation.

Studio also exposes finer-grained scene tools — text effects, transitions, VFX, stickers, audio-reactivity, and image-source policy (set_scene_image_source, set_video_image_source) — under the same /v1/runs/{run_id}/tools/* prefix, plus direct scene editing:

Method & path Purpose
PATCH /v1/runs/{run_id}/scenes/{scene_id} Edit a scene field directly (e.g. duration).
POST /v1/runs/{run_id}/scenes Insert a scene.
DELETE /v1/runs/{run_id}/scenes/{scene_id} Delete a scene.
POST /v1/runs/{run_id}/scenes/{scene_id}/duplicate Duplicate a scene.
POST /v1/runs/{run_id}/scenes/reorder Reorder scenes.
POST /v1/runs/{run_id}/scenes/{scene_id}/image Upload your own image for a scene.
POST /v1/runs/{run_id}/scenes/{scene_id}/image-from-url Use an image from a URL for a scene.
POST /v1/runs/{run_id}/scenes/transitions Set the transition on every scene at once.
POST /v1/runs/{run_id}/scenes/durations Set the duration on every scene at once.
POST /v1/runs/{run_id}/scenes/{scene_id}/lock / DELETE …/lock Lock a scene against regeneration.
POST /v1/runs/{run_id}/rollback Revert the scene changes from a prior operation.
Method & path Purpose
PUT /v1/runs/{run_id}/music/asset Attach a track from the asset library as the background music.
PATCH / DELETE /v1/runs/{run_id}/music Adjust the music volume against the narration, or clear the track.
POST / PATCH / DELETE /v1/runs/{run_id}/logo Set, reposition, or clear a logo watermark.
GET /v1/runs/{run_id}/renders List a run’s renders, newest first.
GET /v1/runs/{run_id}/captions.srt Export SubRip captions.
GET /v1/runs/{run_id}/transcript.json Export a structured transcript.
POST / DELETE /v1/runs/{run_id}/share Mint or clear a public share link.
GET /v1/share/{token} Public, token-authenticated view of a shared run.
POST /v1/runs/{run_id}/webhook/test Fire a test payload at the run’s configured webhook URL.

Studio proxies voice listing and previews so the web editor doesn’t need a separate Text-to-Speech session:

Method & path Purpose
GET /v1/voices List the voices available to the workspace.
GET /v1/voices/{voice_id}/preview Stream a voice preview sample.

When no voice is chosen, Studio resolves the workspace default (Aria).

Method & path Purpose
POST /v1/chat The chat home — an NDJSON stream.
POST /v1/chat/resume Resume a turn that paused for your approval.
GET /v1/conversations List saved conversations.
GET /v1/conversations/{id}/messages Page one conversation’s history.
PATCH / DELETE /v1/conversations/{id} Rename or delete a conversation.
GET / POST /v1/runs/{run_id}/messages Read or append the per-video editor chat.
POST /v1/runs/{run_id}/chat/production The production agent turn for a video.

Publishing to social channels is wired and workspace-scoped. It targets the social accounts you’ve connected in Connect. A video must be approved and have a render before it can be published.

Method & path Purpose
POST /v1/runs/{run_id}/publish Schedule or immediately publish to one or more connected social accounts.
GET /v1/runs/{run_id}/publishes List a video’s publish records and their state.
GET /v1/renders/{render_id}/publishes List publishes for one specific render.

Supported channels: YouTube, TikTok, Instagram, Facebook, Threads, LinkedIn. Automatic retries are capped at four attempts (backing off roughly 2, 10, and 30 minutes) and only apply to failures that occur before anything reaches the platform — once the upload has started, the attempt is terminal so a retry can never produce a second public post.

Method & path Purpose
POST /v1/source/github Turn a repo URL into a project and its first build. Body: { "repo_url": "https://github.com/owner/repo", "workspace_id": "…", "workspace_slug": "…" }, plus the optional language_codes, aspect_ratio, and voice fields the prompt path accepts.

The GitHub credential is resolved per request from Connect, scoped to workspace_slug. Public repositories work with no connection; to read private repositories, connect GitHub in Connect for the workspace. Error mapping: an unknown repo returns 404; a GitHub or credential-lookup failure returns 502; an unparseable URL returns 400.

Method & path Purpose
GET /v1/promos List saved promo compositions for the workspace.
GET /v1/promos/{id} Fetch a saved promo with fresh signed URLs.

Studio runs as two processes — an HTTP API and a background worker — plus a web front end. Video rendering happens on Studio’s own internal render backend; it is not a separate product and there is nothing for you to sign into or configure for it. The variables below configure the two Studio processes.

Variable Purpose Default
STUDIO_API_ADDR API listen address :9104
STUDIO_DATABASE_URL Postgres DSN (schema studio) unset → database-backed routes fail until set
STUDIO_REDIS_URL Job queue connection redis://localhost:6379/4
STUDIO_IDENTITY_API_URL Identity host for SSO http://localhost:9091
STUDIO_WEB_URL Post-SSO redirect target http://localhost:4332
STUDIO_WEB_URL_ALLOWLIST Allowed post-redeem redirect origins (comma-separated) unset
STUDIO_CORS_ALLOWED_ORIGINS Permitted request origins (comma-separated) unset
IDENTITY_CONTROLLER_SECRET Bearer for /v1/controller/* callbacks unset → controller routes return 404
TTS_API_URL Text-to-Speech host (voice proxy) http://localhost:9098
CHAT_API_URL Chat host http://localhost:9102
CONNECT_API_URL Connect host (per-workspace credentials) http://localhost:9105
GITHUB_API_URL GitHub REST base (override for GitHub Enterprise) https://api.github.com
DRIVE_API_URL Drive host (artifact registration) http://localhost:9106
SUITE_TOOL_SECRET Bearer shared with Drive unset → Drive registration disabled

The worker drains the job queue and runs the pipeline. In addition to the shared STUDIO_REDIS_URL, STUDIO_DATABASE_URL, and peer-service URLs above, it reads:

Variable Purpose
STUDIO_API_URL Callback base to the API
IMAGEGEN_API_URL Image-generation host
STUDIO_STOCK_ENABLED Kill switch for stock-photo lookup (default on)
STUDIO_CACHE_DIR Local cache directory
STUDIO_IMAGEGEN_POLL_TIMEOUT / STUDIO_RENDER_POLL_TIMEOUT / STUDIO_TTS_POLL_TIMEOUT / STUDIO_JOB_TIMEOUT Per-step and per-job timeouts

Studio’s schema lives in the studio Postgres schema. Apply migrations with the bundled command, which reads STUDIO_MIGRATE_DATABASE_URL (falling back to STUDIO_DATABASE_URL):

Terminal window
cd apps/studio/api && go run ./cmd/migrate up
Terminal window
# From the suite root — start the database, queue, and object store first:
make dev-db
# Then, in separate shells:
make dev-studio-api # HTTP API on :9104
make dev-studio-worker # pipeline worker
make dev-studio-web # web app on :4332
# Health check:
curl -s http://localhost:9104/healthz
# => {"status":"ok"}

The worker calls peer services (Chat, Text-to-Speech, Image generation). Start the ones you need with their own make dev-* targets.