The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "Kie.ai - Image + Reference Video -> Motion + LipSync (URL-based, polling)",
"nodes": [
{
"id": "1",
"name": "Webhook (img2video)",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
200,
300
],
"parameters": {
"path": "img2video",
"httpMethod": "POST",
"responseMode": "responseNode",
"options": {}
}
},
{
"id": "2",
"name": "Set Defaults",
"type": "n8n-nodes-base.set",
"typeVersion": 2,
"position": [
420,
300
],
"parameters": {
"keepOnlySet": false,
"values": {
"string": [
{
"name": "imageUrl",
"value": "={{$json.imageUrl}}"
},
{
"name": "refVideoUrl",
"value": "={{$json.refVideoUrl}}"
}
],
"number": [
{
"name": "fps",
"value": "={{$json.fps ?? 25}}"
},
{
"name": "motionStrength",
"value": "={{$json.motionStrength ?? 0.8}}"
},
{
"name": "expressionStrength",
"value": "={{$json.expressionStrength ?? 0.7}}"
},
{
"name": "pollEverySeconds",
"value": "={{$json.pollEverySeconds ?? 10}}"
},
{
"name": "maxPollAttempts",
"value": "={{$json.maxPollAttempts ?? 30}}"
}
]
},
"options": {}
}
},
{
"id": "3",
"name": "IF Validate Inputs",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
640,
300
],
"parameters": {
"conditions": {
"string": [
{
"value1": "={{$json.imageUrl}}",
"operation": "isNotEmpty"
},
{
"value1": "={{$json.refVideoUrl}}",
"operation": "isNotEmpty"
}
]
}
}
},
{
"id": "4",
"name": "Create Motion Job (Kie)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
880,
220
],
"parameters": {
"method": "POST",
"url": "https://api.kie.ai/v1/video/motion-transfer",
"sendHeaders": true,
"headerParametersUi": {
"parameter": [
{
"name": "Authorization",
"value": "Bearer {{$env.KIE_API_KEY}}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"jsonParameters": true,
"sendBody": true,
"bodyParametersJson": "={{ JSON.stringify({ image_url: $json.imageUrl, driving_video_url: $json.refVideoUrl, fps: $json.fps, motion_strength: $json.motionStrength, expression_strength: $json.expressionStrength }) }}",
"options": {
"timeout": 300000
}
}
},
{
"id": "5",
"name": "Make Attempts (Motion)",
"type": "n8n-nodes-base.function",
"typeVersion": 2,
"position": [
1100,
220
],
"parameters": {
"functionCode": "const max = $json.maxPollAttempts ?? 30;\nreturn Array.from({ length: max }, (_, i) => ({ attempt: i + 1 }));"
}
},
{
"id": "6",
"name": "Split In Batches (Motion)",
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
1320,
220
],
"parameters": {
"batchSize": 1,
"options": {}
}
},
{
"id": "7",
"name": "Wait (Motion)",
"type": "n8n-nodes-base.wait",
"typeVersion": 1,
"position": [
1540,
220
],
"parameters": {
"amount": "={{$node[\"Set Defaults\"].json.pollEverySeconds}}",
"unit": "seconds"
}
},
{
"id": "8",
"name": "Get Motion Status (Kie)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
1760,
220
],
"parameters": {
"method": "GET",
"url": "=https://api.kie.ai/v1/jobs/{{$node[\"Create Motion Job (Kie)\"].json.jobId}}",
"sendHeaders": true,
"headerParametersUi": {
"parameter": [
{
"name": "Authorization",
"value": "Bearer {{$env.KIE_API_KEY}}"
}
]
},
"options": {
"timeout": 300000
}
}
},
{
"id": "9",
"name": "IF Motion Done?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1980,
220
],
"parameters": {
"conditions": {
"string": [
{
"value1": "={{$json.status}}",
"operation": "equals",
"value2": "succeeded"
}
]
}
}
},
{
"id": "10",
"name": "Set Motion Output",
"type": "n8n-nodes-base.set",
"typeVersion": 2,
"position": [
2200,
220
],
"parameters": {
"keepOnlySet": false,
"values": {
"string": [
{
"name": "motionVideoUrl",
"value": "={{$json.resultUrl}}"
},
{
"name": "imageUrl",
"value": "={{$node[\"Set Defaults\"].json.imageUrl}}"
},
{
"name": "refVideoUrl",
"value": "={{$node[\"Set Defaults\"].json.refVideoUrl}}"
}
]
},
"options": {}
}
},
{
"id": "11",
"name": "Create LipSync Job (Kie)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
2420,
220
],
"parameters": {
"method": "POST",
"url": "https://api.kie.ai/v1/video/lipsync",
"sendHeaders": true,
"headerParametersUi": {
"parameter": [
{
"name": "Authorization",
"value": "Bearer {{$env.KIE_API_KEY}}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"jsonParameters": true,
"sendBody": true,
"bodyParametersJson": "={{ JSON.stringify({ video_url: $json.motionVideoUrl, audio_from_video_url: $json.refVideoUrl, mouth_strength: 0.8, face_enhance: true }) }}",
"options": {
"timeout": 300000
}
}
},
{
"id": "12",
"name": "Make Attempts (Lip)",
"type": "n8n-nodes-base.function",
"typeVersion": 2,
"position": [
2640,
220
],
"parameters": {
"functionCode": "const max = $node[\"Set Defaults\"].json.maxPollAttempts ?? 30;\nreturn Array.from({ length: max }, (_, i) => ({ attempt: i + 1 }));"
}
},
{
"id": "13",
"name": "Split In Batches (Lip)",
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
2860,
220
],
"parameters": {
"batchSize": 1,
"options": {}
}
},
{
"id": "14",
"name": "Wait (Lip)",
"type": "n8n-nodes-base.wait",
"typeVersion": 1,
"position": [
3080,
220
],
"parameters": {
"amount": "={{$node[\"Set Defaults\"].json.pollEverySeconds}}",
"unit": "seconds"
}
},
{
"id": "15",
"name": "Get Lip Status (Kie)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
3300,
220
],
"parameters": {
"method": "GET",
"url": "=https://api.kie.ai/v1/jobs/{{$node[\"Create LipSync Job (Kie)\"].json.jobId}}",
"sendHeaders": true,
"headerParametersUi": {
"parameter": [
{
"name": "Authorization",
"value": "Bearer {{$env.KIE_API_KEY}}"
}
]
},
"options": {
"timeout": 300000
}
}
},
{
"id": "16",
"name": "IF Lip Done?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
3520,
220
],
"parameters": {
"conditions": {
"string": [
{
"value1": "={{$json.status}}",
"operation": "equals",
"value2": "succeeded"
}
]
}
}
},
{
"id": "17",
"name": "Set Final Output",
"type": "n8n-nodes-base.set",
"typeVersion": 2,
"position": [
3740,
220
],
"parameters": {
"keepOnlySet": false,
"values": {
"string": [
{
"name": "finalVideoUrl",
"value": "={{$json.resultUrl}}"
}
],
"stringArray": [],
"number": [],
"boolean": []
},
"options": {}
}
},
{
"id": "18",
"name": "Respond Success",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
3960,
220
],
"parameters": {
"responseCode": 200,
"responseBody": "={\n \"ok\": true,\n \"motionJobId\": $node[\"Create Motion Job (Kie)\"].json.jobId,\n \"lipJobId\": $node[\"Create LipSync Job (Kie)\"].json.jobId,\n \"finalVideoUrl\": $json.finalVideoUrl\n}"
}
},
{
"id": "19",
"name": "Set Error (Validation)",
"type": "n8n-nodes-base.set",
"typeVersion": 2,
"position": [
880,
420
],
"parameters": {
"keepOnlySet": true,
"values": {
"string": [
{
"name": "error",
"value": "Missing required fields: imageUrl and refVideoUrl"
}
],
"number": [
{
"name": "httpStatusCode",
"value": 400
}
]
},
"options": {}
}
},
{
"id": "20",
"name": "Set Error (Motion Timeout)",
"type": "n8n-nodes-base.set",
"typeVersion": 2,
"position": [
1540,
420
],
"parameters": {
"keepOnlySet": true,
"values": {
"string": [
{
"name": "error",
"value": "Motion job polling timed out (increase maxPollAttempts/pollEverySeconds)"
}
],
"number": [
{
"name": "httpStatusCode",
"value": 504
}
]
},
"options": {}
}
},
{
"id": "21",
"name": "Set Error (Lip Timeout)",
"type": "n8n-nodes-base.set",
"typeVersion": 2,
"position": [
3080,
420
],
"parameters": {
"keepOnlySet": true,
"values": {
"string": [
{
"name": "error",
"value": "Lip-sync job polling timed out (increase maxPollAttempts/pollEverySeconds)"
}
],
"number": [
{
"name": "httpStatusCode",
"value": 504
}
]
},
"options": {}
}
},
{
"id": "22",
"name": "Respond Error",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
3960,
420
],
"parameters": {
"responseCode": "={{$json.httpStatusCode ?? 500}}",
"responseBody": "={\n \"ok\": false,\n \"error\": $json.error,\n \"details\": $json\n}"
}
}
],
"connections": {
"Webhook (img2video)": {
"main": [
[
{
"node": "Set Defaults",
"type": "main",
"index": 0
}
]
]
},
"Set Defaults": {
"main": [
[
{
"node": "IF Validate Inputs",
"type": "main",
"index": 0
}
]
]
},
"IF Validate Inputs": {
"main": [
[
{
"node": "Create Motion Job (Kie)",
"type": "main",
"index": 0
}
],
[
{
"node": "Set Error (Validation)",
"type": "main",
"index": 0
}
]
]
},
"Set Error (Validation)": {
"main": [
[
{
"node": "Respond Error",
"type": "main",
"index": 0
}
]
]
},
"Create Motion Job (Kie)": {
"main": [
[
{
"node": "Make Attempts (Motion)",
"type": "main",
"index": 0
}
]
]
},
"Make Attempts (Motion)": {
"main": [
[
{
"node": "Split In Batches (Motion)",
"type": "main",
"index": 0
}
]
]
},
"Split In Batches (Motion)": {
"main": [
[
{
"node": "Wait (Motion)",
"type": "main",
"index": 0
}
],
[
{
"node": "Set Error (Motion Timeout)",
"type": "main",
"index": 0
}
]
]
},
"Wait (Motion)": {
"main": [
[
{
"node": "Get Motion Status (Kie)",
"type": "main",
"index": 0
}
]
]
},
"Get Motion Status (Kie)": {
"main": [
[
{
"node": "IF Motion Done?",
"type": "main",
"index": 0
}
]
]
},
"IF Motion Done?": {
"main": [
[
{
"node": "Set Motion Output",
"type": "main",
"index": 0
}
],
[
{
"node": "Split In Batches (Motion)",
"type": "main",
"index": 0
}
]
]
},
"Set Error (Motion Timeout)": {
"main": [
[
{
"node": "Respond Error",
"type": "main",
"index": 0
}
]
]
},
"Set Motion Output": {
"main": [
[
{
"node": "Create LipSync Job (Kie)",
"type": "main",
"index": 0
}
]
]
},
"Create LipSync Job (Kie)": {
"main": [
[
{
"node": "Make Attempts (Lip)",
"type": "main",
"index": 0
}
]
]
},
"Make Attempts (Lip)": {
"main": [
[
{
"node": "Split In Batches (Lip)",
"type": "main",
"index": 0
}
]
]
},
"Split In Batches (Lip)": {
"main": [
[
{
"node": "Wait (Lip)",
"type": "main",
"index": 0
}
],
[
{
"node": "Set Error (Lip Timeout)",
"type": "main",
"index": 0
}
]
]
},
"Wait (Lip)": {
"main": [
[
{
"node": "Get Lip Status (Kie)",
"type": "main",
"index": 0
}
]
]
},
"Get Lip Status (Kie)": {
"main": [
[
{
"node": "IF Lip Done?",
"type": "main",
"index": 0
}
]
]
},
"IF Lip Done?": {
"main": [
[
{
"node": "Set Final Output",
"type": "main",
"index": 0
}
],
[
{
"node": "Split In Batches (Lip)",
"type": "main",
"index": 0
}
]
]
},
"Set Error (Lip Timeout)": {
"main": [
[
{
"node": "Respond Error",
"type": "main",
"index": 0
}
]
]
},
"Set Final Output": {
"main": [
[
{
"node": "Respond Success",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionTimeout": 3600,
"saveExecutionProgress": true,
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "none",
"saveManualExecutions": true
},
"versionId": "b3d9f8b4-0d3f-4f54-9f34-9d55b4f3a4a1"
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Kie.ai - Image + Reference Video -> Motion + LipSync (URL-based, polling). Uses httpRequest. Webhook trigger; 22 nodes.
Source: https://gist.github.com/imatomicrohit-ux/e0e382dd8b7c9c43e35b1a9a149e5013 — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This n8n template provides enterprise-level version control for your workflows using GitHub integration. Stop losing hours to broken workflows and manual exports – get proper commit history, visual di
This flow creates dummy files for every item added in your *Arrs (Radarr/Sonarr) with the tag .
This workflow receives webhook requests from a content calendar and uses the X API v2 to publish text posts, threads, image/video posts, and polls, as well as delete existing posts and run a credentia
This workflow acts as a central API gateway for all technical indicator agents in the Binance Spot Market Quant AI system. It listens for incoming webhook requests and dynamically routes them to the c
Sign PDF documents with legally-compliant digital signatures using X.509 certificates. Supports multiple PAdES signature levels (B, T, LT, LTA) with optional visible stamps.