Generating images, characters and video
Second Studio can make the material around your real footage: a recurring character, the places it lives, the props it holds, cinematic b-roll, and the voice it speaks with.
How generation works
Every request runs through a prompt director before it reaches a model. The director applies the
studio's production craft — face locks, character sheets, location plates, timed video beats and
performed voiceover, including what failed before — and turns your plain-language brief into a
finished prompt. The craft and the prompts it writes stay on our side; each result returns notes on
any choice made or part of the brief that could not be honoured.
You don't write prompts. Describe what you want; the skills do the rest. (If you do want to send your
own prompt verbatim, pass raw: true with prompt.)
Rules applied to every generation: people are fine, including photorealistic people. A specific real person's likeness is allowed only as a character built from photos you confirm you have permission to use; named celebrities and public figures are never rendered. No software interfaces or legible screen text (real product UI is recorded, not generated — see Recording), no third-party logos, nothing harmful. Requests the model provider's moderation rejects fail and are refunded.
Characters
A character is an identity the studio stores for you — a mascot, an invented character, or a real
person such as your founder: a name, a description, optional photos, and a generated reference sheet. Once it is ready, pass its id to any image or video and the studio
attaches the sheet (and photos) as identity references automatically — no asset ids to manage.
POST /v1/characters (MCP: create_character) — priced like one image.
{ "name": "Chip", "description": "A friendly cartoon chili pepper mascot: glossy red body, curly green stem cap, big warm eyes, tiny white chef's apron. 3D animated-film look." }
| Field | |
|---|---|
name |
Required. Up to 80 characters. |
description |
Required. Looks, outfit, personality, art style. |
referenceAssetIds |
Up to 6 photos of the person or character, uploaded as assets. The sheet is made to match them. |
consent |
Required with photos. true confirms you have this person's permission to use their likeness (or that it is not a real person). |
aspect, resolution |
For the sheet; default 16:9 at 2K. |
The response is the character with status: "creating". Poll GET /v1/characters/{id} until it is
ready (about a minute), then use it:
{ "kind": "scene", "characterIds": ["<character id>"], "aspect": "9:16",
"brief": "Chip stirs a pot on a sunlit stove, steam rising, wearing his apron." }
characterIds works on POST /v1/images and POST /v1/videos (up to 4 characters). GET /v1/characters
lists yours with sheet URLs; DELETE /v1/characters/{id} removes one and its sheet. Restate wardrobe
in each brief: the character locks who they are, not what they wear in a given scene.
Images
POST /v1/images (MCP: generate_image) — GPT Image 2.5. Priced per image by resolution; see Credits.
kind |
Use it for |
|---|---|
character_sheet |
Locking a character's identity: turnarounds, face close-up, details |
character |
A single character image |
location |
An empty, reusable set your character can be placed into |
prop |
Objects a character holds or uses, as a reference sheet |
scene |
A locked character inside a locked location |
product |
Product shots |
freeform |
Anything else |
| Field | |
|---|---|
brief |
Required. What you want, in plain words. |
kind |
Default freeform. |
references |
Up to 16 { "assetId": "…", "role": "character identity" }. Use your character sheet here for consistency. |
aspect |
1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 4:5, 5:4, 21:9 |
resolution |
1K, 2K (default) or 4K |
n |
1–4 images |
model |
gpt-image-2.5 (default), soul-2, grok-imagine-2, qwen-image-3, ideogram-4, nano-banana-pro. GET /v1/models lists what each supports (reference count, resolutions, batch). No Midjourney: it has no API and automating a personal account breaks its terms. |
seed |
Where the model supports it, for repeatable variations. |
skillIds |
Up to 4 of your own skills. |
{ "kind": "character_sheet", "aspect": "16:9",
"brief": "Chip, a friendly cartoon chili pepper mascot: glossy red body, curly green stem cap, big warm eyes, tiny chef's apron." }
Keeping a character consistent
Use a stored character and pass characterIds. If you'd rather manage it yourself, make
a character_sheet image and pass it in references with a role like "character identity".
Video
POST /v1/videos (MCP: generate_video) — Seedance 2.5, directed so every beat is timed to the clip's length.
| Mode | Send |
|---|---|
| Text to video | brief only |
| Animate an image | imageAssetId, optional endImageAssetId for the last frame |
| From references | referenceAssetIds (up to 9: character sheet, location…), characterIds |
| Following a motion reference | referenceVideoAssetIds (up to 3 clips whose choreography, camera and timing the shot follows) |
| A character speaking | referenceAudioAssetIds (a voice track, up to 3), with image or video references |
| Field | |
|---|---|
brief |
Required. The shot. |
duration |
4–30 seconds (default 5) |
resolution |
480p, 720p (default), 1080p |
aspect |
21:9, 16:9, 4:3, 1:1, 3:4, 9:16 (ignored when animating an image) |
audio |
Generate sound with the video (default true) |
estimate |
true returns the price in credits without generating. Accounts on their own provider key also see providerUsd, the cost that key will incur. |
Video is priced per request from the model provider's own quote for those exact settings, so the price follows length and resolution. Always available up front:
curl -X POST https://frugal-anteater-681.convex.site/v1/videos -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"brief":"slow push-in on Chip stirring a pot, steam rising","duration":6,"estimate":true}'
# → { "estimate": true, "credits": 14 }
For a character who talks on camera, generating the shot with the voice track (reference audio) gives far better results than lip-syncing an existing clip afterwards — the motion is built around the speech.
To put a stored character into a trend: upload the trend clip as an asset (you must have the right to use
it), pass it in referenceVideoAssetIds with the character in characterIds, and describe the scene in
the brief. The studio follows the clip's motion and camera; it does not copy the clip's footage.
Voice
POST /v1/voice (MCP: generate_voice) — ElevenLabs v3, 1 credit per 500 characters.
By default your script is directed into a performed one — same words, with
v3 audio tags and punctuation for rhythm and emotion, because flat reads are almost always a rhythm
problem rather than a voice problem. perform: false reads the text exactly as written.
| Field | |
|---|---|
text |
Required. Up to 5,000 characters. |
voice |
A voice name (default Rachel). |
direction |
How it should be performed: "warm, slightly amused, conspiratorial". |
stability |
0–1. Lower is more expressive. |
perform |
Default true. |
Lip sync
POST /v1/lipsync (MCP: lipsync) — 15 credits per clip: re-times a video's mouth to an audio track.
Use it for footage you already have; for new speaking shots, prefer video with reference audio.
Availability
Image generation and the skills are live. Video, voice and lip sync return 503 not_configured on a
deployment where their provider has not been connected yet.
Your own skills
The studio applies its own craft inside every generate_* call. If your team has recipes of its
own — an unretouched-portrait look, a brand duotone, how to add someone to a group photo so the
grain matches — keep them in the product instead of in a doc: POST /v1/skills
{ "name", "instructions", "defaults": { "aspect", "resolution", "n" }, "references": [{ "assetId", "role" }] }.
Skills are shared with the team and versioned: an update makes a new version, and a job records the
version it ran with. Apply them with skillIds on images, characters and edits, or set skillIds on a
brand kit so every image made for that brand gets them. The studio's skills still run first; yours are
appended to the director's context and its references join the request's references.
GET /v1/skills, GET/POST/DELETE /v1/skills/{id}. Free.
Edit a photo in place — POST /v1/images/edit
Put a stored character or a referenced subject into a masked area of a real photo and keep every pixel outside it exactly as shot. The model only sees a window around the mask; its output is composited back inside the mask with a feathered edge, so the rest of the photo is the original bytes, not a re-render.
{ "sourceAssetId": "…", "mask": { "rect": [0.62, 0.12, 0.3, 0.8] },
"brief": "standing behind the couch, hands in pockets, lit from the left like the man next to him",
"characterIds": ["…"] }
| field | |
|---|---|
mask |
{ "rect": [x, y, w, h] } or { "polygon": [[x, y], …] } in pixels or 0–1 fractions — include the whole subject (head to feet), with a little room. |
maskAssetId |
Instead of mask: an image the size of the photo, white or opaque where to paint. |
characterIds, references, model, skillIds |
As for images. |
Output: the edited image at the photo's resolution (outputs.image) and the mask used (outputs.mask).
Priced like one image. For several people, run one edit per mask.
Swap a character into a video — POST /v1/swaps
Keeps the source footage — camera, motion, everyone else, timing — and changes only the subject.
This is the trend-video tool: POST /v1/fetch with start/end for a 4–30 s clip, then:
{ "mode": "object_swap", "sourceVideoAssetId": "…", "characterIds": ["…"], "resolution": "720p", "estimate": true }
mode: "motion_transfer" is the other direction: your character driven by the source's
choreography and camera. 1–8 reference images in total (characterIds attach a character's sheet
and photos; references adds more). Output keeps the source length (sources over 30 s are trimmed).
Quoted per request; estimate: true first. Own-key accounts pay the platform fee plus the provider.
Several people, one photo
edits: [{ mask, brief, characterIds }, …] (up to 6) applies the slots in order on the same photo in
one job; each slot sees the previous one's result, so inserted people see each other.
Voices of your own
With your ElevenLabs key connected (Team):
GET /v1/voices— your workspace's voices and the clones made here.POST /v1/voices/clone{ name, sampleAssetIds (1–5 clean single-speaker clips), consent: true }— an instant clone on your workspace; returns avoiceIdforPOST /v1/voice,POST /v1/dialogueand explainers. Priced like any voice (1 credit per 500 characters).DELETE /v1/voices/{id}.POST /v1/dialogue{ lines: [{ speaker, voiceId, text }] }— one track, per-linestart/end.
Drafts, finalize, cancel
draft: trueonPOST /v1/videosrenders at 480p for a cheap likeness/choreography check.POST /v1/videos/{jobId}/finalize{ "resolution": "1080p" }re-renders a finished job with the same directed prompt and inputs;estimate: trueshows the price.POST /v1/jobs/{id}/cancelcancels a queued or running job and refunds its credits in full.- Generated clips start moving on the first frame; pass
holds: trueif you want the opening and closing frames held for a title.
Upscale — POST /v1/upscales
{ "assetId": "…", "factor": 2 } resamples a video up to 2160p with Lanczos and light sharpening.
This is honest resampling, not a generative upscaler; the result says method: "lanczos". 3 credits.
Agents: this page is also available as Markdown; the whole reference is in llms-full.txt.