{
  "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
          }
        ]
      ]
    }
  }
}