AutomationFlowsData & Sheets › Generate Social Videos with AI Avatars Using Veed, Claude, and Openai

Generate Social Videos with AI Avatars Using Veed, Claude, and Openai

ByVEED @veed on n8n.io

This n8n workflow automatically generates TikTok/Reels-ready talking head videos from scratch. You provide a topic and intention, and the workflow handles everything: scriptwriting, avatar generation, voiceover creation, and video rendering.

Event trigger★★★★☆ complexity28 nodesHTTP RequestN8N Nodes VeedGoogle DriveGoogle Sheets
Data & Sheets Trigger: Event Nodes: 28 Complexity: ★★★★☆ Added:

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

This workflow follows the Google Drive → 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": "m57fyIGOHcVm_DzcOJso9",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Generate social videos with AI avatars using VEED and Claude",
  "tags": [],
  "nodes": [
    {
      "id": "faf9466b-1b0c-443f-be17-627379ef2ac1",
      "name": "When clicking 'Execute workflow'",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        2480,
        1168
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "826715fd-5123-4c78-be23-e70a21d4536e",
      "name": "\u2699\ufe0f Workflow Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        2704,
        1168
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "{\n  \"topic\": \"AI video creation tools\",\n  \"intention\": \"informative\",\n  \"brand_name\": \"YOUR_BRAND_NAME\",\n  \"target_audience\": \"content creators and marketers\",\n  \"trending_hashtags\": \"#AIvideo #ContentCreation #VideoMarketing #AItools #TikTokTips\",\n  \"num_videos\": 1,\n  \"anthropic_api_key\": \"YOUR_ANTHROPIC_API_KEY\",\n  \"openai_api_key\": \"YOUR_OPENAI_API_KEY\",\n  \"video_resolution\": \"720p\",\n  \"video_aspect_ratio\": \"9:16\",\n  \"custom_avatar_description\": \"\",\n  \"custom_script\": \"\"\n}"
      },
      "typeVersion": 3.4
    },
    {
      "id": "43174c93-3ec4-4d80-94da-5029bc1fe205",
      "name": "\ud83d\udccb Generate Video Tasks",
      "type": "n8n-nodes-base.code",
      "position": [
        2944,
        1168
      ],
      "parameters": {
        "jsCode": "const config = $input.first().json;\nconst numVideos = config.num_videos || 1;\n\n// Generate array of video tasks with unique angles\nconst videoAngles = [\n  { angle: \"problem-solution\", hook_style: \"question\" },\n  { angle: \"myth-busting\", hook_style: \"controversial\" },\n  { angle: \"quick-tip\", hook_style: \"number\" },\n  { angle: \"before-after\", hook_style: \"transformation\" },\n  { angle: \"trend-commentary\", hook_style: \"news\" }\n];\n\nconst tasks = [];\nfor (let i = 0; i < numVideos; i++) {\n  tasks.push({\n    json: {\n      ...config,\n      video_index: i + 1,\n      content_angle: videoAngles[i % videoAngles.length].angle,\n      hook_style: videoAngles[i % videoAngles.length].hook_style\n    }\n  });\n}\n\nreturn tasks;"
      },
      "typeVersion": 2
    },
    {
      "id": "1cfd2f7c-d245-4710-a599-4b264dec6c65",
      "name": "\ud83d\udd04 Loop Through Videos",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        3184,
        1168
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "c98a35a7-2aff-49b7-9829-1a1152c38891",
      "name": "\ud83e\udde0 Build Claude Prompt",
      "type": "n8n-nodes-base.code",
      "position": [
        3408,
        1280
      ],
      "parameters": {
        "jsCode": "const config = $input.first().json;\n\n// Check if user provided custom inputs\nconst hasCustomAvatar = config.custom_avatar_description && config.custom_avatar_description.trim() !== '';\nconst hasCustomScript = config.custom_script && config.custom_script.trim() !== '';\n\n// Build the prompt for Claude\nconst intentionGuides = {\n  \"informative\": \"Focus on educating the viewer. Provide genuine value and actionable insights. Build trust through expertise.\",\n  \"lead_generation\": \"Create curiosity and desire. Include a soft call-to-action. Make viewers want to learn more about the tool/service.\",\n  \"disruption\": \"Challenge conventional thinking. Be bold and provocative. Make viewers stop scrolling and pay attention.\"\n};\n\nconst hookStyles = {\n  \"question\": \"Start with a thought-provoking question that challenges assumptions\",\n  \"controversial\": \"Start with a bold, slightly controversial statement that makes people want to hear more\",\n  \"number\": \"Start with a specific number or statistic that grabs attention\",\n  \"transformation\": \"Start by describing a transformation or before/after scenario\",\n  \"news\": \"Start with a 'breaking' or trending angle that feels timely\"\n};\n\n// Build dynamic prompt based on what's needed\nlet taskInstructions = '';\nlet responseFormat = '';\n\nif (!hasCustomAvatar && !hasCustomScript) {\n  // Generate both\n  taskInstructions = `### 1. IMAGE PROMPT (for AI avatar):\nCreate a detailed prompt for generating a photorealistic human avatar in PORTRAIT orientation. Consider:\n- Professional but approachable appearance\n- Age 25-40, appropriate for tech content\n- Gender: choose what fits the content best\n- Head and shoulders framing, looking at camera\n- Neutral or vibrant background (NOT white - use colors, gradients, or contextual settings)\n- Good lighting, high quality\n- Clothing that matches the content theme and target audience\n- IMPORTANT: Frame for vertical/portrait composition\n\n### 2. VIDEO SCRIPT (voiceover, 30-45 seconds max):\nCreate an engaging script that:\n- OPENS with a powerful hook using the specified hook style\n- Delivers value related to the topic\n- Maintains conversational, TikTok-native tone\n- Is optimized for text-to-speech (natural phrasing, no special characters)\n- Maximum 450 characters\n- Ends with engagement driver (question, CTA, or thought-provoker)\n- Subtly incorporates the brand if relevant (not salesy)\n\n### 3. CAPTION:\nWrite a TikTok caption (under 150 chars) + include the trending hashtags.`;\n  responseFormat = '{\"image_prompt\": \"detailed image prompt here\", \"audio_script\": \"video script here\", \"caption\": \"caption with hashtags here\", \"avatar_gender\": \"male or female\", \"content_theme\": \"2-3 word theme\"}';\n\n} else if (hasCustomAvatar && !hasCustomScript) {\n  // Only generate script (avatar provided)\n  taskInstructions = `### 1. VIDEO SCRIPT (voiceover, 30-45 seconds max):\nCreate an engaging script that:\n- OPENS with a powerful hook using the specified hook style\n- Delivers value related to the topic\n- Maintains conversational, TikTok-native tone\n- Is optimized for text-to-speech (natural phrasing, no special characters)\n- Maximum 450 characters\n- Ends with engagement driver (question, CTA, or thought-provoker)\n- Subtly incorporates the brand if relevant (not salesy)\n\n### 2. CAPTION:\nWrite a TikTok caption (under 150 chars) + include the trending hashtags.`;\n  responseFormat = '{\"audio_script\": \"video script here\", \"caption\": \"caption with hashtags here\", \"content_theme\": \"2-3 word theme\"}';\n\n} else if (!hasCustomAvatar && hasCustomScript) {\n  // Only generate avatar (script provided)\n  taskInstructions = `### 1. IMAGE PROMPT (for AI avatar):\nCreate a detailed prompt for generating a photorealistic human avatar in PORTRAIT orientation. Consider:\n- Professional but approachable appearance\n- Age 25-40, appropriate for tech content\n- Gender: choose what fits the content best\n- Head and shoulders framing, looking at camera\n- Neutral or vibrant background (NOT white - use colors, gradients, or contextual settings)\n- Good lighting, high quality\n- Clothing that matches the content theme and target audience\n- IMPORTANT: Frame for vertical/portrait composition\n\n### 2. CAPTION:\nWrite a TikTok caption (under 150 chars) + include the trending hashtags.`;\n  responseFormat = '{\"image_prompt\": \"detailed image prompt here\", \"caption\": \"caption with hashtags here\", \"avatar_gender\": \"male or female\", \"content_theme\": \"2-3 word theme\"}';\n\n} else {\n  // Both provided - only generate caption\n  taskInstructions = `### 1. CAPTION:\nWrite a TikTok caption (under 150 chars) + include the trending hashtags.`;\n  responseFormat = '{\"caption\": \"caption with hashtags here\", \"content_theme\": \"2-3 word theme\"}';\n}\n\nconst prompt = `You are an expert short-form video content strategist specializing in tech and AI content for TikTok and social media.\n\n## CONTENT BRIEF:\n- Topic: ${config.topic}\n- Brand/Tool: ${config.brand_name}\n- Target Audience: ${config.target_audience}\n- Content Intention: ${config.intention.toUpperCase()}\n- Content Angle: ${config.content_angle}\n- Trending Hashtags to incorporate: ${config.trending_hashtags}\n\n## INTENTION GUIDE:\n${intentionGuides[config.intention] || intentionGuides.informative}\n\n## HOOK STYLE:\n${hookStyles[config.hook_style] || hookStyles.question}\n\n## YOUR TASK:\nCreate content for a 30-45 second talking-head video in VERTICAL FORMAT (9:16 for TikTok/Reels). Generate:\n\n${taskInstructions}\n\nRespond ONLY with valid JSON in this exact format (no markdown, no code blocks):\n${responseFormat}`;\n\nreturn [{\n  json: {\n    ...config,\n    claude_prompt: prompt,\n    has_custom_avatar: hasCustomAvatar,\n    has_custom_script: hasCustomScript\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "2cda4f21-e3bc-4ed1-911d-2917fb0fca8e",
      "name": "\ud83e\udd16 Claude: Generate Content",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3648,
        1280
      ],
      "parameters": {
        "url": "https://api.anthropic.com/v1/messages",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"model\": \"claude-sonnet-4-20250514\",\n  \"max_tokens\": 2000,\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify($json.claude_prompt) }}\n    }\n  ]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $json.anthropic_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "cb6b083f-2fe9-47b3-bc19-4a03c50bc9fb",
      "name": "\ud83d\udccb Parse Claude Response",
      "type": "n8n-nodes-base.code",
      "position": [
        3888,
        1280
      ],
      "parameters": {
        "jsCode": "const item = $input.first();\nconst claudeResponse = item.json;\nconst config = $('\ud83e\udde0 Build Claude Prompt').item.json;\n\n// Extract the text content from Claude's response\nlet responseText = '';\nif (claudeResponse.content && claudeResponse.content[0]) {\n  responseText = claudeResponse.content[0].text;\n}\n\n// Parse the JSON from Claude's response\nlet parsed = {};\ntry {\n  let cleanText = responseText.trim();\n  // Remove markdown code blocks if present\n  if (cleanText.startsWith('```json')) {\n    cleanText = cleanText.replace(/```json\\n?/, '').replace(/\\n?```$/, '');\n  } else if (cleanText.startsWith('```')) {\n    cleanText = cleanText.replace(/```\\n?/, '').replace(/\\n?```$/, '');\n  }\n  parsed = JSON.parse(cleanText.trim());\n} catch (e) {\n  // Fallback content\n  parsed = {\n    image_prompt: \"\",\n    audio_script: \"\",\n    caption: \"Check out this AI video! #AIvideo #ContentCreation #VideoMarketing\",\n    avatar_gender: \"female\",\n    content_theme: \"AI video\"\n  };\n}\n\n// Build the final image prompt\nlet finalImagePrompt = '';\nif (config.has_custom_avatar && config.custom_avatar_description) {\n  // Use custom avatar description and enhance it for image generation\n  finalImagePrompt = `Photorealistic portrait photo of ${config.custom_avatar_description}. Head and shoulders framing, looking directly at camera, natural expression, high quality, portrait orientation, vibrant or contextual background (not white), good lighting, 4K quality.`;\n} else if (parsed.image_prompt) {\n  finalImagePrompt = parsed.image_prompt;\n} else {\n  finalImagePrompt = \"Photorealistic portrait photo of a friendly content creator in their late 20s, casual stylish clothing, head and shoulders framing, looking directly at camera, confident smile, colorful gradient background, good lighting, portrait orientation, high quality\";\n}\n\n// Build the final script\nlet finalScript = '';\nif (config.has_custom_script && config.custom_script) {\n  finalScript = config.custom_script.trim();\n} else if (parsed.audio_script) {\n  finalScript = parsed.audio_script;\n} else {\n  finalScript = \"What if I told you that creating professional videos no longer requires expensive equipment or editing skills? AI video tools are completely changing the game for content creators. In just minutes, you can go from an idea to a polished video ready for social media. The question is: are you ready to level up your content?\";\n}\n\nreturn [{\n  json: {\n    topic: config.topic,\n    intention: config.intention,\n    brand_name: config.brand_name,\n    video_index: config.video_index,\n    script_image: finalImagePrompt,\n    script_audio: finalScript,\n    caption: parsed.caption || \"Check out this AI-generated video! #AIvideo #ContentCreation\",\n    avatar_gender: parsed.avatar_gender || \"female\",\n    content_theme: parsed.content_theme || \"AI video\",\n    openai_api_key: config.openai_api_key,\n    video_resolution: config.video_resolution,\n    video_aspect_ratio: config.video_aspect_ratio,\n    used_custom_avatar: config.has_custom_avatar,\n    used_custom_script: config.has_custom_script\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "7aa724fb-736f-46cf-ba4a-e4e40f98dc3e",
      "name": "\ud83c\udfa8 Generate Avatar (OpenAI)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4128,
        1280
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/images/generations",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"model\": \"gpt-image-1\",\n  \"prompt\": {{ JSON.stringify($json.script_image) }},\n  \"n\": 1,\n  \"size\": \"1024x1536\",\n  \"quality\": \"high\"\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.openai_api_key }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "399e2a91-be1a-4e90-93d1-9b5f059cc955",
      "name": "\ud83d\udcf8 Extract Image Data",
      "type": "n8n-nodes-base.code",
      "position": [
        4368,
        1280
      ],
      "parameters": {
        "jsCode": "const item = $input.first();\nconst imageResponse = item.json;\nconst previousData = $('\ud83d\udccb Parse Claude Response').item.json;\n\nlet imageBase64 = '';\n\n// gpt-image-1 returns b64_json by default\nif (imageResponse.data && imageResponse.data[0] && imageResponse.data[0].b64_json) {\n  imageBase64 = imageResponse.data[0].b64_json;\n}\n\nif (!imageBase64) {\n  throw new Error('No base64 image data received from OpenAI. Response: ' + JSON.stringify(imageResponse));\n}\n\nreturn [{\n  json: {\n    ...previousData\n  },\n  binary: {\n    image_data: {\n      data: imageBase64,\n      mimeType: 'image/png',\n      fileName: 'avatar.png'\n    }\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "d74ba138-544f-497d-9681-ece8812280bb",
      "name": "\u2601\ufe0f Upload Image",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4592,
        1280
      ],
      "parameters": {
        "url": "https://tmpfiles.org/api/v1/upload",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "name": "file",
              "parameterType": "formBinaryData",
              "inputDataFieldName": "image_data"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "0ab57308-8206-402b-b778-e705ce7127dc",
      "name": "\ud83d\udcbe Store Image URL",
      "type": "n8n-nodes-base.code",
      "position": [
        4816,
        1280
      ],
      "parameters": {
        "jsCode": "const uploadResponse = $input.first().json;\nconst previousData = $('\ud83d\udcf8 Extract Image Data').item.json;\n\n// Convert tmpfiles URL to direct download URL\nconst publicImageUrl = uploadResponse.data.url.replace(\n  /^http:\\/\\/tmpfiles\\.org\\/(\\d+)\\/(.*)$/i,\n  'https://tmpfiles.org/dl/$1/$2'\n);\n\nreturn [{\n  json: {\n    ...previousData,\n    public_image_url: publicImageUrl\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "fdb0a9e7-71a9-4906-a74a-0f7ec213ef1a",
      "name": "\ud83d\udd0a Generate Audio (TTS)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        5040,
        1280
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/audio/speech",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file",
              "outputPropertyName": "audio"
            }
          }
        },
        "jsonBody": "={\n  \"model\": \"tts-1-hd\",\n  \"input\": {{ JSON.stringify($json.script_audio) }},\n  \"voice\": \"nova\",\n  \"response_format\": \"mp3\"\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.openai_api_key }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "cb24f84a-6b96-4fc1-8e82-634d390ea2ba",
      "name": "\ud83c\udfb5 Convert Audio",
      "type": "n8n-nodes-base.code",
      "position": [
        5264,
        1280
      ],
      "parameters": {
        "jsCode": "return items.map(item => {\n  const b = item.binary?.audio;\n  if (!b) return item;\n\n  item.binary.audio_mp3 = {\n    ...b,\n    fileName: 'voiceover.mp3',\n    mimeType: 'audio/mpeg'\n  };\n\n  return item;\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "46b78438-86cb-442c-8180-80b1d86876ca",
      "name": "\u2601\ufe0f Upload Audio",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        5488,
        1280
      ],
      "parameters": {
        "url": "https://tmpfiles.org/api/v1/upload",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "name": "file",
              "parameterType": "formBinaryData",
              "inputDataFieldName": "audio_mp3"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "ee599f88-8246-446e-95f0-6223c29f168b",
      "name": "\ud83d\udce6 Prepare VEED Request",
      "type": "n8n-nodes-base.code",
      "position": [
        5712,
        1280
      ],
      "parameters": {
        "jsCode": "const audioUpload = $input.first().json;\nconst previousData = $('\ud83d\udcbe Store Image URL').item.json;\n\n// Convert tmpfiles URL to direct download URL\nconst publicAudioUrl = audioUpload.data.url.replace(\n  /^http:\\/\\/tmpfiles\\.org\\/(\\d+)\\/(.*)$/i,\n  'https://tmpfiles.org/dl/$1/$2'\n);\n\nreturn [{\n  json: {\n    image_url: previousData.public_image_url,\n    audio_url: publicAudioUrl,\n    resolution: previousData.video_resolution || '720p',\n    aspect_ratio: previousData.video_aspect_ratio || '9:16',\n    topic: previousData.topic,\n    intention: previousData.intention,\n    brand_name: previousData.brand_name,\n    video_index: previousData.video_index,\n    script_audio: previousData.script_audio,\n    script_image: previousData.script_image,\n    caption: previousData.caption,\n    content_theme: previousData.content_theme\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "74053fe2-b750-46d2-8b41-378917955e3f",
      "name": "\ud83c\udfac Generate Video (VEED)",
      "type": "n8n-nodes-veed.veed",
      "position": [
        5952,
        1280
      ],
      "parameters": {
        "options": {},
        "audioUrl": "={{ $json.audio_url }}",
        "imageUrl": "={{ $json.image_url }}",
        "resolution": "={{ $json.resolution }}",
        "aspectRatio": "={{ $json.aspect_ratio }}"
      },
      "typeVersion": 1
    },
    {
      "id": "792788e1-6080-4e15-8a15-dbdaa87ebc78",
      "name": "\ud83d\udcc1 Prepare Upload",
      "type": "n8n-nodes-base.code",
      "position": [
        6176,
        1280
      ],
      "parameters": {
        "jsCode": "const veedResult = $input.first().json;\nconst prepData = $('\ud83d\udce6 Prepare VEED Request').item.json;\n\nconst now = new Date();\nconst monthNames = ['January', 'February', 'March', 'April', 'May', 'June',\n  'July', 'August', 'September', 'October', 'November', 'December'];\nconst monthFolder = `${monthNames[now.getMonth()]} ${now.getFullYear()}`;\nconst dateStr = now.toISOString().split('T')[0];\nconst fileName = `${dateStr}-${prepData.topic.replace(/\\s+/g, '_')}_${prepData.video_index}_${Date.now()}.mp4`;\n\n// Get video URL from VEED response - check various possible response structures\nlet videoUrl = '';\nif (veedResult.video && veedResult.video.url) {\n  videoUrl = veedResult.video.url;\n} else if (veedResult.output && veedResult.output.video_url) {\n  videoUrl = veedResult.output.video_url;\n} else if (veedResult.videoUrl) {\n  videoUrl = veedResult.videoUrl;\n} else if (veedResult.url) {\n  videoUrl = veedResult.url;\n}\n\nreturn [{\n  json: {\n    video_url: videoUrl,\n    file_name: fileName,\n    month_folder: monthFolder,\n    topic: prepData.topic,\n    intention: prepData.intention,\n    brand_name: prepData.brand_name,\n    video_index: prepData.video_index,\n    script_audio: prepData.script_audio,\n    script_image: prepData.script_image,\n    caption: prepData.caption,\n    content_theme: prepData.content_theme,\n    image_url: prepData.image_url,\n    audio_url: prepData.audio_url,\n    status: videoUrl ? 'done' : 'error',\n    veed_raw_response: veedResult\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "0d5813ff-dfd6-4745-b8d2-b3cf85a99356",
      "name": "\u2b07\ufe0f Download Video",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        6416,
        1280
      ],
      "parameters": {
        "url": "={{ $json.video_url }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "b68991c0-835b-4466-a1c2-a429437debf9",
      "name": "\ud83d\udce4 Upload to Drive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        6640,
        1280
      ],
      "parameters": {
        "name": "={{ $('\ud83d\udcc1 Prepare Upload').item.json.file_name }}",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive",
          "cachedResultUrl": "https://drive.google.com/drive/my-drive",
          "cachedResultName": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "url",
          "value": "YOUR_GOOGLE_DRIVE_FOLDER_URL"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "6bd0a1e7-9d33-4ffe-b5a9-908f5b3ec381",
      "name": "\u2705 Prepare Final Data",
      "type": "n8n-nodes-base.code",
      "position": [
        6864,
        1280
      ],
      "parameters": {
        "jsCode": "const driveResult = $input.first().json;\nconst prepData = $('\ud83d\udcc1 Prepare Upload').item.json;\n\nconst driveFileUrl = driveResult.webViewLink || \n  `https://drive.google.com/file/d/${driveResult.id}/view`;\n\nreturn [{\n  json: {\n    topic: prepData.topic,\n    intention: prepData.intention,\n    brand_name: prepData.brand_name,\n    content_theme: prepData.content_theme,\n    script_audio: prepData.script_audio,\n    script_image: prepData.script_image,\n    caption: prepData.caption,\n    image_url: prepData.image_url,\n    audio_url: prepData.audio_url,\n    video_url: driveFileUrl,\n    status: 'done',\n    created_at: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "c8b3a865-4aa7-4ae8-9bdb-667a31a19f13",
      "name": "\ud83d\udcdd Log to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        7088,
        1280
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "YOUR_GOOGLE_SHEETS_URL"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "1a7d44bb-12a9-4815-ba54-d040642482c2",
      "name": "\ud83d\udd04 Continue Loop1",
      "type": "n8n-nodes-base.noOp",
      "position": [
        7312,
        1280
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "5fef2c70-7dc3-417e-9fb7-2e3e1b4f15b1",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1904,
        896
      ],
      "parameters": {
        "width": 500,
        "height": 784,
        "content": "# Generate social videos with AI avatars using VEED and Claude\n\n## How it works\nThis workflow automates the creation of short-form social videos featuring AI-generated talking-head avatars.\n\n1. You configure your topic, content intention (informative, lead generation, or disruption), brand name, and target audience\n2. Claude AI generates a video script optimized for TikTok/Reels, an image prompt for the avatar, and a caption with hashtags\n3. OpenAI's gpt-image-1 creates a photorealistic portrait avatar based on the prompt\n4. OpenAI's TTS generates natural voiceover audio from the script\n5. The avatar image and audio are uploaded to temporary storage\n6. VEED renders a lip-synced talking-head video combining the avatar and audio\n7. The final video is downloaded and uploaded to your Google Drive\n8. Video metadata and links are logged to Google Sheets for tracking\n\n## Setup steps\n1. Add your Anthropic API key and OpenAI API key in the Configuration node\n2. Connect your VEED/FAL.ai credential to the VEED node\n3. Connect Google Drive and Google Sheets OAuth2 credentials\n4. Update the Drive folder URL and Sheets document URL\n5. Customize the topic, brand name, target audience, and video settings"
      },
      "typeVersion": 1
    },
    {
      "id": "62b3a1d6-47a5-41e3-b0fa-46dcfbbf71c0",
      "name": "Sticky Note Configuration",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2416,
        1040
      ],
      "parameters": {
        "color": 7,
        "width": 450,
        "height": 462,
        "content": "### Configuration\nSet your topic, API keys, and video settings. Supports custom avatar descriptions and scripts."
      },
      "typeVersion": 1
    },
    {
      "id": "865efaa2-ebff-48a8-9d2f-333e72efc637",
      "name": "Sticky Note AI Content",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2880,
        1040
      ],
      "parameters": {
        "color": 7,
        "width": 1172,
        "height": 464,
        "content": "### AI Content Generation\nClaude creates the script, image prompt, and social caption based on your topic and intention."
      },
      "typeVersion": 1
    },
    {
      "id": "55973e05-0aa5-4309-a03d-6299d689ab67",
      "name": "Sticky Note Avatar Audio",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4064,
        1040
      ],
      "parameters": {
        "color": 7,
        "width": 1568,
        "height": 462,
        "content": "### Avatar & Audio\nOpenAI generates a photorealistic avatar image and converts the script to natural speech."
      },
      "typeVersion": 1
    },
    {
      "id": "7349858c-b29c-40c9-b5cd-f271f76b74f9",
      "name": "Sticky Note Video Rendering",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5648,
        1040
      ],
      "parameters": {
        "color": 7,
        "width": 462,
        "height": 462,
        "content": "### Video Rendering\nVEED creates the final lip-synced talking-head video from your avatar and audio."
      },
      "typeVersion": 1
    },
    {
      "id": "d63164f9-675a-4488-b4b3-de50a03abe1a",
      "name": "Sticky Note Storage Logging",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6128,
        1040
      ],
      "parameters": {
        "color": 7,
        "width": 1364,
        "height": 462,
        "content": "### Storage & Logging\nDownloads the video, uploads to Google Drive, and logs all metadata to Google Sheets."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "f5471b15-9e31-46fc-ae0d-c7e55fb85ee8",
  "connections": {
    "\ud83c\udfb5 Convert Audio": {
      "main": [
        [
          {
            "node": "\u2601\ufe0f Upload Audio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcdd Log to Sheets": {
      "main": [
        [
          {
            "node": "\ud83d\udd04 Continue Loop1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2601\ufe0f Upload Audio": {
      "main": [
        [
          {
            "node": "\ud83d\udce6 Prepare VEED Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2601\ufe0f Upload Image": {
      "main": [
        [
          {
            "node": "\ud83d\udcbe Store Image URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcc1 Prepare Upload": {
      "main": [
        [
          {
            "node": "\u2b07\ufe0f Download Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd04 Continue Loop1": {
      "main": [
        [
          {
            "node": "\ud83d\udd04 Loop Through Videos",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcbe Store Image URL": {
      "main": [
        [
          {
            "node": "\ud83d\udd0a Generate Audio (TTS)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce4 Upload to Drive": {
      "main": [
        [
          {
            "node": "\u2705 Prepare Final Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2b07\ufe0f Download Video": {
      "main": [
        [
          {
            "node": "\ud83d\udce4 Upload to Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2705 Prepare Final Data": {
      "main": [
        [
          {
            "node": "\ud83d\udcdd Log to Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcf8 Extract Image Data": {
      "main": [
        [
          {
            "node": "\u2601\ufe0f Upload Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd04 Loop Through Videos": {
      "main": [
        [],
        [
          {
            "node": "\ud83e\udde0 Build Claude Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\udde0 Build Claude Prompt": {
      "main": [
        [
          {
            "node": "\ud83e\udd16 Claude: Generate Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udccb Generate Video Tasks": {
      "main": [
        [
          {
            "node": "\ud83d\udd04 Loop Through Videos",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce6 Prepare VEED Request": {
      "main": [
        [
          {
            "node": "\ud83c\udfac Generate Video (VEED)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd0a Generate Audio (TTS)": {
      "main": [
        [
          {
            "node": "\ud83c\udfb5 Convert Audio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83c\udfac Generate Video (VEED)": {
      "main": [
        [
          {
            "node": "\ud83d\udcc1 Prepare Upload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udccb Parse Claude Response": {
      "main": [
        [
          {
            "node": "\ud83c\udfa8 Generate Avatar (OpenAI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2699\ufe0f Workflow Configuration": {
      "main": [
        [
          {
            "node": "\ud83d\udccb Generate Video Tasks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83c\udfa8 Generate Avatar (OpenAI)": {
      "main": [
        [
          {
            "node": "\ud83d\udcf8 Extract Image Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\udd16 Claude: Generate Content": {
      "main": [
        [
          {
            "node": "\ud83d\udccb Parse Claude Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking 'Execute workflow'": {
      "main": [
        [
          {
            "node": "\u2699\ufe0f Workflow Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
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 TikTok/Reels-ready talking head videos from scratch. You provide a topic and intention, and the workflow handles everything: scriptwriting, avatar generation, voiceover creation, and video rendering.

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

More Data & Sheets workflows → · Browse all categories →

Related workflows

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

Data & Sheets

PCN. Uses googleSheets, httpRequest, @n-octo-n/n8n-nodes-json-database, itemLists. Event-driven trigger; 60 nodes.

Google Sheets, HTTP Request, @N Octo N/N8N Nodes Json Database +3
Data & Sheets

The workflow automates the process of gathering extensive keyword data for a "Main Keyword." It starts by reading initial parameters from a Google Sheets template, creates a new dedicated Google Sheet

Google Sheets, Google Drive, HTTP Request
Data & Sheets

🔥 March Sale – n8n Community Members Get ideoGener8r for Just $27! (Reg. $47) Use Coupon Code: (Valid until 3/31/2025 for n8n community members)

HTTP Request, Google Drive, Google Sheets
Data & Sheets

📄 Documentation: Notion Guide

Google Sheets, Google Drive, HTTP Request +2
Data & Sheets

Overview

Google Sheets, Google Drive, HTTP Request