Pro API: generate videos with sound
A clip from a prompt, or an animation of a still you supply. The file is an MP4, with sound when you ask for it.
Submit a video
POST /videos
{ "prompt": "a woman in a white linen dress walks along a beach at sunset, wind in her hair, she turns and smiles at the camera",
"width": 1080, "height": 1920, "audio": true }| Field | Type | Default | What it does |
|---|---|---|---|
prompt | string | required | The action, the mood, the light. Describe the movement. |
width, height | int | 720 x 1280 | Vertical by default. See the price grid below. |
quality | string | max: Ultra mode (the finest render), see Ultra mode. | |
consistency | string | standard | enhanced: enhanced consistency, steadier movement, faces and background over time, up to 721 frames per shot (30 s at 24 fps, 24 s at 30), extensions included. Quality mode only. See Enhanced consistency. |
mode | string | ltx25 | ltx25: engine 2.5 (sound, automatic duration, 1080p, end frame). turbo: retired on 11 October 2026; a turbo request is rendered in Quality 480p (same orientation, same length, 241 frames at most, fixed length), at the Quality 480p price. |
duration_auto | bool | true | The engine picks the length from the prompt, up to 20 s at 24 fps (16 s at 30, 8 s at 60: the cap counts frames, see fps), and length is then ignored. Set false for a fixed length, given by length. With consistency enhanced above 30 fps, the length is always fixed (fps_requires_fixed_duration otherwise). |
length | int | 121 | Length in frames, at the fps rate, always of the form 8n+1; the duration is (length - 1) / fps. 481 at most (721 with consistency enhanced). At 24 fps: 73 (3 s), 121 (5 s), 241 (10 s), 361 (15 s), 481 (20 s; 721, that is 30 s, in enhanced). At 30: 241 (8 s), 481 (16 s; 721, 24 s). At 60: 241 (4 s), 481 (8 s; 721, 12 s). Used for a fixed length (duration_auto set to false); ignored with automatic duration. |
fps | int | 30 | Frame rate, in frames per second: 24, 30 or 60. 30 by default, the recommended setting (crisp movement); 60, the smoothest; 24, the cinema rate. 50, with consistency enhanced only, gives a slight slow motion (about 0.83x: the engine sets the movement as for 60 and plays it at 50). length counts frames at this rate. The price does not change (flat rate per clip); the maximum length does, since it counts frames. An extension keeps the frame rate of the previous clip, a sequence that of its first shot. A rate the mode does not have: invalid_fps, which lists the ones it has. |
audio | bool | false | Sound generated along with the picture (ambience, voice). Doubles the price of the clip (see Formats and price). |
image | string | Start frame in base64: the video animates this image. | |
end_image | string | End frame in base64, alongside image: the clip transitions from one to the other (engine 2.5 only). A transition comes out at the requested size, from 480p to 1080p, and is billed as a clip of that format. | |
keyframes | array | Keyframes (Quality mode): 1 to 4 objects { "image": base64, "at": seconds, "strength": 0.1 to 1 (1 by default) }. The clip shows each image at its moment, for example a character in the middle of a shot that starts and ends on an empty location. The length becomes fixed (length, duration_auto forced to false); 0 < at < length, at least 0.5 s between two keyframes; accurate to about a third of a second. Works with image and end_image. Not in Ultra or Multi-Ref; in an extension, only with consistency enhanced (see Enhanced consistency). Same price as a clip of the same format. | |
lora, lora_strength | string, number | A trained character on the account (name from GET /loras) and its strength (0 to 1). | |
lora2, lora2_strength | string, number | A second character. | |
multiref | object | Cast 1 to 4 characters (12 in Ultra) from GET /characters, plus saved objects, animals and a location from GET /subjects, called by @Name in the prompt, see Multi-Ref Video. | |
seed | int | random | Seed of the render, from 0 to 4294967295. Same seed, same prompt, same image and same settings (format, length, sound, mode): the same clip, with slight differences if the graphics card changes. With automatic duration, a length chosen differently changes the clip: for a strict reproduction, set duration_auto to false and give length. |
steps | int | engine | Leave empty. |
Default frame rate: 30 fps. Clips used to be rendered at 24 fps. Without fps, a clip of the
same length is therefore shorter than before (241 frames: 8 s instead of 10 s). Send "fps": 24
to keep the former behaviour.
The answer:
{ "jobId": "...", "seed": 908172, "cost": 30 }The cost is computed and announced at submission, before anything is charged. The seed is
returned too, whether you gave it or it was drawn at random.
Formats and price
A video is priced per format, as a flat rate independent of its length. Base: 5 credits.
The flat rate is multiplied by 2 at 720p and by 3 at 1080p, and then sound doubles the price.
The factors apply to the base rate, not to each other: 1080p with sound costs 5 x 3 x 2 = 30
credits. Up to 481 frames, that is 20 s at 24 fps, length changes nothing (5 s or 20 s, same rate).
Beyond 481 frames (up to 721, with consistency enhanced only, fixed length), the price of the
format is doubled. The frame rate (fps) does not change the price, but the tiers count frames:
at 30 fps, the normal price goes up to 16 s; at 60, up to 8 s (12 s at double the price in enhanced).
One exception: in Quality, 480p without sound costs 2 credits, whatever its length (since 11 October 2026). Multi-Ref and enhanced consistency keep the flat rate of the format, 5 credits.
| Format | Short side | Example sizes | Silent | With sound |
|---|---|---|---|---|
| 480p | 480 | 480 x 832, 832 x 480 | 2 (5 in Multi-Ref and enhanced consistency) | 10 |
| 720p | 720 | 720 x 1280, 1280 x 720 | 10 | 20 |
| 1080p | 1080 | 1080 x 1920, 1920 x 1080 | 15 | 30 |
The size you get back. The engine works in multiples of 32 and in two stages, so the file comes out at 768 x 1280 (or 1280 x 768) for 720p and 1088 x 1920 (or 1920 x 1088) for 1080p. 480p stays at 480 x 832 (or 832 x 480), except in enhanced consistency. Worth knowing if you stitch or overlay our renders.
The turbo lane was retired on 11 October 2026: a turbo request is rendered in Quality 480p
and priced like it (2 credits without sound, 10 with sound).
Know the price before you submit
POST /videos/quote takes the same body as POST /videos and runs the same calculation, but
nothing is created: no render, no credit held, no quota consumed. Call it before you commit,
especially from an agent working against a budget.
{ "cost": 30, "mode": "ltx25", "format": "1080p", "audio": true, "fps": 30, "duration_auto": true,
"seconds_max": 16, "detail": "flat 5 x 3 (1080p) x 2 (sound) = 30" }detail spells out the figure; fps is the frame rate used, and seconds_max the length in seconds
at that rate. With "consistency": "enhanced", the quote accepts a fixed length up to 721 frames and
applies the rate beyond 481 frames (x 2 (over 20 s) in detail at 24 fps, x 2 (over 481 frames) at
another rate). The same route exists for images, POST /images/quote, which
always answers { "cost": 1, ... }: an image always costs 1 credit.
For an Ultra clip billed per second, the quote gives the billed length in seconds, and detail spells out the price per second.
Poll, download, cancel
The same routes as images, under /videos.
GET /videos/{jobId}:pending/completed/failed, same fields. An enhanced extension can end asfailedwith"error": "extension_not_applied"(the engine did not take up the end of the previous clip): nothing is charged, submit it again.GET /videos/{jobId}/file: the MP4 (Content-Type: video/mp4).POST /videos/{jobId}/cancel: cancels, and nothing is charged.
In Ultra billed per second, a render that is already computing no longer stops: the cancel answers 409 render_in_progress, and the clip is delivered and billed. A clip the engine refuses on output ends as failed, with a sentence in detail and the engine's code in reason: nothing is charged.
A video takes 1 to 5 minutes, longer when the engine starts cold. Poll every 3 seconds and
download as soon as the status is completed: the file is not archived.
Several shots in one clip
A single prompt can hold several shots with hard cuts, up to 20 seconds in all at 24 fps (16 s at 30, the default). Describe them in
order: Shot 1: <framing>, <action>. Cut to Shot 2: .... Repeat in every shot what has to stay
the same (character, outfit, place, light), and end with one line of shared mood.
Leave out timecodes: keep duration_auto on, and the engine picks the length (up to 20 s at 24 fps, 16 s at 30) and
where to cut. Two to four shots per clip work best.
A cinematic sequence of three shots, same woman, same place, same lighting throughout.
Shot 1: Wide shot, night, light rain on a narrow Paris street with glowing café windows. A woman in a long beige trench coat walks toward the camera under a transparent umbrella, wet cobblestones reflecting warm yellow streetlights.
Cut to Shot 2: Close-up on her face under the umbrella, raindrops on the plastic, warm café light on her cheek, she smiles softly.
Cut to Shot 3: Medium shot from inside the café through the rainy window, she pushes the door open and steps in, warm light spilling onto the wet pavement.
Calm, melancholic, romantic mood.Several shots in one clip, or a sequence? They do different things:
| Several shots in one clip | Sequence (extensions) | |
|---|---|---|
| Between shots | Hard cuts | No cut: each shot carries the movement on |
| Length | 20 s at most at 24 fps (16 s at 30) | Longer |
| Renders | One | One per shot |
| Price | One clip | One clip per shot |
Extend a clip
A clip can be extended: the next shot carries on the movement of the previous one, with no
stop at the join. It is guided by roughly the last second of the previous clip, not by a single
still frame. Add to POST /videos:
| Field | Type | Default | What it does |
|---|---|---|---|
extend_job_id | string | The clip to extend: a render from your licence, taken shortly after it finishes. | |
extend_frames | int | 25 | How much of the end of the clip guides the next shot, in frames: 9, 17, 25, 33, 41 or 49 (25 is about 1 s at 24 fps). |
extend_video | string | Instead of extend_job_id: the end of a clip you have yourself, as a base64 MP4 of exactly extend_frames frames, 8 MB at most. |
The extended shot keeps the resolution of the previous clip: width and height are ignored
with extend_job_id, and with extend_video the size is read from the file you send. It holds
only the new frames, so the shots laid end to end form one continuous video. It keeps the frame
rate of the previous clip: leave out fps (it takes it over) or give the same one; another one is
refused (extend_fps_mismatch). length is the new duration of the shot (121 = 5 s more at
24 fps), and it is exact: automatic duration does not apply
to an extension. An extension costs the price of a clip.
Limits: no end frame or keyframes at the same time, except with enhanced consistency.
The sound carries on at the join. With audio: true, when the end of the previous clip has a sound
track, the new shot picks up that sound instead of making up a new one. With extend_job_id, the cut
end keeps the clip's sound; with extend_video, send the end with its sound. Without a sound track in
that end, in enhanced consistency and in Ultra, the sound starts fresh with
the shot: keep the same ambience in every prompt, so the join is heard as little as possible.
Extend a clip shortly after it finishes. Later on it is no longer available to us
(clip_unavailable), and you send its end yourself in extend_video.
Extend an Ultra shot, forward or backward
A shot rendered in Ultra per second is extended in Ultra too, forward or backward; any other clip is extended as above. Send POST /videos with "quality": "max" and:
| Field | Type | Default | What it does |
|---|---|---|---|
extend_job_id | string | The Ultra shot to extend, rendered by your licence less than 24 hours ago. | |
extend_upload_id | string | Instead, for an older shot: the id returned by POST /extensions (below), once its original file is uploaded. | |
extend_direction | string | forward | forward renders what comes next; backward renders the moments just before the shot, to cut in front of it. |
- The render holds only the new part, as long as an Ultra clip (
length/fps, 5 s with the automatic length). It takes the resolution of the shot (widthandheightare ignored) and joins its last frame (forward) or its first (backward). - No start image, references (
multiref), motion or enhanced consistency.backwarddoes not go into a sequence, which files its shots one after the other. - Price: the new part at the Ultra price per second, plus, per second of the shot you extend, 2 credits in 480p, 5 in 720p and 13 in 1080p (
ultra.extend_per_secondinGET /pricing). Those seconds are rounded up to the next second (to within 0.1 s) and count at 24 frames per second: a shot at a higher frame rate counts pro rata (2 s at 60 fps count as 5 s).POST /videos/quotewith the same fields givesextend_secondsand the exact price. - A shot older than 24 hours: upload its original file, as delivered by
GET /videos/{jobId}/file.POST /extensionswith{ "size": <bytes> }returnsupload_id,upload_url,headersandexpires_in(900 s); send the file withPUT upload_urland exactly thoseheaders, then passextend_upload_idwithin 2 hours. An upload serves one render: an MP4 of 4 to 30 s, 60 MB and 1080p at most. Before the render, every video sent to the Ultra mode (a motion or a video to extend) is checked, and nothing is charged if it is refused (400 input_video_out_of_limits): 24 to 60 frames per second, a width/height ratio of 0.4 to 2.5 and, for one render, 30 s and 10 files at most for the videos and reference sounds together. For a realistic face, a re-encoded or edited file is refused (400 engine_refused): only the original goes through. A video the Ultra mode did not render (a render from another mode, for example) is not extended in Ultra when it shows a realistic face (400 extension_real_face, nothing is charged): extend it in Quality mode, or redo the shot in Ultra.
Enhanced consistency
With "consistency": "enhanced", the clip is rendered by a lane that holds movement, faces and
background steadier over time. It goes up to 721 frames per shot, that is 30 s at 24 fps and 24 s at 30 (length, fixed length)
and is mostly meant for long videos:
- First shot: a normal clip (prompt, start frame,
keyframes,end_image), up to 721 frames. - Extension:
extend_job_id(orextend_video) as in Extend a clip, with the sameconsistency.lengthis still the new duration (up to 721, that is 30 s more at 24 fps). An enhanced extension also takeskeyframes, whoseatcounts from the first new frame, and an end frameend_image, set on the last frame of the shot: for example the start frame of the first shot, so that a long video loops.
Quality mode only (mode ltx25, without quality: "max"), up to 1080p, not in Multi-Ref
(400 consistency_mode). Price: the flat rate of the format (480p without sound:
5 credits) up to 481 new frames (20 s at 24 fps), doubled beyond (see the
price grid above). Frame rates: 24, 30, 50 or 60. Above 30, the length is fixed
(fps_requires_fixed_duration otherwise); 50 gives a slight slow motion (see fps). The render takes longer than a standard
clip, and its start may wait for a larger graphics card (long shots). A sequence keeps the same
consistency for all its shots. Size delivered: the same as in Quality at 720p and 1080p; at 480p,
the short side comes out at 448 (448 x 832, 832 x 448, 448 x 448), as this lane works in multiples of 64.
Some prompts are not supported yet. For them, a single shot of 20 s or less (15 s with an
end_image, at a fixed length) is rendered in standard consistency, at the same price. Beyond that
(length above 481 with duration_auto: false, or above 361 with an end_image), in an extension or in
a sequence, the request is refused with 400 consistency_explicit: shorten the shot or ask for
consistency: "standard".
Long video (sequences)
A clip lasts 481 frames at most, that is 20 s at 24 fps (721 with enhanced consistency, 30 s at 24 fps). For longer, a sequence files each shot as its render finishes, then joins them into a single MP4 without re-encoding. One minute, for example, is 3 shots of 20 s, or 2 shots of 30 s in enhanced consistency, at 24 fps.
POST /sequencesopens a sequence and answers{ "sequence_id": "seq_...", ... }.POST /videoswith"sequence_id": "seq_..."for each shot: the first one as usual, the next ones withextend_job_idset to the previous shot. Each shot is filed in the sequence, in submission order, as soon asGET /videos/{jobId}sees it finished.POST /sequences/{id}/exportonce the last shot is finished. It answers withseconds,resolutionandfile_url.GET /sequences/{id}/file: the assembled video (MP4).
Alongside: GET /sequences/{id} gives the state (shots filed or still expected),
POST /sequences/{id}/clips with { "job_id": "..." } files a render by hand, and
DELETE /sequences/{id} deletes the sequence at once.
Rules. Every shot has the same resolution (extensions keep it) and the same sound choice, all
with sound or all silent, the same consistency and the same frame rate (fps): the first shot sets them, and a shot
that differs is refused (409 consistency_mismatch, 409 sequence_fps_mismatch; 422 encoding_mismatch
for a shot filed by hand). A shot without fps takes the sequence's. At most
30 shots per sequence and 5 sequences in progress per licence.
What we keep, and for how long. The shots of a sequence in progress are erased 30 minutes after the last one was added, and the assembled video 24 hours after the export. Then everything is gone.
Export the project folder
A project folder gathers in a ZIP everything needed to pick a video up elsewhere or to automate it: the shots, their references, the edit, and a manifest that can render each shot again. It is free: nothing is rendered.
POST /videos/{jobId}/export: one render ofPOST /videos(single shot, Multi-Ref, extension).POST /sequences/{id}/project: every shot of a sequence, end to end.- Optional body, the same choices as the app's export window:
include_media(trueby default): the videos of the shots and their signed originals.false: no video at all (no clip, no original, no film); the shots keep their text, settings and references ("state": "expired") and the manifest carriesvideos_included: false.include_references(trueby default): the views of the characters and the images of the elements, inreferences/.false: none is copied; their names, @mentions,idand fingerprints stay in the manifest (references_included: false).exclude_references: the references not to copy, one by one, byid(character_id,subject_id), name or @mention, for example["@Parc", "chr_..."](200 at most). The manifest carriesreferences_included: "partial"and"included": falseon each one left out (falseif all are). An entry that names no reference of the export:400 unknown_reference.include_film(sequence only,falseby default): the film of a sequence already assembled; it needsinclude_media(otherwise400 invalid_export_options, like an option that is not a boolean, or anexclude_referencesthat is not a list of texts).manifest_only: true: the manifest alone, without a ZIP.
The answer is { "manifest": { ... }, "file_url": "...", "export_id": "exp_...", "expires_at": "...", "bytes": n }, and GET /exports/{id}/file downloads the ZIP. One export at a time per licence (409 export_in_progress), 2 GB at most (413 export_too_large: export the shots one by one).
| File | What it holds |
|---|---|
tendre-project.json | The manifest (schema tendre.project/1, English keys). For each shot: its state, the prompt you typed (prompt) and the one sent to the engine (prompt_sent), the settings, the seed, the cost, the extension (extends, direction), the references linked to their files, and api_request, the POST /videos body that renders the shot again (null if an image was only sent in base64); at its head, references_included: true in the ZIP (the reference files are in references/), false with manifest_only (references without files: files empty, sha256 fingerprints only); a manifest without this field (older exports) reads as true. A folder exported by the app, where the client picks the references one by one, carries true, false or "partial", and each reference may carry included (absent: like the head, false if the head is false, otherwise true); a reference left out keeps its name, @mention, id and description, with files empty. project.settings holds the settings of the first shot (quality, width, height, fps, audio, output_format "mp4") and settings_from_shot, that shot's id. A folder exported by the app may hold an app block reserved for the app: ignore it |
shots/NN/clip.mp4 | The render of each shot, while the server still has it; shots/NN/original.mp4 or .mov: the engine's signed file, byte for byte, if it still exists |
references/ | The views of the characters and the images of the locations, objects, animals, styles and outfits used |
timeline.otio, timeline.fcpxml | The edit (OpenTimelineIO; FCPXML for Final Cut Pro and DaVinci Resolve), media by path relative to the folder |
film.mp4 | The assembled video of a sequence, only with include_film: true and if it was already assembled (POST /sequences/{id}/export); otherwise assembled is null in the manifest |
What we keep, and for how long. For each accepted POST /videos, the server keeps a record of the render without any image or video: the text, the settings, the identifiers (an image sent in base64 only leaves its fingerprint). It is erased 24 hours after submission; past that, the export answers 404 unknown_export. The ZIP is erased 45 minutes after its first download, 2 hours after its creation at most. A shot the server no longer has comes out as "state": "expired", without its file but with its text, settings and references: an agent can render it again with api_request.
Sequence. Assembling erases the shots one by one. To get the shots and the film, export the project before assembling, then assemble and export again with { "include_film": true }: the film is added, and the shots then come out as expired.
Ultra mode
The Ultra mode is the finest video render. Add "quality": "max" to POST /videos (with mode ltx25, the default). It is billed per second, or at a fixed price with a LoRA or in an extension of a clip not rendered per second; extending an Ultra shot, forward or backward, is billed per second. POST /videos/quote gives the exact price of your request.
Per second
The clip is billed per second according to its resolution, sound included:
| Resolution | Price |
|---|---|
| 480p | 4 credits per second |
| 720p | 8 credits per second |
| 1080p | 20 credits per second |
- Length: 4 to 30 s at 24 frames per second, known before the render: 5 s with the automatic length (
duration_auto, the default), otherwiselength/fps(24 whenfpsis absent), rounded to the second. - Format: the one asked, at the nearest ratio; 480p is rendered in 480p. Above 1080p:
resolution_outside_ultra. - Start image (
image): possible. The engine may refuse a text or an image on submission:400 engine_refused, with its reason indetailand its code incode; nothing is charged. - No seed:
seedis ignored and the answer gives none. - Cancel: free while the render waits its turn; once it computes,
409 render_in_progress, and the clip is delivered and billed. - No end image, no keyframes and no enhanced consistency in Ultra.
At a fixed price
With a LoRA or in an extension of a clip not rendered per second, Ultra is rendered with the full video model, at a fixed price:
| Format asked | Size delivered | Fixed price per clip |
|---|---|---|
| 480p or 720p | As in the table above at 720p (1280 x 768), a 480p request is rendered in 720p | 40 credits |
| 1080p | As in the table above (1920 x 1088) | 60 credits |
The price is fixed per format: sound and length change nothing. Ultra starts at 720p: a 480p
request is rendered, and billed, in 720p. POST /videos/quote gives the exact price. 1080p (1920 x 1088) is the ceiling, in every mode: 4K is no longer offered. A larger size is refused (ultra_4k_unavailable with quality: "max", resolution_above_quality without).
- Length: 481 frames at most, like any clip (20 s at 24 fps, 16 s at 30, 8 s at 60). Extensions and long videos work in Ultra too. An extension keeps the size of the clip it continues, and below 720p it is billed at the 720p price.
- Time: several minutes for a 5 s clip, longer for a 20 s clip. The start may wait for a large graphics card to become free.
- No transition (
end_image) in Ultra (ultra_incompatible). - No keyframes (
keyframes) in Ultra (keyframes_mode). - A character LoRA from your account applies in Ultra as in the normal mode.
Multi-Ref Video
A clip where 1 to 4 characters (up to 12 in Ultra) play, each one recognisable, in an optional location, with saved
objects, animals and a saved location when you need them. The characters come from
GET /characters: made from one photo through the API, or in the app. The
objects, animals and locations come from GET /subjects. Add the
multiref object to POST /videos (permission videos) and call each character by @Name in the
prompt:
{
"prompt": "@Lea walks toward @Tom in a sunny kitchen and hands him a cup. Sound: footsteps, soft music.",
"multiref": {
"characters": [
{ "character_id": "chr_8f3a...", "name": "Lea", "description": "a 28 year old woman with long red hair" },
{ "character_id": "chr_21bc...", "name": "Tom", "description": "a 30 year old man", "strength": 1.2 }
],
"background_b64": "<optional: an image of the location>",
"background_description": "a sunny kitchen"
},
"width": 1088, "height": 1920,
"audio": true
}Field of multiref | What it does |
|---|---|
characters[].character_id | A character from GET /characters |
characters[].name | One word (letters, digits, _), the one written after @ in the prompt |
characters[].description | A short description in English; by default, its kind |
characters[].strength | Weight of the likeness, 0 to 2 (1 by default) |
characters[].sheet_id | Optional: which sheet to use for a character that has several (the most recent by default) |
characters[].voice | With "audio": true, characters[0] and characters[1]: character (the default) is the voice of its profile when it is online (has_voice on the character), otherwise a voice made by the model; generated asks for a voice made by the model. The answer says which one was used (voices). |
characters[].voice_b64 | Optional, characters[0] and characters[1] only, with "audio": true: another voice for the character, an audio file (WAV, MP3, OGG, FLAC) in base64, 4 MB at most (a short excerpt is enough), which takes precedence over the profile's |
background_b64, background_description | Optional location: an image and a few English words |
- Prompt in English, one paragraph in the present tense;
Sound:for the sound, lines of dialogue in quotes,exactly 2 peopleand where each one stands when it helps, no negations. A creature is described by what it is. An@Namethat matches no character is refused (unknown_mention). - Formats (
widthxheight): portrait 480 x 832, 720 x 1280, 1088 x 1920; landscape 832 x 480, 1280 x 720, 1920 x 1088; square 480 x 480, 720 x 720, 1088 x 1088. No 4K; 480p in Quality only. - Modes: Quality (the default), or Ultra (
quality: "max"). No extension and no transition. - Length: automatic (
duration_auto, the default), or fixed withduration_auto: falseandlength121 (5 s), 241 (10 s) or 481 (20 s) at 24 fps; at another rate, the same durations counted in frames, 481 at most (at 30: 145, 297, 481; at 60: 297, 481). - Frame rate:
fps24, 30 (the default) or 60, as for any clip;lengthcounts frames at this rate, 481 at most (16 s at 30, 8 s at 60). - Price: the price of a clip. In Quality, the flat price of the format in the grid above,
doubled with sound; 480p without sound stays at 5 credits (the low price of
Quality does not apply to Multi-Ref).
POST /videos/quotewith"multiref": truegives the exact price. - The server fits the views to the exact format of the video: send the characters as they are. A
character that has only one view is rendered, with
warningslimited_likenessin the answer: the likeness is limited. - Profile voices: with sound,
characters[0]andcharacters[1]speak with the voice of their profile by default, when it is online (has_voice);voicegeneratedlets the model make one. The answer gives the voice used for each one invoices(character_id,voice:character,customorgenerated). - Character voices (
voice_b64): the model makes the character speak with that voice, lips included; the server takes its first 5 seconds. Two voices at most, oncharacters[0]andcharacters[1](the engine supports 2 voices). Ordercharacterslike the dialogue:characters[0]speaks first,characters[1]answers. It needs sound ("audio": true), otherwisevoice_requires_audio; a voice on another character:voice_slot; an unreadable file:unsupported_audio_format; over 4 MB:audio_too_large. Only the first 5 seconds are used: send a short excerpt. The whole request body stays under 30 MB (past that, a413with no JSON body). No extra charge. - Polling, download and cancel are those of any video (see Poll, download, cancel, above).
Saved objects, animals and locations. Two more fields of multiref cast the
subjects you saved from your own images:
Field of multiref | What it does |
|---|---|
subjects[] | Objects and animals (subject_id, then optional name, description, strength). They share the slots with the characters: 4 references at most in all; in Ultra, up to 30 images in all (see below). |
location | One saved location as the setting (subject_id, then optional name, description, strength): its images, with a strength of its own. It replaces background_b64 (both together: multiref_incompatible). It needs at least one character, object or animal with it. |
name defaults to the first word of the subject's name, and each @Name in the prompt is matched
like a character's. A location passed in subjects, or an object passed as location, is refused
(invalid_subject_type). The price does not change: it is the price of a clip. A subject plays with its first 5 images (all of them in Ultra, 6 at most).
"multiref": {
"characters": [{ "character_id": "chr_8f3a...", "name": "Lea" }],
"subjects": [{ "subject_id": "sub_1c2d...", "name": "Rex" }],
"location": { "subject_id": "sub_9e8f...", "name": "Loft" }
},
"prompt": "@Lea throws a ball to @Rex across @Loft. Sound: barking, laughter."In Ultra, a Multi-Ref clip follows the rules of the Ultra billed per second: the same price per second, sound included, 4 to 30 s at 24 frames per second (5 s with the automatic length), 480p to 1080p, no seed.
- Each character goes with its character sheet (or, without one, with all its images), assembled by the server into a single image; each object, animal or location with all its images (6 at most).
- The first render of a character prepares that image, a few tens of seconds more: its
jobIdthen starts withsdp-, and is followed like the others. - No voice:
voice_b64and the profile voice are ignored (warningsvoice_ignored), andstrengthhas no effect. - The engine may refuse a reference image (a real face, for example): the render ends as
failedwith the reason, and nothing is charged.
Styles, outfits and motion. In Ultra only (quality: "max"), three more fields of multiref; elsewhere the request is refused (multiref_ultra_only), never rendered without them.
Field of multiref | What it does |
|---|---|
styles[] | Saved styles (subject_id, then optional name, description): the whole clip takes their look (technique, light, colours, textures), never their content. |
outfits[] | Saved outfits (same fields, plus worn_by), worn as on their images. worn_by (optional) is the name of one of the characters: it adds "<Name> wears <Outfit>." at the head of the scene. Without it, write it in the prompt: @Lea wears @RedGown. |
motion | One motion video of 4 to 30 s: { "motion_id": ... } (uploaded with POST /motions) or { "video_b64": ... } (MP4, MOV, WebM or MKV, 20 MB at most), and an optional description of the movement in English. mode says what the clip takes from it: camera (the default), its camera movement, framing and rhythm; choreography, the body movements, gestures and their rhythm. Never the person or the place of the video; the same price in both modes. The server brings it down to about 410,000 pixels (856 × 482 in 16:9), at 24 frames per second, without sound; its width/height ratio must be between 0.4 and 2.5. |
- 30 images at most in Ultra, characters, subjects, styles, outfits and location together (
multiref_too_many_images), with 12 characters at most (multiref_characters_requiredbeyond). 8 are recommended: from 9 to 12, the render goes through withwarningsmany_characters, as some may be missing or mixed up. A style outsidestyles, or an outfit outsideoutfits, is refused (invalid_subject_type). -
Price of the motion: 3 credits per second of the motion video (rounded up to the next second), on top of the render, read in
GET /pricing(ultra.motion_per_second);POST /videos/quotewith the samemultiref.motiongivesmotion_secondsand the exact price. - Direct upload, for a heavy motion video:
POST /motionswith{ "size": <bytes> }(20 MB at most) returnsmotion_id,upload_url,headersandexpires_in(900 s); send the file withPUT upload_urland exactly thoseheaders, then passmultiref.motion.motion_idtoPOST /videoswithin 2 hours. Amotion_idserves one render.
"quality": "max",
"multiref": {
"characters": [{ "character_id": "chr_8f3a...", "name": "Lea" }],
"styles": [{ "subject_id": "sub_a1b2..." }],
"outfits": [{ "subject_id": "sub_c3d4...", "name": "RedGown", "worn_by": "Lea" }],
"motion": { "motion_id": "4f0c2a9b1d7e6a35", "description": "a low tracking shot from the side" }
},
"prompt": "@Lea walks along a seaside promenade at sunset."The error codes of styles, outfits and motion are in Limits and errors.
With the MCP server, the agent does all this with tendre_multiref_video: ask for the
scene and name the characters, objects, animals and the location (subjects, location_id).
Face swap
The person filmed in your own video becomes one of your characters: face,
hair, body and outfit (or the head alone, see part), keeping their gestures, their lips and the setting, and their voice unless the character has its own (see voice). It is the app's
Face swap, on one passage of 20 s at most: one character, with no chaining and
no transition. The request is POST /faceswap (permission videos):
{
"video_b64": "<the video in base64: MP4, MOV, WebM or MKV, 20 MB at most>",
"character_id": "chr_8f3a...",
"start": 2,
"duration": 6,
"scene": "@Lea wears a long red coat.",
"zone": "normal",
"likeness": 1
}| Field | What it does |
|---|---|
video_b64 | The video, 20 MB at most, with one person clearly visible. Past that, trim or compress it first, or go through the MCP tool tendre_face_swap, which takes up to 95 MB. |
character_id, sheet_id | A character from GET /characters, and its sheet if it has several (the most recent by default). |
name | Optional, 60 characters at most: the character's name, by default the one it has in GET /characters. A character created in the app only has its name online once the app has sent it (app 0.9.1 and later; otherwise name is null in GET /characters): give one to call it @Name in scene, otherwise call it Image 1. |
start, duration | The passage, in seconds: from start (0 by default), for duration (by default up to the end of the video, 20 s at most). |
scene | Optional, in English, 400 characters at most: what should change or be made precise. The character is called @Name (the first word of its name, see name) or Image 1. Without a scene, it moves exactly like the person on camera. Never make the character speak in it, nor describe its mouth, gaze or head ("says", "looks at the camera", "smiles"): it copies them from the video, and an instruction makes it invent them, with lips out of sync and a turned head. Describe what changes around it: outfit, accessory, light. |
description | Optional, in English, 360 characters at most: who the character is ("a 28 year old woman with long red hair, wearing a black leather jacket"). By default, its kind and its outfit. |
part | full (the default): the whole character. head: only the head (face and hair); the filmed body, clothes and setting stay. The character is then rendered from the three portraits of its character sheet, cropped to the head (heads_only in the answer); a character with one or two views keeps them whole. Its default description gives only its kind, without the outfit, and zone is tight by default. |
zone | The space around the filmed silhouette where the character can be drawn: tight, normal (the default) or wide (room for big ears, wings, a broad build). With part head, it is the space around the head, tight by default. |
likeness | How closely the character sticks to its views, from 0.5 to 2 (1 by default). |
audio | true by default: the sound of the video is kept, and the lips follow it. false: a silent video. |
voice | character (the default): the sound of the video is converted into the voice of the character's profile when it has one online (has_voice in GET /characters), in the same render and without changing its pace, so the lips stay in sync. Without a profile voice, the filmed voice stays. original: keep the filmed voice. The answer says which one was used (voice: character, custom, original, or none without sound). |
voice_b64 | Optional, with sound: another voice for the character (WAV, MP3, OGG, FLAC, 2 MB at most; its first 20 seconds are used), which takes precedence over the profile's. The sound of the video is converted into this voice, without changing its pace. |
seed | Seed of the render, from 0 to 4294967295, to render it again or vary it. |
Video and voice together: 22 MB at most, so that the request body stays under 30 MB (past that, a
413 with no JSON body).
- The server prepares everything. It cuts the passage, sets it to the working frame rate (30 fps for a source at 29 fps or more, 50 and 60 included, 24 below) and to the working format (multiples of 128, a short side of 768, the ratio closest to your video), and fits the character's views to that format.
- What you get back is the video of the passage at the working format, about one megapixel (768 x 1408 for a vertical video, for example), with the proportions of your video within 5%. Full resolution and several characters in a row stay specific to the tendre.AI desktop app: it pastes the character back onto the original video at its full resolution, and chains several characters.
- Price: 6 credits per second of the prepared passage (
secondsin the answer), rounded up to the next credit. The answer givesjobId,seed,costand the prepared passage (width,height,length,fps,seconds,audio,voice,views,part). A character that has only one view is rendered, withwarningslimited_likeness. - No quote: neither
POST /videos/quotenor the MCP'stendre_quoteknows Face swap. To work the price out before you submit, readgrid.body_swapinGET /pricing: seconds of the passage xper_block_720p(the price of a block) /block_seconds(the length of a block), rounded up to the next credit. - Some scenes are rendered from the character's three portraits cropped to the head (
heads_onlyin the answer), and the outfit of its views is no longer imposed: the scene decides. - A Face swap counts as a use of the character and pushes back its erasure, like Multi-Ref.
- Polling, download and cancel are those of any video (see Poll, download, cancel, above):
GET /videos/{jobId},GET /videos/{jobId}/file,POST /videos/{jobId}/cancel. - Errors of its own:
video_required,video_too_large,unreadable_video,invalid_start,passage_too_short,invalid_duration,invalid_zone,invalid_likeness,invalid_scene,invalid_description,invalid_character_name,invalid_part,invalid_voice_choice,character_required,unknown_character,audio_too_large(a voice past 2 MB),face_swap_too_large(the passage is still too heavy once prepared: shortenduration),server_busy(two videos are already being prepared: retry afterRetry-After, 20 s),face_swap_unavailableandface_swap_price_missing(Face swap closed, or without a price: retry later). What each one means: Limits and errors. - Your video passes through our servers while it is prepared, then goes to the graphics card that renders it, like any source image.
Only use videos of people who have given their consent: it is a rule of the terms of use.
With the MCP server, the agent does all this with tendre_face_swap.