🎯 Overview
This n8n workflow automates the entire YouTube Shorts creation process:
User submits video idea via web form
AI generates SEO-optimized script (OpenAI GPT-4)
AI creates professional voiceover (ElevenLabs)
Veo 3 generates high-quality vertical video (Google Gemini)
System merges audio + video
User receives download link via email
Total Processing Time: 2-3 minutes per video
✨ Features
🤖 AI-Powered Content Generation
Smart Script Writing: GPT-4 creates engaging, viral-ready scripts
SEO Optimization: Automatic keyword extraction and title suggestions
Content Analysis: AI determines optimal video style based on topic
🎬 Veo 3 Video Generation
Cinematic Quality: 720p vertical videos (9:16 aspect ratio)
Style Flexibility: Realistic, animated, or mixed based on content
8-second clips: Perfect for short-form content(Currently Gemini API limits the video to 8 seconds default, but it can be by-passed by creating multiple 8-sec videos and stitching them together FFmpeg
🎤 Professional Voiceovers
Natural Speech: ElevenLabs Turbo v2.5 for realistic narration
Voice Options: Multiple voice styles (professional, energetic, warm)
Perfect Timing: Voiceover synced with video length
📊 Complete Workflow
Web Form Interface: Easy-to-use submission form
Automated Processing: No manual intervention required
Email Delivery: Downloadable links with full video details
7-Day Storage: Videos accessible for a week
🔧 Prerequisites
Required Services:
n8n Instance (v1.0+)
Self-hosted or cloud (n8n.cloud)
API Keys:
Google Gemini API - Veo 3 access
OpenAI API - GPT-4 script generation
ElevenLabs API - Voiceover generation
Storage Solution:
Cloudflare R2 (recommended - free tier available)
OR AWS S3
Email Service:
Gmail account with OAuth2 configured in n8n
Recommended:
FFmpeg installed on n8n server (for audio-video merging)
At least 2GB RAM on n8n server
Fast internet connection (for video downloads)
🚀 Quick Start
1. Import Workflow
# In n8n UI:
1. Go to Workflows
2. Click "Import from File"
3. Select: youtube-shorts-veo3-workflow.json
4. Click "Import"
2. Configure Environment Variables
# Copy example file
cp .env.example .env
# Edit with your API keys
nano .env
Add your API keys:
GOOGLE_API_KEY=your_actual_google_key
OPENAI_API_KEY=your_actual_openai_key
ELEVENLABS_API_KEY=your_actual_elevenlabs_key
CLOUDFLARE_R2_ENDPOINT=your_r2_endpoint
3. Set Up Credentials in n8n
DO NOT paste credentials directly into nodes! Use n8n’s credential manager:
Google Gemini/Veo 3:
Go to: Credentials → Add Credential → HTTP Header Auth
Name:
Google Veo 3 APIHeader Name:
x-goog-api-keyValue: Your Google API key
OpenAI:
Go to: Credentials → Add Credential → OpenAI API
Name:
OpenAI GPT-4API Key: Your OpenAI key
ElevenLabs:
Go to: Credentials → Add Credential → HTTP Header Auth
Name:
ElevenLabs VoiceHeader Name:
xi-api-keyValue: Your ElevenLabs key
Gmail:
Go to: Credentials → Add Credential → Gmail OAuth2
Follow the OAuth setup wizard
4. Attach Credentials to Nodes
In the n8n workflow editor:
Click each node that requires credentials
Select the appropriate credential from dropdown
Save the workflow
5. Activate Workflow
# In n8n UI:
1. Open the workflow
2. Toggle "Active" switch to ON
3. Copy the webhook URL
📖 Detailed Setup
Step 1: Install n8n (if not already installed)
Option A: Self-hosted (Docker)
docker run -it --rm \\
--name n8n \\
-p 5678:5678 \\
-v ~/.n8n:/home/node/.n8n \\
n8nio/n8n
Option B: n8n Cloud
Sign up at n8n.cloud
Follow guided setup
Step 2: Get API Keys
Google Gemini API (Veo 3)
Go to Google AI Studio
Click “Get API Key”
Create new API key
Copy and save securely
Cost: Pay-per-use (see pricing)
OpenAI API (GPT-4)
Go to OpenAI Platform
Create new secret key
Copy immediately (shown only once)
Cost: ~$0.01-0.03 per video script
ElevenLabs API
Go to ElevenLabs
Sign up and navigate to API settings
Generate API key
Cost: ~10,000 characters free/month, then pay-per-use
Step 3: Set Up Cloudflare R2 Storage
Create R2 Bucket:
# In Cloudflare Dashboard: - Go to R2 Storage - Create bucket: "youtube-shorts" - Enable public accessGet API Credentials:
- Go to R2 → Manage R2 API Tokens - Create new API token - Copy: Access Key ID + Secret Access KeyConfigure Public URL:
# Custom domain (optional but recommended) - Go to R2 bucket settings - Add custom domain: videos.yourdomain.com # OR use default R2 URL: your-bucket.r2.dev
Step 4: Configure FFmpeg (for audio-video merging)
Linux/Mac:
# Install FFmpeg
sudo apt-get install ffmpeg # Ubuntu/Debian
brew install ffmpeg # macOS
# Verify installation
ffmpeg -version
Docker n8n:
# Add to Dockerfile
RUN apk add --no-cache ffmpeg
Note: The workflow includes a code node that simulates merging. Replace with actual FFmpeg command in production.
Step 5: Configure Gmail OAuth
In n8n:
Go to Credentials → Gmail OAuth2
Click “Create New Credential”
Google Cloud Console:
Create project
Enable Gmail API
Create OAuth 2.0 credentials
Add authorized redirect:
https://your-n8n.com/rest/oauth2-credential/callback
Connect in n8n:
Enter Client ID + Secret
Click “Connect my account”
Authorize access
💻 Usage
Method 1: Web Form (Recommended)
Get Webhook URL:
<https://your-n8n-instance.com/form/youtube-shorts>Share with Team:
Send URL to content creators
Embed in internal tools
Add to website
Submit Video Idea:
Fill out form
Click “Generate Video”
Wait 2-3 minutes
Receive email with download link
Method 2: API Call
curl -X POST '<https://your-n8n.com/webhook/youtube-shorts>' \\
-H 'Content-Type: application/json' \\
-d '{
"video_idea": "Top 5 productivity hacks for remote workers",
"target_audience": "Millennials (25-40)",
"video_style": "Energetic & Fast-paced",
"duration": "30 seconds",
"voice_type": "Young Energetic Female",
"email": "[email protected]"
}'
Method 3: Manual Execution (Testing)
Open workflow in n8n
Click “Execute Workflow”
Manually enter test data
Check execution log for results
💰 API Costs
Estimated Cost Per Video:
Service | Cost | Notes |
|---|---|---|
OpenAI GPT-4 | $0.01-0.03 | Script generation (~500 tokens) |
ElevenLabs | $0.01-0.02 | ~200 characters voiceover |
Google Veo 3 | $0.10-0.30 | 8-second 720p video |
Cloudflare R2 | $0.00-0.01 | Storage + bandwidth |
Total | ~$0.12-0.36 | Per video |
Monthly Estimates:
Videos/Month | Estimated Cost |
|---|---|
50 videos | $6-18 |
100 videos | $12-36 |
500 videos | $60-180 |
Free Tiers:
ElevenLabs: 10,000 characters/month free
Cloudflare R2: 10GB storage free
OpenAI: $5 free credit (new accounts)
🔧 Troubleshooting
Common Issues:
1. “Veo 3 video generation failed”
Cause: API quota exceeded or invalid prompt
Solution:
# Check API quota
curl -H "x-goog-api-key: YOUR_KEY" \\
<https://generativelanguage.googleapis.com/v1beta/models>
# Verify prompt length (max 1024 tokens)
# Simplify complex prompts
2. “Voiceover generation timeout”
Cause: ElevenLabs API slow or quota exceeded
Solution:
# Check ElevenLabs quota
curl -H "xi-api-key: YOUR_KEY" \\
<https://api.elevenlabs.io/v1/user>
# Reduce script length
# Increase timeout in node settings
3. “Email not received”
Cause: Gmail OAuth expired or spam folder
Solution:
# Re-authenticate Gmail:
1. Go to Credentials in n8n
2. Delete old Gmail credential
3. Create new one
4. Re-authorize
# Check spam/junk folders
# Verify email address is correct
4. “Video download fails”
Cause: Veo 3 URL expired (2-day limit)
Solution:
# Videos expire after 48 hours
# Download immediately after generation
# Consider increasing storage retention
5. “Audio-video merge error”
Cause: FFmpeg not installed or incorrect path
Solution:
# Install FFmpeg
sudo apt-get install ffmpeg
# Verify installation
ffmpeg -version
# Update code node with correct FFmpeg path
⚙️ Advanced Configuration
Custom Voice Mapping
Edit the voiceover-generation node to map custom voices:
// Voice ID mapping
const voiceMap = {
"Professional Male": "21m00Tcm4TlvDq8ikWAM",
"Professional Female": "EXAVITQu4vr4xnSDxMaL",
"Young Energetic Male": "pNInz6obpgDQGcFmaJgB",
"Young Energetic Female": "jBpfuIE2acCO8z3wKNLl",
"Custom Voice 1": "YOUR_VOICE_ID_HERE"
};
const selectedVoice = voiceMap[$json.voice_type] || voiceMap["Professional Male"];
Extend Video Duration
Veo 3 supports up to 8 seconds per generation. For longer videos:
Generate multiple 8-second clips
Use Veo 3’s extension feature
Stitch clips using FFmpeg
Add Watermark/Logo
Update merge-audio-video node:
# FFmpeg command with watermark
ffmpeg -i video.mp4 -i logo.png -i audio.mp3 \\
-filter_complex "[0:v][1:v]overlay=W-w-10:H-h-10" \\
-c:a aac -map 2:a output.mp4
Batch Processing
Enable batch mode to process multiple videos:
Replace form trigger with Google Sheets trigger
Read rows of video ideas
Loop through each row
Generate videos in parallel (max 3-5 concurrent)
FULL JSON Workflow
{
"name": "🎬 YouTube Shorts Automation with Veo 3",
"nodes": [
{
"parameters": {
"formTitle": "🎬 YouTube Shorts Video Generator",
"formDescription": "Create professional YouTube Shorts videos with AI-powered script, voiceover, and Veo 3 video generation. Enter your video concept and let AI handle the rest!",
"formFields": {
"values": [
{
"fieldLabel": "Video Topic/Idea",
"fieldType": "textarea",
"placeholder": "e.g., 'Top 5 productivity hacks for remote workers' or 'How to make the perfect espresso at home'",
"requiredField": true
},
{
"fieldLabel": "Target Audience",
"fieldType": "dropdown",
"fieldOptions": {
"values": [
{
"option": "Gen Z (18-24)"
},
{
"option": "Millennials (25-40)"
},
{
"option": "Gen X (41-56)"
},
{
"option": "Professionals"
},
{
"option": "Entrepreneurs"
},
{
"option": "Students"
},
{
"option": "Parents"
},
{
"option": "General Audience"
}
]
},
"requiredField": true
},
{
"fieldLabel": "Video Style",
"fieldType": "dropdown",
"fieldOptions": {
"values": [
{
"option": "Auto-detect (AI decides)"
},
{
"option": "Energetic & Fast-paced"
},
{
"option": "Professional & Clean"
},
{
"option": "Fun & Casual"
},
{
"option": "Educational & Informative"
},
{
"option": "Inspirational & Motivational"
},
{
"option": "Dramatic & Cinematic"
}
]
}
},
{
"fieldLabel": "Video Duration",
"fieldType": "dropdown",
"fieldOptions": {
"values": [
{
"option": "8 seconds"
},
{
"option": "30 seconds"
},
{
"option": "45 seconds"
},
{
"option": "60 seconds"
}
]
},
"requiredField": true
},
{
"fieldLabel": "Voiceover Voice Type",
"fieldType": "dropdown",
"fieldOptions": {
"values": [
{
"option": "Professional Male"
},
{
"option": "Professional Female"
},
{
"option": "Young Energetic Male"
},
{
"option": "Young Energetic Female"
},
{
"option": "Warm & Friendly"
},
{
"option": "Deep & Authoritative"
}
]
}
},
{
"fieldLabel": "Email for Delivery",
"fieldType": "email",
"placeholder": "[email protected]",
"requiredField": true
}
]
},
"responseMode": "lastNode",
"options": {}
},
"id": "form-trigger-node",
"name": "🎯 Video Idea Input Form",
"type": "n8n-nodes-base.formTrigger",
"typeVersion": 2.3,
"position": [
-7152,
928
]
},
{
"parameters": {
"jsCode": "// Parse the OpenAI response and extract script data\nconst response = items[0].json;\n\n// Handle different response formats (Standard Node vs API Raw)\nconst aiResponse = response.message?.content || response.choices?.[0]?.message?.content || response.content;\n\nif (!aiResponse) {\n throw new Error(`Could not find content in OpenAI response. Received: ${JSON.stringify(response)}`);\n}\n\nlet scriptData;\ntry {\n // Try to parse JSON from the response\n const jsonMatch = aiResponse.match(/```(?:json)?\\s*([\\s\\S]*?)\\s*```/);\n if (jsonMatch) {\n scriptData = JSON.parse(jsonMatch[1]);\n } else {\n scriptData = JSON.parse(aiResponse);\n }\n} catch (e) {\n console.error('Failed to parse script data:', e);\n // Fallback structure\n scriptData = {\n script: {\n hook: 'Amazing discovery ahead!',\n main_content: 'This is the most important thing you need to know.',\n cta: 'Follow for more tips!',\n full_script: 'Amazing discovery ahead! This is the most important thing you need to know. Follow for more tips!'\n },\n timing: {\n hook_duration: 3,\n main_duration: 24,\n cta_duration: 3,\n total_estimated: 30\n },\n visual_direction: {\n style: 'realistic',\n energy_level: 'high',\n color_palette: 'vibrant',\n camera_movement: 'dynamic'\n },\n veo3_scene_prompt: 'Dynamic scene with engaging visuals',\n seo_keywords: ['viral', 'trending', 'must-watch'],\n title_suggestion: 'You Won\\'t Believe This!',\n description_suggestion: 'Amazing content you need to see!'\n };\n}\n\n// Merge with original form data\nconst formData = $('🎯 Video Idea Input Form').item.json;\n\nreturn [{\n json: {\n ...formData,\n ...scriptData,\n processing_id: `yt-shorts-${Date.now()}`,\n created_at: new Date().toISOString()\n }\n}];"
},
"id": "parse-script-node",
"name": "Parse Script Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-6704,
928
]
},
{
"parameters": {
"jsCode": "// Store the audio file data and pass through all context\nconst audioData = items[0].binary;\nconst contextData = $('Parse Script Data').item.json;\n\n// Get audio duration (approximate from file size)\nconst audioBuffer = audioData.data;\nconst estimatedDuration = Math.min(Math.ceil(audioBuffer.byteLength / 44100 / 2), 60);\n\nreturn [{\n json: {\n ...contextData,\n audio_file: {\n data: audioBuffer.toString('base64'),\n mimeType: 'audio/mpeg',\n fileName: `voiceover_${contextData.processing_id}.mp3`,\n estimated_duration: estimatedDuration\n },\n audio_ready: true,\n audio_duration_seconds: estimatedDuration\n },\n binary: audioData\n}];"
},
"id": "store-audio-node",
"name": "Store Audio File",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-6256,
928
]
},
{
"parameters": {
"jsCode": "const response = items[0].json;\n\n// ✅ Handle ALL n8n OpenAI response formats\nconst enhancedPrompt =\n response.output_text ||\n response.message?.content ||\n response.choices?.[0]?.message?.content ||\n response.content ||\n '';\n\nif (!enhancedPrompt || typeof enhancedPrompt !== 'string') {\n throw new Error(\n `Enhanced Veo prompt missing. Received: ${JSON.stringify(response)}`\n );\n}\n\nconst contextData = $('Store Audio File').item.json;\n\nreturn [\n {\n json: {\n ...contextData,\n veo3_enhanced_prompt: enhancedPrompt.trim(),\n veo3_ready: true,\n },\n },\n];"
},
"id": "extract-prompt-node",
"name": "Extract Enhanced Prompt",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-5808,
928
]
},
{
"parameters": {
"jsCode": "// Video comes directly from Veo node\nconst videoBinary = items[0].binary;\n\n// Pull audio + metadata from Store Audio File (still exists)\nconst contextData = $('Store Audio File').item.json;\nconst audioBinary = $('Store Audio File').item.binary;\n\n// Simulated merged metadata\nconst mergedVideo = {\n filename: `youtube_short_${contextData.processing_id}.mp4`,\n mimeType: 'video/mp4',\n duration: contextData.audio_duration_seconds,\n resolution: '720p',\n aspect_ratio: '9:16',\n has_audio: true,\n};\n\nreturn [\n {\n json: {\n ...contextData,\n merged_video: mergedVideo,\n merge_status: 'completed',\n final_video_ready: true,\n },\n binary: {\n video: videoBinary.data,\n audio: audioBinary.data,\n },\n },\n];"
},
"id": "merge-audio-video-node",
"name": "🎞️ Merge Audio + Video",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-5152,
928
]
},
{
"parameters": {
"jsCode": "const contextData = items[0].json;\n\n// Resolve filename safely\nconst filename =\n contextData.merged_video?.filename ||\n `youtube_short_${contextData.processing_id}.mp4`;\n\n// ⚠️ REPLACE WITH YOUR CLOUDFLARE R2 PUBLIC URL\nconst baseUrl = 'https://YOUR-R2-PUBLIC-URL.r2.dev';\n\n// Build URL\nconst shareableUrl = `${baseUrl}/${filename}`;\n\n// Expiry (7 days)\nconst expiryDate = new Date();\nexpiryDate.setDate(expiryDate.getDate() + 7);\n\nreturn [\n {\n json: {\n ...contextData,\n shareable_url: shareableUrl,\n download_url: shareableUrl,\n url_expires_at: expiryDate.toISOString(),\n share_ready: true,\n },\n },\n];"
},
"id": "generate-link-node",
"name": "🔗 Generate Shareable Link",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-4256,
928
]
},
{
"parameters": {
"jsCode": "// Generate beautiful HTML email with video details\nconst data = items[0].json;\n\n// ---- SAFETY NORMALIZATION ----\nconst visualDirection = data.visual_direction || {\n style: 'Auto',\n energy_level: 'Auto',\n};\n\nconst script = data.script || {\n hook: '—',\n main_content: '—',\n cta: '—',\n};\n\nconst seoKeywords = Array.isArray(data.seo_keywords)\n ? data.seo_keywords\n : [];\n\nconst html = `\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n line-height: 1.6;\n color: #333;\n max-width: 600px;\n margin: 0 auto;\n padding: 20px;\n background-color: #f5f5f5;\n }\n .container {\n background-color: white;\n border-radius: 12px;\n padding: 40px;\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n }\n .header {\n text-align: center;\n border-bottom: 4px solid #FF0000;\n padding-bottom: 20px;\n margin-bottom: 30px;\n }\n h1 {\n color: #FF0000;\n margin: 0;\n font-size: 32px;\n }\n .emoji {\n font-size: 48px;\n margin-bottom: 10px;\n }\n .video-preview {\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n border-radius: 8px;\n padding: 30px;\n text-align: center;\n margin: 30px 0;\n color: white;\n }\n .download-btn {\n display: inline-block;\n background: #FF0000;\n color: white;\n padding: 16px 32px;\n text-decoration: none;\n border-radius: 8px;\n font-weight: bold;\n font-size: 18px;\n margin: 20px 0;\n transition: background 0.3s ease;\n }\n .download-btn:hover {\n background: #CC0000;\n }\n .details {\n background: #f8f9fa;\n border-radius: 8px;\n padding: 20px;\n margin: 20px 0;\n }\n .detail-row {\n display: flex;\n justify-content: space-between;\n padding: 10px 0;\n border-bottom: 1px solid #e9ecef;\n }\n .detail-row:last-child {\n border-bottom: none;\n }\n .label {\n font-weight: 600;\n color: #495057;\n }\n .value {\n color: #212529;\n }\n .script-box {\n background: #fff3cd;\n border-left: 4px solid #ffc107;\n padding: 15px;\n margin: 20px 0;\n border-radius: 4px;\n }\n .tips {\n background: #d1ecf1;\n border-left: 4px solid #0c5460;\n padding: 15px;\n margin: 20px 0;\n border-radius: 4px;\n }\n .footer {\n text-align: center;\n margin-top: 30px;\n padding-top: 20px;\n border-top: 1px solid #dee2e6;\n font-size: 12px;\n color: #6c757d;\n }\n .keywords {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n margin: 10px 0;\n }\n .keyword-tag {\n background: #e7f3ff;\n color: #0066cc;\n padding: 4px 12px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 500;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <div class=\"emoji\">🎬</div>\n <h1>Your YouTube Short is Ready!</h1>\n <p style=\"margin: 10px 0 0 0; color: #6c757d;\">Generated with AI-powered Veo 3 technology</p>\n </div>\n\n <div class=\"video-preview\">\n <h2 style=\"margin-top: 0;\">✨ ${data.title_suggestion}</h2>\n <p style=\"font-size: 14px; opacity: 0.9;\">Duration: ${data.audio_duration_seconds}s | Resolution: 720p | Format: 9:16 (Vertical)</p>\n </div>\n\n <div style=\"text-align: center;\">\n <a href=\"${data.download_url}\" class=\"download-btn\">📥 Download Your Video</a>\n <p style=\"font-size: 12px; color: #6c757d;\">Link expires: ${new Date(data.url_expires_at).toLocaleDateString()}</p>\n </div>\n\n <div class=\"details\">\n <h3 style=\"margin-top: 0; color: #212529;\">📊 Video Details</h3>\n <div class=\"detail-row\">\n <span class=\"label\">Processing ID:</span>\n <span class=\"value\">${data.processing_id}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"label\">Target Audience:</span>\n <span class=\"value\">${data.target_audience}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"label\">Video Style:</span>\n <span class=\"value\">${visualDirection.style}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"label\">Energy Level:</span>\n <span class=\"value\">${visualDirection.energy_level}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"label\">Generated:</span>\n <span class=\"value\">${new Date(data.completion_time).toLocaleString()}</span>\n </div>\n </div>\n\n <div class=\"script-box\">\n <h3 style=\"margin-top: 0;\">📝 Video Script</h3>\n <p><strong>Hook:</strong> ${script.hook}</p>\n <p><strong>Main Content:</strong> ${script.main_content}</p>\n <p><strong>Call-to-Action:</strong> ${script.cta}</p>\n </div>\n\n <div class=\"details\">\n <h3 style=\"margin-top: 0;\">🎯 SEO Keywords</h3>\n <div class=\"keywords\">\n ${seoKeywords.map(kw => `<span class=\"keyword-tag\">#${kw}</span>`).join('')}\n </div>\n </div>\n\n <div class=\"details\">\n <h3 style=\"margin-top: 0;\">📱 Suggested YouTube Details</h3>\n <p><strong>Title:</strong> ${data.title_suggestion}</p>\n <p><strong>Description:</strong></p>\n <p style=\"white-space: pre-wrap;\">${data.description_suggestion}</p>\n </div>\n\n <div class=\"tips\">\n <h3 style=\"margin-top: 0;\">💡 Publishing Tips</h3>\n <ul style=\"margin: 10px 0; padding-left: 20px;\">\n <li>Upload during peak hours (6-9 PM your timezone)</li>\n <li>Use all suggested SEO keywords in your description</li>\n <li>Create an eye-catching thumbnail</li>\n <li>Pin a comment asking viewers to subscribe</li>\n <li>Share on Instagram Reels and TikTok for cross-platform reach</li>\n <li>Respond to comments in the first hour to boost engagement</li>\n </ul>\n </div>\n\n <div class=\"footer\">\n <p><strong>🤖 Powered by AI</strong></p>\n <p>Script: OpenAI GPT-4 | Video: Google Veo 3.1 | Voiceover: ElevenLabs</p>\n <p style=\"margin-top: 15px;\">Questions? Reply to this email for support.</p>\n </div>\n </div>\n</body>\n</html>\n`;\n\nreturn [{\n json: {\n ...data,\n email_html: html,\n email_subject: `🎬 Your YouTube Short is Ready: \"${data.title_suggestion}\"`,\n email_ready: true\n }\n}];"
},
"id": "format-email-node",
"name": "📧 Format Email Report",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-4032,
928
]
},
{
"parameters": {
"sendTo": "={{ $('🎯 Video Idea Input Form').item.json['Email for Delivery'] }}",
"subject": "={{ $json.email_subject }}",
"message": "={{ $json.email_html }}",
"options": {}
},
"id": "send-email-node",
"name": "📧 Send Email Notification",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
-3808,
928
],
"credentials": {
"gmailOAuth2": {
"id": "YOUR_GMAIL_CREDENTIAL_ID",
"name": "Gmail account"
}
}
},
{
"parameters": {
"jsCode": "// Log successful completion\nconst data = items[0].json;\n\nconsole.log('✅ YouTube Short generation completed successfully!');\nconsole.log('Processing ID:', data.processing_id);\nconsole.log('Video URL:', data.download_url);\nconsole.log('Duration:', data.audio_duration_seconds + 's');\nconsole.log('Sent to:', data.email);\n\nreturn [{\n json: {\n status: 'success',\n processing_id: data.processing_id,\n video_url: data.download_url,\n email_sent_to: data.email,\n completion_time: new Date().toISOString(),\n total_processing_time: Math.round((new Date(data.completion_time) - new Date(data.created_at)) / 1000) + 's'\n }\n}];"
},
"id": "log-success-node",
"name": "✅ Log Success",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-3584,
928
]
},
{
"parameters": {
"resource": "chat",
"model": "gpt-4o-mini",
"prompt": {
"messages": [
{
"role": "system",
"content": "You are an expert at writing prompts for Google's Veo 3 video generation model. Create detailed, cinematic prompts that specify subject, action, camera movement, composition, lighting, mood, and style. Reference the Veo 3 best practices: be descriptive, specify camera angles, include lighting details, and describe motion clearly."
},
{
"role": "user",
"content": "Create an optimized Veo 3.1 prompt for this YouTube Short:\n\n**Original Prompt:** {{ $json.veo3_scene_prompt }}\n**Script Content:** {{ $json.script.full_script }}\n**Visual Style:** {{ $json.visual_direction.style }}\n**Energy Level:** {{ $json.visual_direction.energy_level }}\n\nCreate a steady-paced but clear {{ $('🎯 Video Idea Input Form').item.json['Video Duration'] }} YouTube Short.\nVisual pacing should naturally follow the spoken narration.\n\n**Target Audience:** {{ $json['Target Audience'] }}\n\nCreate a detailed Veo 3.1 prompt that:\n1. Match the pacing and scene transitions to the spoken script content,\nensuring visuals naturally align with the narration flow.\n2. Uses appropriate visual style ({{ $json.visual_direction.style }})\n3. Specifies camera movement and composition\n4. Includes lighting and mood details\n5. Works in 9:16 vertical format (YouTube Shorts)\n6. Is optimized for the target audience\n\nReturn ONLY the optimized prompt text, no JSON, no extra commentary. Make it detailed and cinematic.\n\nIMPORTANT TIMING REQUIREMENT:\nThis video MUST run for approximately {{ $('🎯 Video Idea Input Form').item.json['Video Duration'] }}.\nDo NOT compress or summarize the narration.\nDo NOT shorten scenes to fit a shorter runtime.\nEach sentence should be given sufficient on-screen time.\nVisual pacing rules:\n- Each sentence should have its own visual beat\n- Maintain steady pacing throughout\n- Do not montage multiple ideas into one shot\n- Scene transitions should align with narration pauses\nEnd the video only after the narration fully completes."
}
]
},
"options": {
"maxTokens": 500,
"temperature": 0.4
},
"requestOptions": {}
},
"type": "n8n-nodes-base.openAi",
"typeVersion": 1,
"position": [
-6032,
928
],
"id": "enhance-prompt-node",
"name": "🎨 Enhance Veo 3 Prompt",
"credentials": {
"openAiApi": {
"id": "YOUR_OPENAI_CREDENTIAL_ID",
"name": "OpenAi account"
}
}
},
{
"parameters": {
"resource": "video",
"modelId": {
"__rl": true,
"value": "models/veo-3.1-generate-preview",
"mode": "list",
"cachedResultName": "models/veo-3.1-generate-preview"
},
"prompt": "={{ $json.veo3_enhanced_prompt }}",
"options": {
"durationSeconds": 8,
"aspectRatio": "9:16",
"personGeneration": "allow_all"
}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1,
"position": [
-5376,
928
],
"id": "veo-generation-node",
"name": "🎬 Veo 3.1 Video Generation",
"credentials": {
"googlePalmApi": {
"id": "YOUR_GOOGLE_GEMINI_CREDENTIAL_ID",
"name": "Google Gemini(PaLM) Api account"
}
}
},
{
"parameters": {
"resource": "chat",
"model": "gpt-4o",
"prompt": {
"messages": [
{
"role": "system",
"content": "You are an expert YouTube Shorts scriptwriter specializing in viral, engaging short-form content. Create scripts that hook viewers in the first 3 seconds, maintain engagement throughout, and end with a strong call-to-action. Scripts should be optimized for voiceover timing and visual pacing."
},
{
"role": "user",
"content": "Create a YouTube Shorts script for this concept:\n\n**Topic:** {{ $json['Video Topic/Idea'] }}\n**Target Audience:** {{ $json['Target Audience'] }}\n**Duration:** {{ $json['Video Duration'] }}\n**Style:** {{ $json.video_style || 'Auto-detect based on topic' }}\n\nGenerate a script with:\n1. HOOK (0-3 seconds): Attention-grabbing opening\n2. Aim for approximately 120–140 spoken words.\nMAIN CONTENT: Key points with precise timing\n3. CALL-TO-ACTION: Strong ending\n\nAlso provide:\n- Scene descriptions for video generation\n- Recommended pacing and energy level\n- Visual style recommendations (realistic, animated, cinematic, etc.)\n- Keywords for SEO\n\nFormat as JSON:\n```json\n{\n \"script\": {\n \"hook\": \"Opening line that grabs attention\",\n \"main_content\": \"Main script content with natural pauses\",\n \"cta\": \"Call to action line\",\n \"full_script\": \"Complete script for voiceover\"\n },\n \"timing\": {\n \"hook_duration\": 3,\n \"main_duration\": 24,\n \"cta_duration\": 3,\n \"total_estimated\": 30\n },\n \"visual_direction\": {\n \"style\": \"cinematic|realistic|animated|mixed\",\n \"energy_level\": \"high|medium|calm\",\n \"color_palette\": \"vibrant|muted|dramatic|natural\",\n \"camera_movement\": \"dynamic|steady|slow-motion\"\n },\n \"veo3_scene_prompt\": \"Detailed prompt for Veo 3 video generation that describes the visual scene, camera angles, lighting, mood, and action\",\n \"seo_keywords\": [\"keyword1\", \"keyword2\", \"keyword3\"],\n \"title_suggestion\": \"Catchy video title\",\n \"description_suggestion\": \"Video description for YouTube\"\n}\n```"
}
]
},
"options": {
"maxTokens": 2000,
"temperature": 0.7
},
"requestOptions": {}
},
"type": "n8n-nodes-base.openAi",
"typeVersion": 1,
"position": [
-6928,
928
],
"id": "script-generation-node",
"name": "📝 AI Script Generation",
"credentials": {
"openAiApi": {
"id": "YOUR_OPENAI_CREDENTIAL_ID",
"name": "OpenAi account"
}
}
},
{
"parameters": {
"resource": "speech",
"voice": {
"__rl": true,
"value": "CwhRBWXzGAHq8TQ4Fs17",
"mode": "list",
"cachedResultName": "Roger - Laid-Back, Casual, Resonant"
},
"text": "={{ $json.script.full_script }}",
"additionalOptions": {
"voiceSettings": "{\n \"stability\": 0.5,\n \"similarity_boost\": 0.75,\n \"style\": 0.5,\n \"use_speaker_boost\": true\n }"
},
"requestOptions": {}
},
"type": "@elevenlabs/n8n-nodes-elevenlabs.elevenLabs",
"typeVersion": 1,
"position": [
-6480,
928
],
"id": "voiceover-generation-node",
"name": "🎤 ElevenLabs Voiceover",
"credentials": {
"elevenLabsApi": {
"id": "YOUR_ELEVENLABS_CREDENTIAL_ID",
"name": "ElevenLabs account"
}
}
},
{
"parameters": {
"jsCode": "// Freeze merged context so it survives upload\nreturn [\n {\n json: items[0].json,\n binary: items[0].binary,\n },\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-4928,
928
],
"id": "prepare-upload-node",
"name": "Prepare Upload Context"
},
{
"parameters": {
"operation": "upload",
"bucketName": "youtube-shorts",
"fileName": "={{ $json.merged_video.filename }}",
"binaryPropertyName": "video",
"additionalFields": {}
},
"type": "n8n-nodes-base.s3",
"typeVersion": 1,
"position": [
-4704,
1024
],
"id": "upload-file-node",
"name": "Upload a file",
"credentials": {
"s3": {
"id": "YOUR_S3_CREDENTIAL_ID",
"name": "S3 account"
}
}
},
{
"parameters": {
"mode": "combine",
"combineBy": "combineByPosition",
"options": {}
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [
-4480,
928
],
"id": "merge-node",
"name": "Merge"
},
{
"parameters": {
"jsCode": "return [{\n json: {\n ...items[0].json,\n images: []\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-5600,
928
],
"id": "cleanup-node",
"name": "Code in JavaScript"
}
],
"pinData": {},
"connections": {
"🎯 Video Idea Input Form": {
"main": [
[
{
"node": "📝 AI Script Generation",
"type": "main",
"index": 0
}
]
]
},
"Parse Script Data": {
"main": [
[
{
"node": "🎤 ElevenLabs Voiceover",
"type": "main",
"index": 0
}
]
]
},
"Store Audio File": {
"main": [
[
{
"node": "🎨 Enhance Veo 3 Prompt",
"type": "main",
"index": 0
}
]
]
},
"🎨 Enhance Veo 3 Prompt": {
"main": [
[
{
"node": "Extract Enhanced Prompt",
"type": "main",
"index": 0
}
]
]
},
"Extract Enhanced Prompt": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
]
]
},
"🎬 Veo 3.1 Video Generation": {
"main": [
[
{
"node": "🎞️ Merge Audio + Video",
"type": "main",
"index": 0
}
]
]
},
"🎞️ Merge Audio + Video": {
"main": [
[
{
"node": "Prepare Upload Context",
"type": "main",
"index": 0
}
]
]
},
"🔗 Generate Shareable Link": {
"main": [
[
{
"node": "📧 Format Email Report",
"type": "main",
"index": 0
}
]
]
},
"📧 Send Email Notification": {
"main": [
[
{
"node": "✅ Log Success",
"type": "main",
"index": 0
}
]
]
},
"📧 Format Email Report": {
"main": [
[
{
"node": "📧 Send Email Notification",
"type": "main",
"index": 0
}
]
]
},
"📝 AI Script Generation": {
"main": [
[
{
"node": "Parse Script Data",
"type": "main",
"index": 0
}
]
]
},
"🎤 ElevenLabs Voiceover": {
"main": [
[
{
"node": "Store Audio File",
"type": "main",
"index": 0
}
]
]
},
"Prepare Upload Context": {
"main": [
[
{
"node": "Upload a file",
"type": "main",
"index": 0
},
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Upload a file": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Merge": {
"main": [
[
{
"node": "🔗 Generate Shareable Link",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "🎬 Veo 3.1 Video Generation",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"meta": {
"templateCredsSetupCompleted": false
},
"tags": [
{
"name": "YouTube Automation"
},
{
"name": "AI Content Creation"
},
{
"name": "Veo 3 Video Generation"
}
]
}🎓 FAQ
Q: Can I use this for commercial purposes?
A: Yes, but check each API’s terms of service. OpenAI and ElevenLabs allow commercial use. Ensure you have rights to any content you generate.
Q: How long does video generation take?
A: Typically 2-3 minutes total:
Script generation: 10-20 seconds
Voiceover: 5-10 seconds
Veo 3 video: 60-120 seconds
Processing: 10-20 seconds
Q: Can I customize the video style?
A: Yes! Edit the veo3-prompt-enhancement node to adjust:
Camera angles
Lighting
Color grading
Visual effects
Q: What happens if video generation fails?
A: The workflow includes error handling:
Retries up to 3 times
Logs detailed error messages
Sends notification email
Stores partial results for debugging
Q: Can I integrate with YouTube API for direct upload?
A: Absolutely! Add a YouTube upload node after video generation. See n8n YouTube documentation.
Q: How do I reduce API costs?
A: Several strategies:
Use GPT-3.5 instead of GPT-4 for scripts
Cache common prompts
Implement rate limiting
Use free tier limits first
Process in batches during off-peak hours
Q: Can I use different video models?
A: Yes! The workflow can be adapted for:
Runway Gen-2/3
Stability AI Video
Pika Labs
Other video generation APIs
Just replace the Veo 3 nodes with the appropriate API calls.
📞 Support
Getting Help:
Check Logs:
# In n8n UI: - Open workflow - Click "Executions" - View detailed logsCommunity Support:
📄 License
This workflow is provided as-is for educational and commercial use. Individual API services have their own terms of service.
🙏 Credits
Built with:
n8n - Workflow automation
Google Veo 3 - Video generation
OpenAI - Script generation
ElevenLabs - Voice synthesis
Cloudflare R2 - Video storage
Made with ❤️ for content creators worldwide 🎬
