# Generating images, characters and video # 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](#characters) 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](recording.html)), 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. ```json { "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: ```json { "kind": "scene", "characterIds": [""], "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](billing.html). | `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](#your-own-skills). | ```json { "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](#characters) 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: ```bash 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. ```json { "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: ```json { "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](team.html#your-own-provider-keys)): - `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 a `voiceId` for `POST /v1/voice`, `POST /v1/dialogue` and explainers. Priced like any voice (1 credit per 500 characters). `DELETE /v1/voices/{id}`. - `POST /v1/dialogue` `{ lines: [{ speaker, voiceId, text }] }` — one track, per-line `start`/`end`. ## Drafts, finalize, cancel - `draft: true` on `POST /v1/videos` renders 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: true` shows the price. - `POST /v1/jobs/{id}/cancel` cancels a queued or running job and refunds its credits in full. - Generated clips start moving on the first frame; pass `holds: true` if 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.