Kling Image-to-Video (Audio-Visual Sync)
Generate high-quality videos from static images using Kling AI. Supports multiple model versions (kling-v1 through kling-v2-6), professional quality mode, camera control, motion brush trajectories, end-frame control, and voice synthesis. Videos can be 5s or 10s.
This is an async skill: submitting the task returns a task_id immediately; the video becomes available only after polling until task_status is succeed.
Read
references/create-task-api.mdfor the full submit-task specification and Edge Function code. Readreferences/query-task-api.mdfor the full query-task specification and Edge Function code.
End-to-End Workflow
Generation-time usage (Agent direct call)
Use this pattern when working directly as an agent (no application frontend needed).
const apiKey = process.env["INTEGRATIONS_API_KEY"]!; // platform_managed — injected by platform
// Step 1: Submit the image-to-video task
async function submitImage2VideoTask(params: {
image: string; // Required: Base64 string or accessible URL (jpg/jpeg/png, max 10MB)
prompt?: string; // Optional: text description, max 2500 chars
model_name?: string; // Optional: default "kling-v2-6"
mode?: "std" | "pro"; // Optional: default "pro"
duration?: "5" | "10"; // Optional: default "5"
image_tail?: string; // Optional: end-frame control image
negative_prompt?: string;
cfg_scale?: number; // [0,1], not supported in v2.x
voice_list?: { voice_id: string }[];
sound?: "on" | "off";
static_mask?: string; // Optional: static motion brush mask image (Base64 or URL)
camera_control?: {
type: "simple" | "down_back" | "forward_up" | "right_turn_forward" | "left_turn_forward";
config?: { horizontal?: number; vertical?: number; pan?: number; tilt?: number; roll?: number; zoom?: number };
};
external_task_id?: string;
callback_url?: string;
}): Promise<string> {
const response = await fetch("https://api-eLMlJj3KJD89@api-singapore.klingai.com/v1/videos/image2video", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Gateway-Authorization": `Bearer ${apiKey}`,
},
body: JSON.stringify(params),
});
if (!response.ok) throw new Error(`HTTP error: ${response.status}`);
const json = await response.json();
if (json.code !== 0) throw new Error(`API error ${json.code}: ${json.message}`);
return json.data.task_id;
}
// Step 2: Poll until the video is ready
async function pollUntilDone(taskId: string): Promise<{ id: string; url: string; duration: string }[]> {
const POLL_INTERVAL_MS = 7000;
const TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
const deadline = Date.now() + TIMEOUT_MS;
while (Date.now() < deadline) {
await new Promise(r => setTimeout(r, POLL_INTERVAL_MS));
const res = await fetch(
`https://api-rLobzpqX85m9@api-singapore.klingai.com/v1/videos/image2video/${taskId}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
"X-Gateway-Authorization": `Bearer ${apiKey}`,
},
}
);
if (!res.ok) throw new Error(`Poll HTTP error: ${res.status}`);
const json = await res.json();
if (json.code !== 0) throw new Error(`Poll API error ${json.code}: ${json.message}`);
const { task_status, task_status_msg, task_result } = json.data;
if (task_status === "succeed") return task_result.videos;
if (task_status === "failed") throw new Error(`Task failed: ${task_status_msg}`);
// submitted / processing — keep polling
}
throw new Error(`Task ${taskId} timed out after 10 minutes`);
}
// Full workflow
async function generateVideo(image: string, prompt?: string) {
const taskId = await submitImage2VideoTask({ image, prompt });
console.log(`Task submitted: ${taskId}`);
const videos = await pollUntilDone(taskId);
return videos; // [{ id, url, duration }]
}
Generation-time file download (required):
The video URL returned by the generation API is an ephemeral CDN link with a 30-day TTL. After obtaining the URL during generation (Agent direct call scenario), immediately use the Bash tool to download the file locally so the user can view the result.
curl -L -o ./output_video.mp4 "<generated video URL>"
Complete generation-time workflow (including download step):
- Call
submitImage2VideoTaskto obtain thetask_id - Call
pollUntilDoneto poll until status issucceedand obtain the video URL - Use the Bash tool to run
curl -L -o <local-path>.mp4 "<url>"to download the video locally - Inform the user of the file path where the video has been saved
Note: The upstream CDN link expires after 30 days; download immediately after obtaining the URL.
Post-generation usage (in-app via Edge Function)
For production apps, use two Edge Functions — one per endpoint — so the platform API key never reaches the browser. After the query Edge Function retrieves the video URL, transfer it to Supabase Storage for persistence.
Read
references/create-task-api.mdfor the complete submit Edge Function (edge-functions/kling-submit-image2video.ts). Readreferences/query-task-api.mdfor the complete query Edge Function (edge-functions/kling-query-image2video.ts) including Supabase Storage transfer.
Frontend polling loop:
// 1. Submit task via Edge Function
const { data: submitData, error: submitError } = await supabase.functions.invoke(
"kling-submit-image2video",
{ body: { image, prompt, model_name: "kling-v2-6", mode: "pro", duration: "5" } }
);
if (submitError) throw submitError;
const taskId: string = submitData.data.task_id;
// 2. Poll via Edge Function until done
const POLL_INTERVAL_MS = 7000;
const TIMEOUT_MS = 10 * 60 * 1000;
const deadline = Date.now() + TIMEOUT_MS;
while (Date.now() < deadline) {
await new Promise(r => setTimeout(r, POLL_INTERVAL_MS));
const { data: queryData, error: queryError } = await supabase.functions.invoke(
"kling-query-image2video",
{ body: { task_id: taskId } }
);
if (queryError) throw queryError;
const { task_status, task_status_msg, task_result } = queryData.data;
if (task_status === "succeed") {
// task_result.videos[].url is now a persistent Supabase Storage URL
return task_result.videos;
}
if (task_status === "failed") throw new Error(`Generation failed: ${task_status_msg}`);
}
throw new Error("Timed out waiting for video generation");
Parameter Summary
For full parameter tables, see references/create-task-api.md (submit) and references/query-task-api.md (query).
| Parameter | Type | Required | Description |
|---|---|---|---|
image |
string | Yes | Reference image (Base64 or URL), jpg/jpeg/png, ≤10MB, ≥300px, aspect ratio 1:2.5~2.5:1 |
prompt |
string | No | Positive text prompt, ≤2500 characters |
model_name |
string | No | Model version, default kling-v2-6 |
mode |
string | No | Generation mode: std / pro, default pro |
duration |
string | No | Video duration (seconds): 5 or 10, default 5 |
image_tail |
string | No | End-frame control image, same format requirements as image |
static_mask |
string | No | Static motion brush mask image (Base64 or URL); aspect ratio must match image |
camera_control |
object | No | Camera movement control; mutually exclusive with image_tail and dynamic_masks/static_mask |
camera_control.type |
string | No | Preset type: simple (custom) / down_back / forward_up / right_turn_forward / left_turn_forward |
camera_control.config.horizontal |
number | No | Left/right translation [-10, 10]; only when type=simple, only one config field may be non-zero |
camera_control.config.vertical |
number | No | Up/down translation [-10, 10] |
camera_control.config.pan |
number | No | Pitch [-10, 10] |
camera_control.config.tilt |
number | No | Yaw [-10, 10] |
camera_control.config.roll |
number | No | Roll [-10, 10] |
camera_control.config.zoom |
number | No | Focal length [-10, 10] |
Notes
- Key security:
INTEGRATIONS_API_KEYmust only be read server-side in Edge Functions; never expose it to the frontend. - Error handling: Always handle 429 (quota exceeded) and 402 (insufficient balance).
- Billing: The submit task endpoint (
api-eLMlJj3KJD89) has billing enabled — original price ¥100.80, discount price ¥84.00 (per 84 billing units). The query endpoint (api-rLobzpqX85m9) is free. Avoid re-submitting duplicate tasks to minimise unnecessary charges. - Mutually exclusive parameters:
image+image_tail,dynamic_masks/static_mask, andcamera_controlcannot be used simultaneously. - Base64 format: Do not include the
data:image/xxx;base64,prefix in Base64 image data; pass only the encoded string itself. - Mask images: The aspect ratio of a mask image must match the input
image; otherwise the task will fail. - Video expiry: Generated video CDN links are automatically cleared after 30 days — download or transfer to Supabase Storage promptly.