Kolbo.AIKolbo.AI Docs
Developer API

Video-to-Video

Restyle and transform existing videos using AI with the Kolbo API.

Restyle and transform existing videos using AI. Provide a source video and a text prompt describing the desired transformation, and Kolbo generates a new video with the applied style or changes.

model is required here. Unlike the other generation endpoints, video-to-video does not support Smart Select — the model families are too heterogeneous (restyle vs. subtitles vs. motion control) for an auto-pick. Omitting model (or sending an auto-select alias such as "auto" / "smart-select" / "") returns 400 with code MODEL_REQUIRED. List the options with GET /api/v1/models?type=video_from_video (or the canonical ?type=video_to_video — both return the same models) and pass an identifier.

Model identifiers are Kolbo-specific. Never hardcode model identifiers — always fetch the current list from GET /api/v1/models?type=video_from_video first. Models may be added, renamed, or retired at any time. model must be a string; an array or object returns 400 with code INVALID_MODEL.

All generation endpoints accept an optional project_id body field that routes the output into a specific project. See Projects. Rate limit: 30 accepted requests per minute per user (all API keys on one account share the counter), counted in one bucket shared with the other standard-limit /v1/generate/* routes; requests that fail are not counted. The endpoint returns 403 / INSUFFICIENT_CREDITS before any work starts when the account cannot cover the generation.

Endpoint

POST /api/v1/generate/video-from-video

Request Body

Accepts multipart/form-data (for file uploads) or application/json (for URL-based inputs).

FieldTypeRequiredDescription
modelstringYesModel identifier from GET /api/v1/models?type=video_from_video. The identifier's types array must contain video_to_video — anything else (an image-to-video model, for example) is rejected up front with 400 and code WRONG_MODEL_TYPE; the error string names each rejected identifier and its real types. No auto-select — see the callout above.
video_urlstringConditionalURL of the primary input video. Required unless you upload a files part. Must be a Kolbo-hosted URL (upload through the Media Library first, or reuse a Kolbo output URL) — any other host returns 400 / INVALID_VIDEO_SOURCE (Use a video uploaded to your Kolbo media library.). For motion-control models this slot carries the driving video — see Motion Control.
filesfile(s)ConditionalVideo file upload (multipart field name files, max 100 MB per file). Required unless video_url is set.
promptstringNoDescribe the desired transformation or style. Optional at the API layer, but effectively required by most models: everything outside the prompt-less families fails with 400 Missing required field: prompt in metadata. Prompt-less: VEED Subtitles, Act Two, Wan Animate, Kling Motion Control, Bytedance Dreamactor, prunaai/p-video-animate / prunaai/p-video-replace, the Seedance extend models, the Morphious (kolbo-morphious-*) family (falls back to a built-in instruction for the mode; prompt capped at 4000 characters) and the Genjutsu (higgsfield-genjutsu-*) family (sent as-is, empty allowed; capped at 10,000 characters) — an over-long prompt on either returns 400 / INVALID_MORPHIOUS_INPUT, and every ltx-2.3-quality/… variant other than reference-video-to-video and inpaint. When present it must be a string, otherwise 400 / INVALID_PROMPT.
aspect_ratiostringNoOutput aspect ratio. Must be one of the model's supported_aspect_ratios. Default: "16:9" — except Seedance models (seedance…), which keep the source video's shape (adaptive) when you omit it, and kolbo-morphious-reframe, which has no default and rejects a request without a target ratio (Choose a target aspect ratio for Reframe).
durationnumber | stringNoOutput duration in seconds. Must be in supported_durations, or within min_output_duration–max_output_duration. Omit and the fallback is model-dependent — most models match the source length, others apply their own default. Billing is usually based on the probed source length, not this value — see Billing.
enhance_promptbooleanNoRun the prompt through Kolbo's enhancer. Default: false. true enables it; in a multipart/form-data request send the string "true" (or "1"). Always off for Morphious and Genjutsu models.
resolutionstringNoVideo resolution tier (e.g. "720p", "1080p"). Model-dependent — check supported_resolutions on GET /api/v1/models?type=video_from_video. Higher tiers may multiply the credit cost — see Credit Multipliers. "480p-draft" selects Draft on models that list it (see draft).
draftbooleanNoDraft mode (true / false, or the strings "true" / "false"). true forces the model's 480p Draft tier regardless of resolution; only models whose supported_resolutions include 480p-draft (currently the Seedance 2.5 video-to-video and extend models) support it — any other model returns 400 / UNSUPPORTED_DRAFT_MODE. A non-boolean value returns 400 / INVALID_DRAFT_MODE.
sound_enabledbooleanNoBilling only on this endpoint. It is recorded on the generation and fed to the credit calculation (the model's sound_credit_multiplier), but video-to-video never translates it into a provider sound parameter — it cannot turn audio on or off in the output. Leave it unset unless you know why you are sending it.
visual_dna_idsarray of stringsNoVisual DNA profile IDs (a single ID string, or a JSON-encoded array in multipart, is also accepted). You can instead tag a DNA by name in the prompt (@Name) — mentions are resolved only when no IDs are sent. On models that take reference images, the DNA stills are appended after your own reference_images and the @Name in the dispatched prompt is rewritten to the matching image slot; Kling O1/O3 consume DNA through their elements path. On a model with no image slots the @Name is reduced to plain text. An unknown or inaccessible DNA is skipped without failing the request — except on Morphious / Genjutsu models, which validate the selection and return 400 / INVALID_MORPHIOUS_DNA (or INVALID_GENJUTSU_DNA).
reference_imagesarray of stringsNoReference image URLs for models that take image inputs. A single URL string is also accepted; entries that are not http(s) URLs are dropped and duplicates removed. Pass at most max_images from the model record — max_images: 0 means the model takes none. There is no generic server-side cap check (see Input limits), so over-sending usually fails at the provider instead of returning a clean 400. Required for motion-control models and for prunaai/p-video-animate / prunaai/p-video-replace — see Motion Control.
reference_videosarray of stringsNoAdditional reference video URLs. On Seedance models (seedance…) they are sent as extra video inputs after the source video (each one is admitted and billed as video input). On every other model they land in the slot the legacy WAN 2.6 reference-to-video models read as videos; the other provider paths that read that slot (Yike, Pruna P-Video) merge it into their reference-image list, so there these URLs are sent as images, not videos. Morphious and Genjutsu models reject it with 400 (… takes exactly one source video (video_url); reference_videos is not supported.).
elementsarray of stringsNoElement image URLs (style / character reference assets alongside the main video), consumed by the Kling O1/O3 video-to-video edit models. Pass at most max_elements from the model record; max_elements: 0 means the model takes none. Not enforced server-side.
keyframesarray of objectsNoTimestamp-anchored keyframes, each { "image_url": string, "timestamp_seconds": number } (camelCase imageUrl / timestampSeconds are accepted too). Only accepted by models that declare keyframe support — on any other model they are silently dropped and a plain video-to-video runs instead. Any entry whose image_url is missing or whose timestamp_seconds is not a finite number is dropped without an error before validation, so malformed entries shrink the array rather than failing the request. Surviving entries: images must be Kolbo-hosted (upload via Media Library first) — an external URL returns 400 Keyframe images must be uploaded to Kolbo first.; a negative timestamp returns 400 Each keyframe requires a valid timestamp (seconds ≥ 0).; a timestamp more than 1 s past source_duration_seconds returns 400 Keyframe timestamp is beyond the video duration., a smaller overshoot is clamped to the duration, and two keyframes on the same timestamp (to the millisecond) are separated by nudging the later one down in 0.1 s steps (a duplicate that cannot be separated is dropped). More surviving entries than the model's keyframe ceiling returns 400 This model accepts at most N keyframes. Accepted keyframes are reduced to image_url + timestamp_seconds and re-sorted ascending by time, so the order you send them in does not matter.
source_fpsnumberNoFrame rate of the source video. Only read alongside keyframes, to place them accurately.
source_duration_secondsnumberNoDuration of the source video in seconds. Only read alongside keyframes — it is the ceiling that keyframe timestamps are validated against. Omit it (or send a non-positive value) and no ceiling check runs at all.
presetstringNoVEED Subtitles: caption style preset (e.g. "glass", "whisper", "fusion", "simple", "vegas"). Preset-backed models (such as kolbo_relight): the id of one of the model's looks; omitted or unknown, the model's default look is used. Ignored by other models.
source_languagestringNoVEED Subtitles only. BCP-47 code of the spoken language to improve transcription accuracy (e.g. "en-US", "es-ES", "he-IL"). Omit to auto-detect.
translation_languagestringNoVEED Subtitles only. BCP-47 code to translate the subtitles into (e.g. "en-US", "fr-FR"). Omit to keep the original spoken language.
srt_contentstringNoVEED Subtitles only. Raw .srt subtitle text to burn in. When set, auto-transcription is skipped.
srt_file_urlstringNoVEED Subtitles only. URL to a .srt file. Alternative to srt_content. When set, auto-transcription is skipped.
vocabularyarrayNoVEED Subtitles only. Brand names / jargon to help transcription, e.g. [{"word":"Kolbo","replaces":["colbo"]}]. Ignored when srt_content / srt_file_url is set.
customizationobjectNoVEED Subtitles only. Style overrides — position (top/center/bottom), shadow (none/min/mid/max), and text_customizations.baseline / .highlighted each with font (Google font name), weight (100-900), color (hex). Any omitted field keeps the preset default; best supported by Basic presets.
style_idstringNoMorphious Standard only (kolbo-morphious-motion / kolbo-morphious-swap). A style id from GET /api/v1/morphious-styles (catalog or one of your own; see Morphious Styles). The style takes 5 of the model's reference-image slots, so fewer of your own reference_images fit. The Lite modes and Reframe reject a style.
render_modestringNoMorphious only. "motion" or "swap" — forces the render route. Any other value is ignored.
reference_mapobjectNoMorphious only. Where each trend reference lands. Accepted only as a plain object (JSON body) whose JSON is at most 4000 characters; anything else is silently dropped.
enhancement_modelstringNoTopaz Slow Motion only (topaz/interpolate/video): retiming engine — "Apollo", "Chronos" or "Aion". Aion is billed at its own, higher rate class.
target_fpsnumberNoTopaz Slow Motion only. Output frame rate, clamped to 16–120. Rates above 60 raise the billed length proportionally.
slowdown_factornumberNoTopaz Slow Motion only. How many times longer the output runs, 1–8. Billing is on the output length, so 8x costs 8x.
output_formatstringNoTopaz SDR-to-HDR only (topaz/sdr-to-hdr/video): "mp4" (10-bit H.265 HDR10, default) or "prores" (10-bit ProRes 422 HQ).
project_idstringNoProject to file the generation into. See Projects.
session_idstringNoAn existing video-to-video session you own to append this generation to. Omit and Kolbo picks the API session for the project.

You must provide video_url or a files upload. With neither, the request fails with 400 Provide video_url or upload a video file — this gate is unconditional, including for the model families that could otherwise run from reference inputs alone. Sending both is not rejected: the uploaded file wins and video_url is ignored.

Array and object fields (reference_images, reference_videos, elements, keyframes, vocabulary, customization, visual_dna_ids) may be sent as real JSON in a JSON body, or as JSON-encoded strings in a multipart/form-data request. A value that does not parse to a non-empty array/object is dropped silently. reference_map is only read from a JSON body.

Errors from the generation pipeline (prompt, keyframe and model-family validation) come back as { "success": false, "error": "…", "code": "…" }. When the pipeline sets no code of its own, code is GENERATION_ERROR — match on the HTTP status and the error text in that case.

The LTX inpaint variant cannot be driven from this endpoint. ltx-2.3-quality/inpaint requires a tracked mask video, and there is no request field to supply one — every call fails with 400 Inpaint requires a mask. Draw a box over the object first. Use one of the other video_to_video models, or the inpaint operation on POST /api/v1/edit/video, which does take a mask_video_url.

Motion Control (character image + driving video)

Motion-control models transfer the motion of a reference video onto a character image. They are matched by identifier: anything containing motion-control (such as the Kling v2.6 / v3 motion-control models), animate-move / animate/move (including wan/v2.2-14b/animate/move), wan-animate or dreamactor (including bytedance/dreamactor/v2), plus act_two. For these models the inputs are:

  • reference_images[0] — the character image (the subject that gets animated). Must be a Kolbo-hosted URL whose path has an image extension (.jpg, .jpeg, .png, .gif, .bmp, .webp, .tiff), otherwise 400 with INVALID_VIDEO_SOURCE or INVALID_CHARACTER_IMAGE_TYPE.
  • video_url — the driving / reference video whose motion is transferred (in the MCP tool this is the source_video argument). Its path must have a video extension (.mp4, .mov, .avi, .mkv, .webm, .m4v, .flv, .wmv), otherwise 400 / INVALID_REFERENCE_VIDEO_TYPE.

Omitting the character image, or sending the driving video as an uploaded files part instead of video_url, returns 400 with code MOTION_CONTROL_INPUTS before any credits are held. Send both as URLs (upload a local file to the Media Library first). The cost is sized from the driving video's length.

wan/v2.2-14b/animate/replace is not a motion-control model: it keeps the scene video in video_url and takes the character image as reference_images[0] (400 / MOTION_CONTROL_INPUTS when it is missing). prunaai/p-video-animate and prunaai/p-video-replace work the normal way: source video in video_url plus at least one reference_images entry (400 / MISSING_REFERENCE_IMAGE otherwise, TOO_MANY_REFERENCE_IMAGES above the model's cap).

curl -X POST https://api.kolbo.ai/api/v1/generate/video-from-video \
  -H "X-API-Key: kolbo_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "your-motion-control-model-identifier",
    "reference_images": ["https://media.kolbo.ai/images/character.jpg"],
    "video_url": "https://media.kolbo.ai/videos/dance-reference.mp4"
  }'

VEED Subtitles (prompt-less)

The VEED Subtitles model (veed/subtitles) burns styled, auto-transcribed subtitles into a video. It takes no prompt — pass a style preset and optional language settings instead. Billed per minute of input video with a 1-minute minimum.

curl -X POST https://api.kolbo.ai/api/v1/generate/video-from-video \
  -H "X-API-Key: kolbo_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "veed/subtitles",
    "video_url": "https://media.kolbo.ai/videos/talking-clip.mp4",
    "preset": "glass",
    "source_language": "en-US",
    "translation_language": "es-ES"
  }'

Input limits

There is no generic server-side check of max_images, max_videos, max_elements or the source-length fields on the model record. A few families validate their own inputs before any charge:

  • Seedance (seedance-2…): the source must be Kolbo-hosted and at least the model's min_video_duration long (400 / VIDEO_TOO_SHORT); the edit models take one model per request.
  • Wan 3.0 (wan3.0-video-to-video, wan3.0-prime-video-to-video): a source above max_video_duration returns 400 / VIDEO_TOO_LONG; a duration above the longest supported_durations entry returns 400 / DURATION_NOT_SUPPORTED.
  • FLUX 3 Edit Video (flux-3-edit-video): sources of 15 s or longer return 400 / VIDEO_TOO_LONG.
  • Pruna P-Video (prunaai/p-video-*): source length and reference-image count are checked (VIDEO_TOO_LONG, MISSING_REFERENCE_IMAGE, TOO_MANY_REFERENCE_IMAGES).
  • Morphious / Genjutsu: exactly one source video plus a mode-specific reference-image range, duration and resolution (400 / INVALID_MORPHIOUS_INPUT or INVALID_GENJUTSU_INPUT).

For every other model, validate against the model record yourself — over-sending fails at the provider mid-generation instead of returning a clean 400.

Billing

Most models are billed per second of the probed source video (motion-control models: the driving video), not the duration you request. Exceptions: Seedance edit and extend models add their extra input / requested output seconds on top of the source; VEED Subtitles bills a 60-second minimum; Luma Ray 3.2 bills the selected 5 s or 10 s output tier; Topaz Slow Motion bills the slowed-down output length; Runway Aleph 2.0 bills at least 2 seconds. Read the settled cost from credits_used on the completed status.

Examples

cURL with URL

First fetch identifiers from GET /api/v1/models?type=video_from_video, then pass the identifier value as model. The source video must already be on Kolbo storage:

curl -X POST https://api.kolbo.ai/api/v1/generate/video-from-video \
  -H "X-API-Key: kolbo_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "your-model-identifier",
    "prompt": "Transform into anime style with vibrant colors",
    "video_url": "https://media.kolbo.ai/videos/original-clip.mp4",
    "aspect_ratio": "16:9"
  }'

cURL with File Upload

curl -X POST https://api.kolbo.ai/api/v1/generate/video-from-video \
  -H "X-API-Key: kolbo_live_YOUR_API_KEY" \
  -F "model=your-model-identifier" \
  -F "prompt=Transform into anime style with vibrant colors" \
  -F "[email protected]"

Timestamp-Anchored Keyframes

Models that support keyframes let you pin specific images to specific moments in the output. The keyframe images must already live on Kolbo storage — upload them through the Media Library first.

Check keyframe capability on GET /api/v1/models first: supports_keyframes / max_keyframes report whether the model accepts keyframes and how many. Send keyframes to a model that does not support them and they are silently dropped along with source_fps / source_duration_seconds — the request still succeeds as a plain video-to-video, so verify with a small test generation before relying on them.

curl -X POST https://api.kolbo.ai/api/v1/generate/video-from-video \
  -H "X-API-Key: kolbo_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "your-keyframe-capable-model-identifier",
    "video_url": "https://media.kolbo.ai/videos/source.mp4",
    "prompt": "Match the reference frames",
    "source_fps": 24,
    "source_duration_seconds": 8,
    "keyframes": [
      { "image_url": "https://media.kolbo.ai/images/frame-a.png", "timestamp_seconds": 0 },
      { "image_url": "https://media.kolbo.ai/images/frame-b.png", "timestamp_seconds": 4.5 }
    ]
  }'

Model identifiers come from GET /api/v1/models?type=video_from_video. Always fetch the latest list rather than hardcoding identifiers, as models may change over time.

JavaScript

const API_KEY = "kolbo_live_YOUR_API_KEY";

// Fetch available video-to-video models
async function initModels() {
  const res = await fetch("https://api.kolbo.ai/api/v1/models?type=video_from_video", {
    headers: { "X-API-Key": API_KEY },
  });
  const data = await res.json();
  console.log("Available models:", data.models.map((m) => m.identifier));
}

async function main() {
  await initModels();

  const response = await fetch("https://api.kolbo.ai/api/v1/generate/video-from-video", {
    method: "POST",
    headers: {
      "X-API-Key": API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "your-model-identifier", // required — no auto-select on this endpoint
      prompt: "Transform into anime style with vibrant colors",
      video_url: "https://media.kolbo.ai/videos/original-clip.mp4",
      aspect_ratio: "16:9",
    }),
  });

  const started = await response.json();
  if (!started.success) throw new Error(started.error);

  const result = await pollUntilDone(started.generation_id, started.poll_interval_hint);
  if (result.state !== "completed") throw new Error(result.error || result.state);

  console.log("Video URL:", result.result.urls[0]); // urls is always an array
}

const TERMINAL = new Set(["completed", "failed", "cancelled"]);

// Minimal loop. A production client should also handle transient HTTP errors and
// an overall timeout — see /docs/developer-api/polling-and-cancellation.
async function pollUntilDone(generationId, intervalSeconds) {
  while (true) {
    await new Promise((r) => setTimeout(r, intervalSeconds * 1000));
    const status = await fetch(
      `https://api.kolbo.ai/api/v1/generate/${generationId}/status`,
      { headers: { "X-API-Key": API_KEY } }
    ).then((r) => r.json());
    if (TERMINAL.has(status.state)) return status;
  }
}

main();

Python

import requests
import time

API_KEY = "kolbo_live_YOUR_API_KEY"
BASE_URL = "https://api.kolbo.ai/api"
HEADERS = {"X-API-Key": API_KEY}

# Fetch available video-to-video models
models_res = requests.get(
    f"{BASE_URL}/v1/models",
    headers=HEADERS,
    params={"type": "video_from_video"},
)
print("Available models:", [m["identifier"] for m in models_res.json()["models"]])

# --- Option A: Using URL ---
response = requests.post(
    f"{BASE_URL}/v1/generate/video-from-video",
    headers={**HEADERS, "Content-Type": "application/json"},
    json={
        "model": "your-model-identifier",  # required — no auto-select on this endpoint
        "prompt": "Transform into anime style with vibrant colors",
        "video_url": "https://media.kolbo.ai/videos/original-clip.mp4",
        "aspect_ratio": "16:9",
    },
)

data = response.json()
print("Generation ID:", data["generation_id"])

# --- Option B: Using file upload ---
# with open("original-clip.mp4", "rb") as vid:
#     response = requests.post(
#         f"{BASE_URL}/v1/generate/video-from-video",
#         headers=HEADERS,
#         files={"files": vid},
#         data={
#             "model": "your-model-identifier",
#             "prompt": "Transform into anime style with vibrant colors",
#         },
#     )
#     data = response.json()

TERMINAL = {"completed", "failed", "cancelled"}

# Minimal loop. Add transient-error retries and an overall timeout for production
# — see /docs/developer-api/polling-and-cancellation.
while True:
    time.sleep(data["poll_interval_hint"])
    status = requests.get(
        f"{BASE_URL}/v1/generate/{data['generation_id']}/status",
        headers=HEADERS,
    ).json()
    if status["state"] in TERMINAL:
        break

if status["state"] != "completed":
    raise Exception(status.get("error", status["state"]))

print("Video URL:", status["result"]["urls"][0])  # urls is always a list

Response

This endpoint is asynchronous and fire-and-forget: the POST returns as soon as the job is queued and never contains a video.

Polling is the only completion mechanism. Kolbo never calls you back: there is no webhook, no callback_url field on this request body, and no route delivers outbound notifications. Socket.IO events are the web app's internal transport and are not part of the API contract — API-key generations are registered so the shared progress emitter drops their events, and the few emit sites that bypass that check are undocumented, unversioned and unsafe to build on. After the POST, loop against GET /api/v1/generate/{generation_id}/status until state is completed, failed or cancelled. Full contract: Polling & Cancellation.

Generation Started

{
  "success": true,
  "generation_id": "68f2c1a9b4e5d6f7a8b9c0d1",
  "type": "video_from_video",
  "model": "your-model-identifier",
  "credits_charged": null,
  "poll_url": "/v1/generate/68f2c1a9b4e5d6f7a8b9c0d1/status",
  "poll_interval_hint": 8,
  "session_id": "…",
  "project_id": "…"
}
FieldTypeNotes
generation_idstringMongo ObjectId. This is what you poll and cancel with.
typestringAlways "video_from_video"
modelstringThe identifier that was submitted, after alias resolution — this endpoint has no Smart Select, so model is required
credits_chargednullAlways null on this endpoint — the video-to-video pipeline reports no estimate at submit time. Read credits_used from the completed status.
poll_urlstringThe status path without the /api prefix. Prepend https://api.kolbo.ai/api.
poll_interval_hintnumberSuggested seconds between polls — 8 here
session_id / project_idstringWhere the generation lives in the Kolbo app

Completed Status

GET /api/v1/generate/{generation_id}/status

{
  "success": true,
  "generation_id": "68f2c1a9b4e5d6f7a8b9c0d1",
  "type": "video_from_video",
  "state": "completed",
  "progress": 100,
  "result": {
    "urls": ["https://media.kolbo.ai/videos/.../output.mp4"],
    "thumbnail_url": null,
    "duration": 5,
    "aspect_ratio": "16:9",
    "prompt_used": "Transform into anime style with vibrant colors",
    "model": "your-model-identifier",
    "created_at": "2026-04-12T10:00:00.000Z"
  },
  "credits_used": 50,
  "credits_breakdown": [
    { "model": "your-model-identifier", "amount": 50, "base": 50, "final": 50, "duration_multiplier": null, "pricing": null }
  ]
}

The output lives in result.urls — always an array of strings, even though this endpoint produces exactly one video. Read urls[0].

result (and credits_used / credits_breakdown) appear only under state: "completed"; a processing body has exactly five keys and no result at all. There is no partial or streaming read.

result.thumbnail_url is usually null here — video-to-video has no source still to fall back on, so it is populated only when the pipeline generated a poster frame. result.duration is the recorded output length when the pipeline stored one; otherwise it falls back to the duration you sent (stored as a string) or null.

Optional result keys, present only when they have data: model_name (display name of the model that ran), playback_urls (browser-playable companions to urls, null per clip when the master already plays everywhere), reference_images (the references the generation actually conditioned on, including injected Visual DNA stills), visual_dna ([{ id, name, thumbnail_url }] for the DNAs used) and style ({ id, name, cover, custom } for a Morphious style).

credits_used is the authoritative, multiplier-adjusted cost, not credits_charged from the submit response.

Failure and cancellation

A failed generation is still an HTTP 200 with success: true — the failure is in state, with an error string and a best-effort failure object. cancelled carries neither result nor error, so treat all three terminal states explicitly. Failed generations are not charged. See Polling & Cancellation.

Tips

  • Pick the model first. max_images, max_videos, max_elements, min_video_duration, and max_video_duration on the model record describe which extra inputs the model takes and what source length it is built for. Only some families enforce them (see Input limits), so validate against the model record yourself before submitting.
  • Upload source videos, character images and keyframe images to the Media Library first and pass the returned URLs — an external video_url, motion-control character image or keyframe image is rejected.
  • Video-to-video generation typically takes several minutes depending on the model and duration.
  • Be descriptive in your prompt — specify the target style, mood, and any visual changes you want applied.
  • When using file uploads, the limit is 100 MB per file.
  • Read the settled cost from credits_used on the completed status response — see Credits and Billing.
  • Use poll_interval_hint from the initial response to set your polling interval.
  • Check supported_durations and supported_aspect_ratios on each model via the Models endpoint before requesting specific values.