AutomationFlowsAI & RAG › Create AI Music Videos From a Prompt with Openai, Kie.ai and Beatapi

Create AI Music Videos From a Prompt with Openai, Kie.ai and Beatapi

ByEric @eric-kk on n8n.io

This workflow collects a music video theme via an n8n form, uses OpenAI to generate music, image, and video prompts, then calls Kie to create an instrumental track and a Nano Banana image, and finally uses BeatAPI to render a publish-ready AI music video. Receives a submission…

Event trigger★★★★☆ complexityAI-powered26 nodesHTTP RequestAgentForm TriggerOpenAI ChatOutput Parser Structured
AI & RAG Trigger: Event Nodes: 26 Complexity: ★★★★☆ AI nodes: yes Added:
Create AI Music Videos From a Prompt with Openai, Kie.ai and Beatapi — n8n workflow card showing HTTP Request, Agent, Form Trigger integration

This workflow corresponds to n8n.io template #16953 — we link there as the canonical source.

This workflow follows the Agent → Form Trigger recipe pattern — see all workflows that pair these two integrations.

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 →

Download .json
{
  "id": "1efAbq3gXPTEykr9",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Create AI music videos from a prompt with OpenAI, Kie and BeatAPI",
  "tags": [],
  "nodes": [
    {
      "id": "e9bd95a0-75f7-49db-be96-498ec9fe6186",
      "name": "Overview - Start Here",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3680,
        1216
      ],
      "parameters": {
        "color": 1,
        "width": 1360,
        "height": 500,
        "content": "# Create AI music videos from a prompt\n\nTurn a creative brief into an original instrumental track, a key image, and a publish-ready AI music video. This workflow uses OpenAI to plan production, Kie to generate audio and artwork, and BeatAPI to render the final video.\n\n### How it works\n1. The n8n form captures the theme, genre, mood, visual style, audience, duration, language, aspect ratio, resolution, and quality.\n2. OpenAI returns structured music, image, and video prompts plus publishing metadata.\n3. Kie generates the music and a Nano Banana image. Guarded wait/poll loops handle the asynchronous jobs and stop on failure or timeout.\n4. BeatAPI combines the audio and images, then returns the final video URL, title, description, tags, and provider task IDs.\n\n### Setup\n1. **Text API:** Create an OpenAI API key at [https://platform.openai.com/](https://platform.openai.com/) and add it to **OpenAI Chat Model**.\n2. **Image API:** Create a Kie API key at [https://kie.ai/](https://kie.ai/). Add Header Auth with `Authorization: Bearer <KIE_API_KEY>` and bind it to every Kie request.\n3. **AI MV API:** Create a BeatAPI API key at [https://beatapi.io/](https://beatapi.io/). Add Header Auth with `Authorization: Bearer <BEATAPI_API_KEY>` and bind it to both BeatAPI requests.\n4. Run **On form submission** in test mode, submit a brief, and inspect **Final Output**.\n\n### Customization\nConnect **Final Output** to YouTube, Postiz, Google Sheets, Airtable, or your publishing webhook."
      },
      "typeVersion": 1
    },
    {
      "id": "873ede43-70a3-4c90-b0d7-73deca3c8a4e",
      "name": "Section 1 - Creative Brief",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3680,
        1856
      ],
      "parameters": {
        "color": 7,
        "width": 752,
        "height": 560,
        "content": "## 1. Capture and plan the creative brief\nThe form collects the creative direction and output settings. OpenAI turns them into structured music, image, video, and publishing prompts."
      },
      "typeVersion": 1
    },
    {
      "id": "177cc789-be51-4d76-92a8-59210a3c13db",
      "name": "Section 2 - Music Generation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4512,
        1856
      ],
      "parameters": {
        "color": 7,
        "width": 1056,
        "height": 560,
        "content": "## 2. Generate and verify the music\nKie creates an instrumental track. The wait/poll loop checks progress, stops on provider errors, and enforces BeatAPI's 10\u2013180 second input range."
      },
      "typeVersion": 1
    },
    {
      "id": "22d158a9-988e-466a-96ab-684687c95581",
      "name": "Section 3 - Image Generation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5616,
        1856
      ],
      "parameters": {
        "color": 7,
        "width": 1056,
        "height": 560,
        "content": "## 3. Generate the key visual\nKie Nano Banana creates the cover image. The wait/poll loop retries until the image is ready or the configured maximum attempts is reached."
      },
      "typeVersion": 1
    },
    {
      "id": "5f27e273-1dd7-42c1-ad74-f3534f6e4db9",
      "name": "Section 4 - Video Render and Output",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6736,
        1856
      ],
      "parameters": {
        "color": 7,
        "width": 1360,
        "height": 560,
        "content": "## 4. Render the video and return the result\nBeatAPI combines the generated audio and images into a music video. The workflow polls until completion, then returns the video URL, publishing metadata, and provider task IDs."
      },
      "typeVersion": 1
    },
    {
      "id": "699509ce-8b76-475f-ae2d-943c0eb20622",
      "name": "Kie Create Music",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "stopWorkflow",
      "maxTries": 3,
      "position": [
        4528,
        2072
      ],
      "parameters": {
        "url": "https://api.kie.ai/api/v1/generate",
        "method": "POST",
        "options": {
          "timeout": 120000,
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "keypair",
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "prompt",
              "value": "={{ $json.kie_music_payload.prompt }}"
            },
            {
              "name": "customMode",
              "value": "={{ $json.kie_music_payload.customMode }}"
            },
            {
              "name": "instrumental",
              "value": "={{ $json.kie_music_payload.instrumental }}"
            },
            {
              "name": "model",
              "value": "={{ $json.kie_music_payload.model }}"
            },
            {
              "name": "style",
              "value": "={{ $json.kie_music_payload.style }}"
            },
            {
              "name": "title",
              "value": "={{ $json.kie_music_payload.title }}"
            },
            {
              "name": "negativeTags",
              "value": "={{ $json.kie_music_payload.negativeTags }}"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "f2ab3ff8-3be3-4bfd-bac5-f0be9a080e61",
      "name": "Wait 30s - Music",
      "type": "n8n-nodes-base.wait",
      "position": [
        4752,
        2072
      ],
      "parameters": {
        "amount": 30
      },
      "typeVersion": 1.1
    },
    {
      "id": "a2ae3403-038b-47b1-a2b2-da238e4da507",
      "name": "Kie Poll Music",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "stopWorkflow",
      "maxTries": 3,
      "position": [
        4976,
        2000
      ],
      "parameters": {
        "url": "=https://api.kie.ai/api/v1/generate/record-info?taskId={{ $json.kie_music_task_id || $json.data.taskId }}",
        "options": {
          "timeout": 120000,
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "f8b0c665-76ae-47e9-b0b7-76eb07d78024",
      "name": "Read Music Result",
      "type": "n8n-nodes-base.code",
      "position": [
        5200,
        2000
      ],
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst waitItem = $('Wait 30s - Music').itemMatching(0).json;\nconst base = waitItem.theme ? waitItem : $('Parse AI Agent Plan').itemMatching(0).json;\nconst taskId = waitItem.kie_music_task_id || waitItem.data?.taskId || $('Kie Create Music').itemMatching(0).json.data.taskId;\nif (!taskId) throw new Error('Kie music taskId is missing. Create response: ' + JSON.stringify($('Kie Create Music').itemMatching(0).json));\nconst attempt = Number(base.music_poll_attempt || 0) + 1;\nconst data = response?.data ?? {};\nconst status = data.status || 'PENDING';\nconst failed = ['CREATE_TASK_FAILED', 'GENERATE_AUDIO_FAILED', 'CALLBACK_EXCEPTION', 'SENSITIVE_WORD_ERROR'];\nif (failed.includes(status) || data.errorCode) {\n  throw new Error('Kie music failed: ' + (data.errorMessage || data.errorCode || status));\n}\nif (status !== 'SUCCESS') {\n  if (attempt >= base.max_music_polls) {\n    throw new Error('Kie music timed out after ' + attempt + ' polls. Last status: ' + status);\n  }\n  return [{ json: { ...base, kie_music_task_id: taskId, kie_music_status: status, music_poll_attempt: attempt, music_ready: false }, pairedItem: 0 }];\n}\nconst tracks = data?.response?.sunoData || [];\nconst track = tracks.find((item) => item.audioUrl) || tracks[0];\nif (!track?.audioUrl) throw new Error('Kie music succeeded but no audioUrl was returned: ' + JSON.stringify(response));\nconst duration = Number(track.duration || base.duration_seconds || 60);\nif (duration < 10 || duration > 180) {\n  throw new Error('Kie returned audio duration ' + duration + 's, but BeatAPI music-video input supports 10-180s. Regenerate a shorter track before sending to BeatAPI.');\n}\nreturn [{\n  json: {\n    ...base,\n    kie_music_task_id: taskId,\n    kie_music_status: status,\n    music_poll_attempt: attempt,\n    music_ready: true,\n    audio_url: track.audioUrl,\n    suno_audio_url: track.audioUrl,\n    suno_stream_audio_url: track.streamAudioUrl,\n    suno_cover_image_url: track.imageUrl,\n    suno_music_id: track.id,\n    suno_duration_seconds: duration,\n    suno_title: track.title || base.music_title,\n    suno_tags: track.tags,\n  },\n  pairedItem: 0,\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "cfe3b00d-6348-46f9-ac3d-c2810b7974b5",
      "name": "Music Ready?",
      "type": "n8n-nodes-base.if",
      "position": [
        5424,
        2072
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{$json.music_ready}}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "6f44a59f-81e2-47aa-9c41-e8f0eee17d0f",
      "name": "Kie Create Nano Banana Image",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "stopWorkflow",
      "maxTries": 3,
      "position": [
        5648,
        2072
      ],
      "parameters": {
        "url": "https://api.kie.ai/api/v1/jobs/createTask",
        "method": "POST",
        "options": {
          "timeout": 120000,
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "keypair",
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "model",
              "value": "={{ $json.kie_image_payload.model }}"
            },
            {
              "name": "input",
              "value": "={{ $json.kie_image_payload.input }}"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "19b0d132-97cc-4ca9-b1f5-6fde91a674dd",
      "name": "Wait 20s - Image",
      "type": "n8n-nodes-base.wait",
      "position": [
        5872,
        2072
      ],
      "parameters": {
        "amount": 20
      },
      "typeVersion": 1.1
    },
    {
      "id": "819788a6-14ec-42b1-b5fd-d04a97420249",
      "name": "Kie Poll Nano Banana Image",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "stopWorkflow",
      "maxTries": 3,
      "position": [
        6096,
        2000
      ],
      "parameters": {
        "url": "=https://api.kie.ai/api/v1/jobs/recordInfo?taskId={{ $json.kie_image_task_id || $json.data.taskId }}",
        "options": {
          "timeout": 120000,
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "98567780-2ba6-4674-ba22-db29edf60b13",
      "name": "Read Nano Banana Result",
      "type": "n8n-nodes-base.code",
      "position": [
        6320,
        2000
      ],
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst waitItem = $('Wait 20s - Image').itemMatching(0).json;\nconst base = waitItem.audio_url ? waitItem : $('Read Music Result').itemMatching(0).json;\nconst createResponse = $('Kie Create Nano Banana Image').itemMatching(0).json;\nconst taskId = waitItem.kie_image_task_id || waitItem.data?.taskId || createResponse.data?.taskId;\nif (!taskId) throw new Error('Kie Nano Banana taskId is missing. Create response: ' + JSON.stringify(createResponse));\nconst attempt = Number(base.image_poll_attempt || 0) + 1;\nconst data = response?.data ?? {};\nconst state = String(data.state || data.status || 'waiting').toLowerCase();\nif (state === 'fail' || data.failCode || data.errorCode) {\n  throw new Error('Kie Nano Banana failed: ' + (data.failMsg || data.failCode || data.errorMessage || data.errorCode || JSON.stringify(data)));\n}\nif (state !== 'success') {\n  if (attempt >= base.max_image_polls) {\n    throw new Error('Kie Nano Banana timed out after ' + attempt + ' polls. Last state: ' + state);\n  }\n  return [{ json: { ...base, kie_image_task_id: taskId, kie_image_status: state, image_poll_attempt: attempt, image_ready: false }, pairedItem: 0 }];\n}\nlet result = data.resultJson || data.response || {};\nif (typeof result === 'string') {\n  try {\n    result = JSON.parse(result);\n  } catch (error) {\n    throw new Error('Kie Nano Banana returned invalid resultJson: ' + result);\n  }\n}\nconst imageUrl = (Array.isArray(result.resultUrls) ? result.resultUrls[0] : undefined)\n  || (Array.isArray(result.result_urls) ? result.result_urls[0] : undefined)\n  || (Array.isArray(result.urls) ? result.urls[0] : undefined)\n  || (Array.isArray(result.images) ? result.images[0] : undefined)\n  || result.resultImageUrl\n  || result.result_url\n  || result.url;\nif (!imageUrl) throw new Error('Kie Nano Banana succeeded but no image URL was returned: ' + JSON.stringify(data));\nreturn [{ json: { ...base, kie_image_task_id: taskId, kie_image_status: state, image_poll_attempt: attempt, image_ready: true, image_url: imageUrl, kie_image_url: imageUrl }, pairedItem: 0 }];"
      },
      "typeVersion": 2
    },
    {
      "id": "21c5766b-dc31-4771-a58a-b8c790884612",
      "name": "Image Ready?",
      "type": "n8n-nodes-base.if",
      "position": [
        6544,
        2072
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{$json.image_ready}}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3bebb682-ed02-4bc1-bd1c-f76da08dabd2",
      "name": "BeatAPI Create AIMV",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "stopWorkflow",
      "maxTries": 3,
      "position": [
        6768,
        2072
      ],
      "parameters": {
        "url": "https://api.beatapi.io/v1/music-video/tasks",
        "method": "POST",
        "options": {
          "timeout": 120000,
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "keypair",
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "images",
              "value": "={{ [$json.image_url, $json.suno_cover_image_url].filter(Boolean) }}"
            },
            {
              "name": "audio_url",
              "value": "={{ $json.audio_url }}"
            },
            {
              "name": "prompt",
              "value": "={{ $json.mv_prompt }}"
            },
            {
              "name": "language",
              "value": "={{ $json.beatapi_language }}"
            },
            {
              "name": "aspect_ratio",
              "value": "={{ $json.aspect_ratio }}"
            },
            {
              "name": "resolution",
              "value": "={{ $json.resolution }}"
            },
            {
              "name": "quality",
              "value": "={{ $json.quality }}"
            },
            {
              "name": "compose_mode",
              "value": "auto"
            },
            {
              "name": "duration",
              "value": "={{ $json.suno_duration_seconds || $json.duration_seconds || 60 }}"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "44acf77c-8dd9-43a1-b3bc-9262c883edc7",
      "name": "Wait 10s - BeatAPI",
      "type": "n8n-nodes-base.wait",
      "position": [
        6992,
        2072
      ],
      "parameters": {
        "amount": 10
      },
      "typeVersion": 1.1
    },
    {
      "id": "a4c64273-6fd8-43d2-a8b2-9e35c7f90ac5",
      "name": "BeatAPI Poll AIMV",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "stopWorkflow",
      "maxTries": 3,
      "position": [
        7216,
        2000
      ],
      "parameters": {
        "url": "=https://api.beatapi.io/v1/tasks/{{ $json.beatapi_task_id || $json.data.id }}",
        "options": {
          "timeout": 120000,
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "0a875d8d-90ba-4636-a4fa-1999a6d77378",
      "name": "Read BeatAPI Result",
      "type": "n8n-nodes-base.code",
      "position": [
        7440,
        2000
      ],
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst waitItem = $('Wait 10s - BeatAPI').itemMatching(0).json;\nconst base = waitItem.image_url ? waitItem : $('Read Nano Banana Result').itemMatching(0).json;\nconst createResponse = $('BeatAPI Create AIMV').itemMatching(0).json;\nconst taskId = waitItem.beatapi_task_id || waitItem.data?.id || createResponse.data?.id;\nif (!taskId) throw new Error('BeatAPI task id is missing. Create response: ' + JSON.stringify(createResponse));\nconst images = [];\nfor (const url of [base.image_url, base.suno_cover_image_url]) {\n  if (typeof url === 'string' && url.startsWith('https://') && !images.includes(url)) images.push(url);\n}\nconst attempt = Number(base.beatapi_poll_attempt || 0) + 1;\nconst task = response?.data;\nif (!task) throw new Error('BeatAPI poll response missing data: ' + JSON.stringify(response));\nconst failedStatuses = ['failed', 'canceled', 'cancelled'];\nif (failedStatuses.includes(task.status)) {\n  throw new Error('BeatAPI AIMV failed: ' + (task.error_message || task.error_code || JSON.stringify(task)));\n}\nif (task.status !== 'succeeded') {\n  if (attempt >= base.max_aimv_polls) {\n    throw new Error('BeatAPI AIMV timed out after ' + attempt + ' polls. Last status: ' + task.status + ', stage: ' + (task.stage || 'unknown'));\n  }\n  return [{ json: { ...base, beatapi_images: images.slice(0, 7), beatapi_task_id: taskId, beatapi_status: task.status, beatapi_stage: task.stage, beatapi_poll_attempt: attempt, aimv_ready: false }, pairedItem: 0 }];\n}\nconst media = Array.isArray(task.output?.media) ? task.output.media : [];\nconst finalVideo = media.find((item) => item.type === 'video' && item.url) || media.find((item) => item.url);\nif (!finalVideo?.url) throw new Error('BeatAPI succeeded but output.media did not include a URL: ' + JSON.stringify(task.output));\nconst title = String(base.youtube_title || base.music_title || 'AI Music Video').slice(0, 100);\nconst description = String(base.youtube_description || base.mv_prompt || '').slice(0, 4900);\nconst tags = Array.isArray(base.tags) ? base.tags : [];\nreturn [{\n  json: {\n    ...base,\n    aimv_ready: true,\n    final_video_url: finalVideo.url,\n    beatapi_task_id: taskId,\n    beatapi_status: task.status,\n    beatapi_stage: task.stage,\n    beatapi_poll_attempt: attempt,\n    beatapi_images: images.slice(0, 7),\n    suno_audio_url: base.audio_url,\n    kie_image_url: base.image_url,\n    youtube_title: title,\n    youtube_description: description,\n    tags,\n    publish_ready: {\n      videoUrl: finalVideo.url,\n      title,\n      description,\n      tags,\n      suggestedPrivacyStatus: 'private'\n    },\n    provider_tasks: {\n      kie_music_task_id: base.kie_music_task_id,\n      kie_image_task_id: base.kie_image_task_id,\n      beatapi_task_id: taskId\n    },\n    full_result: {\n      beatapi_task: task,\n      output_media: media,\n      music: {\n        audio_url: base.audio_url,\n        stream_audio_url: base.suno_stream_audio_url,\n        cover_image_url: base.suno_cover_image_url,\n        duration_seconds: base.suno_duration_seconds\n      },\n      image: {\n        image_url: base.image_url\n      }\n    }\n  },\n  pairedItem: 0,\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "f5322e75-d7db-461b-b59e-07058cf5b85f",
      "name": "AIMV Ready?",
      "type": "n8n-nodes-base.if",
      "position": [
        7664,
        2072
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{$json.aimv_ready}}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ac71633d-2d72-4d4a-90d8-6b6fdac20917",
      "name": "Final Output",
      "type": "n8n-nodes-base.set",
      "position": [
        7888,
        2072
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "final-video-url",
              "name": "final_video_url",
              "type": "string",
              "value": "={{ $json.final_video_url }}"
            },
            {
              "id": "youtube-title",
              "name": "youtube_title",
              "type": "string",
              "value": "={{ $json.youtube_title }}"
            },
            {
              "id": "youtube-description",
              "name": "youtube_description",
              "type": "string",
              "value": "={{ $json.youtube_description }}"
            },
            {
              "id": "tags",
              "name": "tags",
              "type": "array",
              "value": "={{ $json.tags }}"
            },
            {
              "id": "publish-ready",
              "name": "publish_ready",
              "type": "object",
              "value": "={{ $json.publish_ready }}"
            },
            {
              "id": "provider-tasks",
              "name": "provider_tasks",
              "type": "object",
              "value": "={{ $json.provider_tasks }}"
            },
            {
              "id": "source-assets",
              "name": "source_assets",
              "type": "object",
              "value": "={{ { audio_url: $json.suno_audio_url, image_url: $json.kie_image_url, cover_image_url: $json.suno_cover_image_url } }}"
            },
            {
              "id": "full-result",
              "name": "full_result",
              "type": "object",
              "value": "={{ $json.full_result }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "fe991cfb-fc81-4b99-b450-23d35b217a2e",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        3952,
        2072
      ],
      "parameters": {
        "text": "=## AI Music Video Production Brief\n\nYou are creating a structured production plan for an automated AI music video workflow.\n\n### User Input\n- **Theme:** {{ $json.theme || $json.Theme || 'A cinematic neon city night drive with hopeful synthwave energy' }}\n- **Genre:** {{ $json.genre || $json.Genre || 'synthwave pop instrumental' }}\n- **Mood:** {{ $json.mood || $json.Mood || 'cinematic, uplifting, night city' }}\n- **Visual style:** {{ $json.visual_style || $json['Visual style'] || 'cinematic neon cyberpunk, high contrast, clean subject, premium music video cover art' }}\n- **Target audience:** {{ $json.audience || $json.Audience || 'YouTube Shorts, TikTok, Reels' }}\n- **Language:** {{ $json.language || $json.Language || 'en' }}\n- **Target duration:** {{ $json.duration_seconds || $json['Duration seconds'] || $json.duration || 60 }} seconds\n- **Aspect ratio:** {{ $json.aspect_ratio || $json['Aspect ratio'] || '9:16' }}\n\n### What To Generate\nCreate one production-ready plan with:\n\n1. **Suno music prompt**\n   - Short, specific, and suitable for generating an instrumental track.\n   - No copyrighted artist names, existing song titles, celebrity voices, or brand names.\n\n2. **Music style and title**\n   - A concise title.\n   - A style description that helps Suno generate a clean instrumental result.\n\n3. **Nano Banana image prompt**\n   - English only.\n   - No text, captions, logos, signatures, or watermarks.\n   - Strong visual subject, cinematic composition, and music-video-ready key art.\n\n4. **BeatAPI AIMV script / prompt**\n   - Describe camera motion, pacing, lighting, cuts, visual rhythm, and progression.\n   - Make it suitable for turning the generated image and music into a polished AI music video.\n\n5. **Publishing metadata**\n   - YouTube-ready title.\n   - YouTube-ready description.\n   - 5 to 12 tags.\n\n### Output Rules\nReturn only the structured object required by the connected Structured Output Parser.\nDo not include markdown in the final answer.\nDo not add fields outside the schema.",
        "options": {
          "systemMessage": "You are an AI music video producer and prompt engineer.\n\nCreate compact, production-safe plans for automated AI music video workflows.\nAvoid copyrighted artists, brands, celebrities, existing song titles, text overlays, captions, logos, signatures, and watermarks.\nFollow the connected Structured Output Parser exactly."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 3
    },
    {
      "id": "9902ad41-72cf-4bc6-b0b1-1bff9a435fc2",
      "name": "On form submission",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        3728,
        2072
      ],
      "parameters": {
        "options": {
          "path": "beatapi-ai-mv",
          "ignoreBots": true,
          "buttonLabel": "Generate AI MV"
        },
        "formTitle": "Generate an AI Music Video from a Prompt",
        "formFields": {
          "values": [
            {
              "fieldName": "theme",
              "fieldType": "textarea",
              "fieldLabel": "Theme",
              "placeholder": "A cinematic neon city night drive with hopeful synthwave energy",
              "requiredField": true
            },
            {
              "fieldName": "genre",
              "fieldType": "text",
              "fieldLabel": "Genre",
              "placeholder": "synthwave pop instrumental"
            },
            {
              "fieldName": "mood",
              "fieldType": "text",
              "fieldLabel": "Mood",
              "placeholder": "cinematic, uplifting, night city"
            },
            {
              "fieldName": "visual_style",
              "fieldType": "textarea",
              "fieldLabel": "Visual style",
              "placeholder": "cinematic neon cyberpunk, high contrast, clean subject, premium music video cover art"
            },
            {
              "fieldName": "audience",
              "fieldType": "text",
              "fieldLabel": "Audience",
              "placeholder": "YouTube Shorts, TikTok, Reels"
            },
            {
              "fieldName": "duration_seconds",
              "fieldType": "number",
              "fieldLabel": "Duration seconds",
              "placeholder": "60"
            },
            {
              "fieldName": "language",
              "fieldType": "text",
              "fieldLabel": "Language",
              "placeholder": "en or zh"
            },
            {
              "fieldName": "aspect_ratio",
              "fieldType": "text",
              "fieldLabel": "Aspect ratio",
              "placeholder": "9:16, 16:9, 1:1, 4:3, or 3:4"
            },
            {
              "fieldName": "resolution",
              "fieldType": "text",
              "fieldLabel": "Resolution",
              "placeholder": "720p or 1080p"
            },
            {
              "fieldName": "quality",
              "fieldType": "text",
              "fieldLabel": "Quality",
              "placeholder": "standard or high"
            }
          ]
        },
        "responseMode": "onReceived",
        "formDescription": "Submit a theme. The AI Agent writes the music prompt, image prompt, and video script; the workflow creates the assets and returns a publish-ready AI music video."
      },
      "typeVersion": 2.3
    },
    {
      "id": "adaddcb1-2579-4d79-a548-15441adf765f",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        3920,
        2288
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini",
          "cachedResultName": "gpt-4.1-mini"
        },
        "options": {
          "temperature": 0.7
        },
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "055b0f70-8772-48a2-b149-1dbcd1a12ae1",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        4088,
        2296
      ],
      "parameters": {
        "autoFix": false,
        "schemaType": "fromJson",
        "jsonSchemaExample": "{\n  \"suno_prompt\": \"Short instrumental synthwave track for a cinematic neon city night drive, hopeful and energetic.\",\n  \"music_style\": \"synthwave pop instrumental, cinematic drums, pulsing bass, bright analog leads, no vocals\",\n  \"music_title\": \"Neon Night Drive\",\n  \"image_prompt\": \"Cinematic neon city night drive, glossy wet streets, futuristic skyline, dynamic perspective, high contrast, no text, no watermark\",\n  \"mv_prompt\": \"Create a rhythmic AI music video with forward camera motion, neon reflections, pulse-synced cuts, cinematic lighting, and rising visual energy.\",\n  \"youtube_title\": \"Neon Night Drive - AI Music Video\",\n  \"youtube_description\": \"A cinematic AI-generated music video built from an original instrumental track and neon city visuals.\",\n  \"tags\": [\n    \"AI music video\",\n    \"synthwave\",\n    \"AI video\",\n    \"neon city\",\n    \"BeatAPI\"\n  ]\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "parse-ai-agent-plan-bapi",
      "name": "Parse AI Agent Plan",
      "type": "n8n-nodes-base.code",
      "position": [
        4304,
        2072
      ],
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst src = $('On form submission').itemMatching(0).json;\nconst pick = (...keys) => {\n  for (const key of keys) {\n    const value = src[key];\n    if (value !== undefined && value !== null && String(value).trim() !== '') return String(value).trim();\n  }\n  return '';\n};\nconst numberInRange = (value, fallback, min, max) => {\n  const n = Number(value);\n  if (!Number.isFinite(n)) return fallback;\n  return Math.max(min, Math.min(max, Math.round(n)));\n};\nconst enumValue = (value, allowed, fallback) => allowed.includes(value) ? value : fallback;\nconst input = {\n  source: 'n8n-form',\n  theme: pick('theme', 'Theme') || 'A cinematic neon city night drive with hopeful synthwave energy',\n  genre: pick('genre', 'Genre') || 'synthwave pop instrumental',\n  mood: pick('mood', 'Mood') || 'cinematic, uplifting, night city',\n  visual_style: pick('visual_style', 'Visual style') || 'cinematic neon cyberpunk, high contrast, clean subject, premium music video cover art',\n  audience: pick('audience', 'Audience') || 'YouTube Shorts, TikTok, Reels',\n  duration_seconds: numberInRange(pick('duration_seconds', 'Duration seconds', 'duration'), 60, 10, 180),\n  language: enumValue(pick('language', 'Language').toLowerCase(), ['en', 'zh'], 'en'),\n  aspect_ratio: enumValue(pick('aspect_ratio', 'Aspect ratio'), ['9:16', '16:9', '1:1', '4:3', '3:4'], '9:16'),\n  resolution: enumValue(pick('resolution', 'Resolution'), ['540p', '720p', '1080p'], '720p'),\n  quality: enumValue(pick('quality', 'Quality'), ['standard', 'high'], 'standard'),\n  kie_music_model: pick('kie_music_model') || 'V5',\n  kie_image_model: pick('kie_image_model') || 'google/nano-banana',\n  max_music_polls: numberInRange(pick('max_music_polls'), 30, 1, 120),\n  max_image_polls: numberInRange(pick('max_image_polls'), 18, 1, 120),\n  max_aimv_polls: numberInRange(pick('max_aimv_polls'), 90, 1, 240),\n};\ninput.beatapi_language = input.language;\ninput.image_aspect_ratio = input.aspect_ratio;\nlet plan = response.output ?? response;\nif (typeof plan === 'string') {\n  try {\n    plan = JSON.parse(plan);\n  } catch (error) {\n    throw new Error('AI Agent did not return valid JSON. Raw output: ' + plan);\n  }\n}\nif (!plan || typeof plan !== 'object') {\n  throw new Error('AI Agent output is missing structured plan: ' + JSON.stringify(response));\n}\nconst trim = (value, max, fallback = '') => String(value || fallback).trim().slice(0, max);\nconst tags = Array.isArray(plan.tags) ? plan.tags.map((tag) => String(tag).trim()).filter(Boolean).slice(0, 12) : [];\nconst sunoPrompt = trim(plan.suno_prompt, 480, input.theme + ', short instrumental track');\nconst musicStyle = trim(plan.music_style, 900, input.genre);\nconst musicTitle = trim(plan.music_title, 80, 'BeatAPI AI MV');\nconst imagePrompt = trim(plan.image_prompt, 1800, input.visual_style) + ', no text, no captions, no watermark, clean composition, high quality music video key art';\nconst mvPrompt = trim(plan.mv_prompt, 3000, input.theme + ', cinematic AI music video with rhythmic camera motion');\nreturn [{\n  json: {\n    ...input,\n    ...plan,\n    tags,\n    suno_prompt: sunoPrompt,\n    music_style: musicStyle,\n    music_title: musicTitle,\n    image_prompt: imagePrompt,\n    mv_prompt: mvPrompt,\n    kie_music_payload: {\n      prompt: sunoPrompt + '. Target length around ' + input.duration_seconds + ' seconds.',\n      customMode: true,\n      instrumental: true,\n      model: input.kie_music_model,\n      style: musicStyle,\n      title: musicTitle,\n      negativeTags: 'copyrighted artist names, existing song titles, celebrity voices, speech-only audio, low quality, harsh noise'\n    },\n    kie_image_payload: {\n      model: input.kie_image_model,\n      input: {\n        prompt: imagePrompt,\n        output_format: 'png',\n        aspect_ratio: input.image_aspect_ratio\n      }\n    }\n  },\n  pairedItem: 0,\n}];"
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "updatedAt": "2026-07-10T00:00:00.000Z",
  "versionId": "bapi-aimv-production-n8n-agent-2026-07-10",
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Parse AI Agent Plan",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AIMV Ready?": {
      "main": [
        [
          {
            "node": "Final Output",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait 10s - BeatAPI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Image Ready?": {
      "main": [
        [
          {
            "node": "BeatAPI Create AIMV",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait 20s - Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Music Ready?": {
      "main": [
        [
          {
            "node": "Kie Create Nano Banana Image",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait 30s - Music",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Kie Poll Music": {
      "main": [
        [
          {
            "node": "Read Music Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Kie Create Music": {
      "main": [
        [
          {
            "node": "Wait 30s - Music",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 20s - Image": {
      "main": [
        [
          {
            "node": "Kie Poll Nano Banana Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 30s - Music": {
      "main": [
        [
          {
            "node": "Kie Poll Music",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "BeatAPI Poll AIMV": {
      "main": [
        [
          {
            "node": "Read BeatAPI Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Read Music Result": {
      "main": [
        [
          {
            "node": "Music Ready?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "On form submission": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 10s - BeatAPI": {
      "main": [
        [
          {
            "node": "BeatAPI Poll AIMV",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "BeatAPI Create AIMV": {
      "main": [
        [
          {
            "node": "Wait 10s - BeatAPI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse AI Agent Plan": {
      "main": [
        [
          {
            "node": "Kie Create Music",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read BeatAPI Result": {
      "main": [
        [
          {
            "node": "AIMV Ready?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Nano Banana Result": {
      "main": [
        [
          {
            "node": "Image Ready?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "AI Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Kie Poll Nano Banana Image": {
      "main": [
        [
          {
            "node": "Read Nano Banana Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Kie Create Nano Banana Image": {
      "main": [
        [
          {
            "node": "Wait 20s - Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow collects a music video theme via an n8n form, uses OpenAI to generate music, image, and video prompts, then calls Kie to create an instrumental track and a Nano Banana image, and finally uses BeatAPI to render a publish-ready AI music video. Receives a submission…

Source: https://n8n.io/workflows/16953/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

🎯 Create viral TikToks, Shorts, Reels, podcasts, and ASMR videos in minutes — all on autopilot.

OpenAI, HTTP Request, Form Trigger +7
AI & RAG

Digistars - Scrape & Crawl. Uses httpRequest, n8n-nodes-firecrawl-scraper, googleSheets, lmChatOpenAi. Event-driven trigger; 63 nodes.

HTTP Request, N8N Nodes Firecrawl Scraper, Google Sheets +5
AI & RAG

22-Automate_Multi_Platform_Social_Media_Content_Creation. Uses outputParserStructured, lmChatGoogleGemini, lmChatOpenAi, httpRequest. Event-driven trigger; 57 nodes.

Output Parser Structured, Google Gemini Chat, OpenAI Chat +11
AI & RAG

PixelSensei(ZH). Uses agent, outputParserStructured, formTrigger, lmChatOpenAi. Event-driven trigger; 55 nodes.

Agent, Output Parser Structured, Form Trigger +4
AI & RAG

🧠 Automate end-to-end SEO blog creation and WordPress publishing using a GPT-5 multi-agent workflow with real-time research, metadata generation, and optional featured images.

Output Parser Structured, HTTP Request, OpenAI +10