Create a video
/v1/videosQueues a video generation: 202 with a job id, or the finished result inline with wait: true.
Request
modelstringbodyrequiredGET /cabinet/api/public/media-models)promptstringbodyrequireddurationnumberbodysecondsnumberbodyduration; if both are sent, duration winsaspect_ratiostringbody= 16:9imagestring|arraybodyvideostring|arraybodyimagewaitbooleanbody= falsetrue — hold the connection and return the result, but no longer than 90 stimeoutnumberbody= 90waitref_video_job_idstringbodyscenesstring|arraybodyResponses
wait: true
{
"id": "vid_9f1c4a2b7e0d4f5a8c3b6d1e2f0a7b4c",
"status": "completed",
"created": 1757500800,
"duration": 5.0,
"cost_usd": "0.95",
"data": [
{ "url": "https://api.teamtoken.store/v1/videos/vid_9f1c4a2b7e0d4f5a8c3b6d1e2f0a7b4c/content" }
]
}Details
Needs a key in Authorization: Bearer or x-api-key.
Generation is asynchronous. By default the endpoint answers 202 with a job id and the size of the hold (estimated_cost_usd), and the result is collected via GET /v1/videos/{job_id}. With wait: true (or any timeout present) the gateway polls the provider itself — 2 s per poll, capped at 90 s; timeout can only shorten that budget, never raise it. If the budget runs out you get a 202 with the same job id and the job keeps running. Polling is optional: the reconciler (one pass every 150 s) finalizes a finished job on its own — charges it and stores the result links.
There are three input modes, chosen by the body rather than by the URL: prompt — text-to-video; prompt + image — image-to-video; prompt + video — video-to-video (edit and motion control, where image is the character and video the motion). What the engine does with an input is the engine's business; our own check is one — an engine that requires an input video answers 400 without one on our side, before the provider. The model list lives in the catalog (GET /cabinet/api/public/media-models), but the catalog does not serve the required inputs or the duration sets: those are a table on our side, visible from outside only through our error text.
/v1/video/generations, /v1/videos/extend and /v1/videos/storyboard are aliases of this same handler: the same body, the same responses and errors, no per-path behaviour at all. Extending a clip and storyboarding are selected by the model — separate engines in the catalog (*-extend, *-storyboard) — and the source clip is passed in ref_video_job_id.
About the fields. Length and its accepted set belong to the engine: seedance — 4–15 s (5 by default) · kling — 3–15 on 3.0, 3–10 on edit/o1/motion (5) · kling 2.5/2.6 — only 5 or 10 · kling 2.1 — exactly 10 or exactly 5, depending on the model · veo — 4/6/8 (8) · omni-flash — 4/6/8/10, though every catalog row pins one of them · grok — 6 only · extend — 8 on veo, 6 on grok, 4–15 on seedance · storyboard — 6–30 (6). A value outside the set answers 400 listing what is allowed (for a range, its bounds). ⚠️ A catalog row may pin the length outright (fixed_params), and then the duration you sent never reaches the provider. ⚠️ Omit duration and the provider gets the engine's default while the hold is sized at the engine's MAXIMUM (hold_duration_seconds); the charge follows the finished clip's actual length. timeout is clamped at the same 90 s, and a non-numeric value falls back to the same 90.
Inputs are accepted under several names: image is also images, image_url, image_urls, input_image, init_image, ref_images, and video is also videos, video_url, input_video, motion_video, ref_video, ref_videos. URLs — images and videos alike — are fetched by the gateway itself (own User-Agent, own size cap, internal addresses refused at every redirect hop); if the fetch fails or the body does not look like media, the link is forwarded as-is. ⚠️ An input the engine does not expect is still forwarded to the provider: the catalog is consulted for requiredness only. Parameters outside the engine's set (resolution, size, any unknown field) are dropped silently — resolution and tier come from the catalog, not from the request.
What answers 400 before the provider: broken JSON, an unknown or disabled model, a missing prompt, a duration outside the engine's set, a required input not sent, an input over 80 MB (the cap is per decoded file). ref_video_job_id is owner-scoped: someone else's id, or a missing one, answers 404 so a leaked id confirms nothing. A provider submit failure: its 4xx is echoed verbatim — with its code and its reason, scrubbed of upstream names — and that is final, the job is failed and the hold released; a 5xx or a network error means the job MAY have been accepted, and refunding on a guess would be a double credit — the job goes to unknown_submit and waits for the reconciler, which either completes it or releases the hold on TTL (unknown_submit has a short one, 15 minutes by default; everything else, a day).