AutomationFlowsAI & RAG › Generate UGC Video Ads from Product Images

Generate UGC Video Ads from Product Images

Original n8n title: Generate Ugc Video Ads From Product Images with Kie.ai Sora 2 + Tavily Research

ByEzema Kingsley Chibuzo @kingsley on n8n.io

This n8n workflow automatically generates 10-second UGC-style portrait video ads for any product — entirely powered by AI. Simply provide your Product Name, Prompt or Idea, and Image Link in Google Sheets, and the system will research your product, craft a modern video prompt,…

Event trigger★★★★☆ complexityAI-powered20 nodesAgentGoogle SheetsOpenAI ChatHTTP Request ToolHTTP Request
AI & RAG Trigger: Event Nodes: 20 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Agent → Google Sheets 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": "gSzbjuHl8OMQZKJw",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Sora 2 UGC Video Generator",
  "tags": [],
  "nodes": [
    {
      "id": "cb38c489-7d45-4ac9-b003-73a08c0117e2",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        0,
        512
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "cb306801-963d-44b1-9387-ee69e96740cc",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        1024,
        512
      ],
      "parameters": {
        "amount": 15
      },
      "typeVersion": 1.1
    },
    {
      "id": "e0ae6f4c-bc54-4f11-a44a-5285d2edb429",
      "name": "Edit Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        1696,
        416
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "4ad4da7c-1909-4bf7-8fc0-af5b4e26574b",
              "name": "video_link",
              "type": "string",
              "value": "={{ JSON.parse($json.data.resultJson).resultUrls[0] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "764c6ae0-2ca4-433a-9261-b791b009ba2a",
      "name": "Switch",
      "type": "n8n-nodes-base.switch",
      "position": [
        1472,
        496
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Success",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "a7093dff-9cdc-46d4-9db7-0a88359fd314",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.data.state }}",
                    "rightValue": "success"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Error",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "3e394160-ef58-438a-b865-0220a29af8b9",
                    "operator": {
                      "type": "number",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.code }}",
                    "rightValue": 500
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        }
      },
      "typeVersion": 3.3
    },
    {
      "id": "d920b33d-c6ce-4e5e-ba8d-80f7f3870ead",
      "name": "Error Message",
      "type": "n8n-nodes-base.set",
      "position": [
        1696,
        608
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "3765855e-a8df-401a-8e78-548f48b7650f",
              "name": "error_message",
              "type": "string",
              "value": "={{ $('Grab Ffff').item.json.msg }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "4f4c54da-1c0d-42ae-b861-b8269d729614",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        448,
        512
      ],
      "parameters": {
        "text": "=Here is the prompt: {{ $json.Prompt }}",
        "options": {
          "systemMessage": "=You are an expert AI video prompt engineer trained to design optimized prompts for Sora 2 video generation on Kie.ai.\nYour role is to take a raw product concept, a short idea, and an image link and transform them into a highly detailed, realistic, and visually rich video prompt that aligns with modern marketing standards.\n\n\n\ud83c\udfaf Objective\nGenerate a prompt that helps Sora 2 produce a realistic, natural-looking short video matching the intent of the product or service.\nThe result should look like it was filmed by a real person or a professional ad team \u2014 depending on the desired style.\n\n\n\ud83e\udde9 Your Rules\n1. Before generating the video prompt, always use the Tavily tool to search for the latest trends, product type, related products or categories, and current market insights of the product or product category. Use this information to ensure the video prompt reflects the most current and effective styles in video advertising, avoiding outdated approaches.\n2. Understand the product \u2014 analyze what it is, who it\u2019s for, and its emotional appeal (use any research insight provided).\n3. Always describe:\n- Main subject(s): appearance, clothing, expression, action.\n- Setting: location, lighting, time of day, atmosphere.\n- Camera style: framing, motion, handheld vs cinematic, lens type, realism.\n- Tone: emotional mood (friendly, elegant, cinematic, testimonial, etc.).\n- Lighting: realistic natural or studio lighting, with mood consistency.\n4. If the user specifies \u201cUGC\u201d or \u201cinfluencer-style\u201d, simulate a smartphone or handheld camera perspective, vertical 9:16 format, and a natural, authentic tone.\n5. If no style is specified, default to a cinematic ad style that looks visually stunning and brand-ready.\n6. Avoid repetition, keep every sentence meaningful, and ensure the description reads like a cinematographer describing the shot to a director."
        },
        "promptType": "define"
      },
      "typeVersion": 2.2
    },
    {
      "id": "12397d56-63df-4660-971b-d1582dd54638",
      "name": "Get row(s) in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        224,
        512
      ],
      "parameters": {
        "options": {
          "returnFirstMatch": true
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "Processed"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/YOUR_AWS_SECRET_KEY_HERE-46kA6gc91yvuDOmqSU/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "14QQpQgyYKWMqWVS7HRMPWsxl-46kA6gc91yvuDOmqSU",
          "cachedResultUrl": "https://docs.google.com/YOUR_AWS_SECRET_KEY_HERE-46kA6gc91yvuDOmqSU/edit?usp=drivesdk",
          "cachedResultName": "Sora 2"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "abfed898-fc53-4c35-b92a-8bed78e9e604",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        464,
        736
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "5c9e70b6-4416-4641-b9c6-b22749e53a84",
      "name": "Tavily",
      "type": "n8n-nodes-base.httpRequestTool",
      "position": [
        592,
        736
      ],
      "parameters": {
        "url": "https://api.tavily.com/search",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"query\": \"{{ \"What is \" + $json[\"Product Name\"] + \" used for and what emotions or settings fit its video marketing?\" }}\",\n  \"topic\": \"general\",\n  \"search_depth\": \"advanced\",\n  \"chunks_per_source\": 3,\n  \"max_results\": 1,\n  \"time_range\": null,\n  \"days\": 30,\n  \"include_answer\": true,\n  \"include_raw_content\": true,\n  \"include_images\": false,\n  \"include_image_descriptions\": false,\n  \"include_domains\": [],\n  \"exclude_domains\": []\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "6beba205-b322-40f3-bff9-80a17a164e28",
      "name": "Append or update row in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1920,
        416
      ],
      "parameters": {
        "columns": {
          "value": {
            "Prompt": "={{ $('Get row(s) in sheet').item.json.Prompt }}",
            "Processed": "Yes",
            "Video Link": "={{ $json.video_link }}"
          },
          "schema": [
            {
              "id": "ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Product Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Product Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Prompt",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Prompt",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Image Link",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Image Link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Video Link",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Video Link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Processed",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Processed",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Prompt"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/YOUR_AWS_SECRET_KEY_HERE-46kA6gc91yvuDOmqSU/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "14QQpQgyYKWMqWVS7HRMPWsxl-46kA6gc91yvuDOmqSU",
          "cachedResultUrl": "https://docs.google.com/YOUR_AWS_SECRET_KEY_HERE-46kA6gc91yvuDOmqSU/edit?usp=drivesdk",
          "cachedResultName": "Sora 2"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "7520e83f-69ea-4b9f-b3df-18019fdf6be9",
      "name": "Create Video",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        800,
        512
      ],
      "parameters": {
        "url": "https://api.kie.ai/api/v1/jobs/createTask",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"model\": \"sora-2-image-to-video\",\n  \"input\": {\n    \"prompt\": \"{{ $json.output.replace(/\\r?\\n|\\r/g, ' ').replace(/\"/g, '\\\\\"') }}\",\n    \"image_urls\": [\n      \"{{\n  $('Get row(s) in sheet').item.json['Image Link'].match(/\\/d\\/(.*?)\\//)\n    ? 'https://drive.google.com/uc?export=download&id=' + $('Get row(s) in sheet').item.json['Image Link'].match(/\\/d\\/(.*?)\\//)[1]\n    : $('Get row(s) in sheet').item.json['Image Link']\n}}\"\n    ],\n    \"aspect_ratio\": \"portrait\",\n    \"n_frames\": \"10\",\n    \"remove_watermark\": true\n  }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "e03fe823-fe4c-4a9f-9bb7-2a7d893964df",
      "name": "Grab Video",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1248,
        448
      ],
      "parameters": {
        "url": "https://api.kie.ai/api/v1/jobs/recordInfo",
        "options": {},
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "taskId",
              "value": "={{ $json.data.taskId }}"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "e0c97b3a-3a5e-4643-8a44-05376a2cea43",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -864,
        592
      ],
      "parameters": {
        "width": 688,
        "height": 528,
        "content": "## How to set it up\n1. Open **n8n** (Cloud or Self-Hosted).\n2. Import the workflow file: Sora 2 UGC Video Generator.json.\n3. Create and connect these credentials:\n   - Google Sheets OAuth 2.0\n   - Tavily Search API (Header Auth)\n   - OpenAI API Key\n   - Kie.ai Sora 2 API (Header Auth)\n4. Update the Google Sheets link inside the nodes to your own sheet.\n5. Ensure the sheet columns include:\n`ID | Product Name | Prompt | Image Link | Video Link | Processed`\n6. Click **Execute Workflow** to begin generating your first ad video.\n\n### Header Auth Setup\nSetup your header Auth for the Sora 2 and Tavily http requests.\n`Authentication \u2192 Generic Credential Type \u2192 Header Auth \u2192 Create new credential `\n- Name: \u201cAuthorization\u201d\n- Value: \u201cBearer YOUR_TOKEN_HERE\u201d\n\n\n### Need help or Setup: [Email Me](mailto:buzanalytics@gmail.com)\n### Thank you."
      },
      "typeVersion": 1
    },
    {
      "id": "2f8f8977-99da-43f8-9f65-52afed1c7f4e",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        384,
        320
      ],
      "parameters": {
        "color": 5,
        "width": 336,
        "height": 560,
        "content": "## Prompt Modification\nGood prompting is very important when using this system. This is because the outcome of your video depends on your prompt. As a result, you are welcome to modify or change the AI Agent **system prompt** to suit your business goals."
      },
      "typeVersion": 1
    },
    {
      "id": "9e62325a-f2f4-4094-828c-d0c9865651cf",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -864,
        -16
      ],
      "parameters": {
        "width": 688,
        "height": 592,
        "content": "## \u2699\ufe0f How It Works\n1. **Manual Trigger**\nExecute this to start the workflow.\n2. **Fetch Info from Google Sheets CRM**\nThis gets product information from an unprocessed row.\n3. **AI Agent**\nThis acts as the **UGC-style** video prompt generator.  With the provided agent system prompt, it describes the scene, tone, lighting, camera motion, and emotion of the video ads. **Tavily** tool is added to help it research trends and related product insights. \n4. **Create Video (Sora 2 Kie.ai API)**\nThe refined video prompt and product image are sent to Kie.ai Sora 2 to create a 10-second portrait video.\n5. **Wait node**\nThis simply pauses the workflow for 15 seconds to give **Sora 2** enough time to finish generating the video before continuing.\n6. **Grab Video**\nThis gets the video once it\u2019s successfully generated.\n7. **Switch node**\nThis checks the generation status:\n   - \u2705 Success \u2192 Save video link\n   - \u26a0\ufe0f 500 Error \u2192 Log error message\n   - \ud83d\udd01 Pending \u2192 Loop back to wait and recheck\n6. **Save to Google Sheets**\nOnce successful, the workflow updates your CRM sheet with:\n   - Video Link (no watermark)\n   - Processed = \u201cYes\u201d\n"
      },
      "typeVersion": 1
    },
    {
      "id": "903dc41f-5f5c-4ea8-a62f-114ae5609596",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        736,
        320
      ],
      "parameters": {
        "color": 3,
        "width": 896,
        "height": 432,
        "content": "## Sora 2 UGC Video Generation\nThis is where the actual video generation occurs.   The **Wait** node is added after the **Create video** node to give Sora 2 enough time to finish the video creation before the **Get Video** node retrieves the result. The **Switch**  node is used to check when the video is either successfully generated or still processing. The node also catches **Sora 2 internal server error (error 500)**."
      },
      "typeVersion": 1
    },
    {
      "id": "10b8e3e0-8eac-4401-b08a-731bbdb7882a",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1664,
        320
      ],
      "parameters": {
        "color": 4,
        "width": 448,
        "height": 240,
        "content": "## CRM Update\nThe video link is stored in our Google Sheets, and other necessary columns updated."
      },
      "typeVersion": 1
    },
    {
      "id": "f8a001e5-35b3-482f-8220-2744b42740a5",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1920,
        592
      ],
      "parameters": {
        "color": 4,
        "width": 192,
        "content": "## Error Message\nThis is for **Sora 2 Internal server error.**"
      },
      "typeVersion": 1
    },
    {
      "id": "e312230a-2a72-4fae-8e4b-5b59d4074286",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1664,
        592
      ],
      "parameters": {
        "color": 4,
        "width": 448,
        "content": ""
      },
      "typeVersion": 1
    },
    {
      "id": "2cccf7f8-7081-429a-b4ee-d9a5d37b42e6",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -80,
        320
      ],
      "parameters": {
        "color": 4,
        "width": 432,
        "height": 432,
        "content": "## Start workflow with Product Info\nWhen the workflow is manually started (executed), it grabs the product information (Name, Prompt, Image Link, Processed Status) from the CRM.\nIt grabs these information from the first matching unprocessed row."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "9b639fb8-0900-4241-a2fb-054c38eee6a7",
  "connections": {
    "Wait": {
      "main": [
        [
          {
            "node": "Grab Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tavily": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Create Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Grab Video": {
      "main": [
        [
          {
            "node": "Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Append or update row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Video": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s) in sheet": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Get row(s) in sheet",
            "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 n8n workflow automatically generates 10-second UGC-style portrait video ads for any product — entirely powered by AI. Simply provide your Product Name, Prompt or Idea, and Image Link in Google Sheets, and the system will research your product, craft a modern video prompt,…

Source: https://n8n.io/workflows/10536/ — 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

This n8n workflow is designed for Facebook Page administrators, social media managers, and community moderators who want to automate comment management on their Facebook Pages. It's perfect for busine

Facebook Graph Api, Agent, HTTP Request +8
AI & RAG

This n8n workflow creates an intelligent WhatsApp customer support bot that can handle text, image, audio, and document messages. The workflow automatically processes incoming messages through differe

HTTP Request, N8N Nodes Rapiwa, Agent Tool +9
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
AI & RAG

K&S-Media Downloadliste SQL. Uses httpRequest, agent, googleSheets, lmChatOpenAi. Event-driven trigger; 97 nodes.

HTTP Request, Agent, Google Sheets +3
AI & RAG

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

OpenAI, HTTP Request, Form Trigger +7