This workflow corresponds to n8n.io template #16652 — we link there as the canonical source.
This workflow follows the Chainllm → 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 →
{
"meta": {
"templateId": "",
"templateCredsSetupCompleted": false
},
"name": "Multi-Agent YouTube \u2192 SEO Blog Factory v2 (Native Gemini Nodes)",
"nodes": [
{
"id": "2a73e6b3-edc0-4980-933e-9f5c7a8e28e0",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-432,
32
],
"parameters": {
"width": 480,
"height": 896,
"content": "## Multi-Agent YouTube \u2192 SEO Blog Factory v2 (Native Gemini Nodes)\n\n### How it works\n\nThis workflow turns a submitted YouTube URL into an SEO-oriented blog article using a multi-agent Gemini pipeline. It validates the video ID, fetches YouTube metadata, prepares context, then uses separate AI stages for research, writing, and quality review. A quality gate publishes high-scoring articles to Google Docs and logs them in Google Sheets, while lower-scoring drafts are routed for human review via Telegram.\n\n### Setup steps\n\n- Configure the Webhook node with the desired production URL and expected request payload containing the YouTube URL.\n- Add YouTube Data API credentials or API key in the HTTP Request node and confirm the metadata endpoint parameters match the extracted video ID.\n- Configure Google Gemini credentials for all Gemini Flash model sub-nodes used by the research, writer, and editor chains.\n- Connect Google Docs and Google Sheets credentials, and set the target document or folder plus logging spreadsheet fields.\n- Configure Telegram bot credentials and chat IDs for both published and needs-review notifications.\n- Verify the quality threshold and webhook response formats before activating the workflow.\n\n### Customization\n\nAdjust the prompts in each LLM chain, the quality score threshold in the IF node, the Google Docs formatting destination, and the Google Sheets logging columns to fit your content workflow."
},
"typeVersion": 1
},
{
"id": "cc97af3a-cd93-4c80-a250-9522eb32d273",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
128,
160
],
"parameters": {
"color": 7,
"width": 848,
"height": 304,
"content": "## Receive and prepare video\n\nAccepts the incoming YouTube URL, extracts and validates the video ID, fetches video metadata from the YouTube Data API, and formats the resulting context for downstream AI agents."
},
"typeVersion": 1
},
{
"id": "e1a9dd56-e527-46aa-a9c9-467599a73c73",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1008,
128
],
"parameters": {
"color": 7,
"width": 416,
"height": 512,
"content": "## Research and structure\n\nUses the first Gemini-powered LLM chain to analyze the prepared video context, generate research and article structure, then parses the agent output into usable JSON."
},
"typeVersion": 1
},
{
"id": "155830cb-5baa-450b-af7d-80575996d7a8",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1456,
144
],
"parameters": {
"color": 7,
"width": 352,
"height": 480,
"content": "## Draft blog article\n\nUses the writer Gemini chain to transform the research structure into a full SEO blog post draft."
},
"typeVersion": 1
},
{
"id": "04db789d-68ad-4b58-a51c-6abd82339c18",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1840,
144
],
"parameters": {
"color": 7,
"width": 640,
"height": 480,
"content": "## Review and quality gate\n\nRuns the editor Gemini chain to review and score the draft, parses the score while attaching the article, then branches execution based on whether the score meets the publishing threshold."
},
"typeVersion": 1
},
{
"id": "04c16021-1bf8-4f28-a946-ee39de6a1ca8",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
2512,
32
],
"parameters": {
"color": 7,
"width": 848,
"height": 304,
"content": "## Publish approved article\n\nHandles the success branch by publishing the approved article to Google Docs, logging the result in Google Sheets, notifying Telegram, and returning a published webhook response."
},
"typeVersion": 1
},
{
"id": "6dfaf59a-01a6-48b5-a0ee-ab8bbd45538f",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
2512,
368
],
"parameters": {
"color": 7,
"width": 416,
"height": 336,
"content": "## Flag manual review\n\nHandles the failure branch for articles below the quality threshold by sending a Telegram review notification and returning a needs-review webhook response."
},
"typeVersion": 1
},
{
"id": "node-webhook",
"name": "When YouTube URL Received",
"type": "n8n-nodes-base.webhook",
"position": [
180,
300
],
"parameters": {
"path": "youtube-blog-factory",
"options": {},
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "node-validate",
"name": "Validate YouTube Video ID",
"type": "n8n-nodes-base.code",
"position": [
400,
300
],
"parameters": {
"jsCode": "const body = $input.item.json.body || $input.item.json;\nconst url = body.youtube_url || '';\nconst keyword = body.target_keyword || '';\nconst tone = body.tone || 'professional and informative';\n\nconst patterns = [\n /youtube\\.com\\/watch\\?v=([^&]+)/,\n /youtu\\.be\\/([^?]+)/,\n /youtube\\.com\\/embed\\/([^?]+)/\n];\nlet video_id = null;\nfor (const p of patterns) {\n const m = url.match(p);\n if (m) { video_id = m[1]; break; }\n}\nif (!video_id) throw new Error('Invalid YouTube URL: ' + url);\n\nreturn [{ json: { video_id, youtube_url: url, keyword, tone } }];"
},
"typeVersion": 2
},
{
"id": "node-yt-api",
"name": "Fetch YouTube Video Metadata",
"type": "n8n-nodes-base.httpRequest",
"position": [
620,
300
],
"parameters": {
"url": "=https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id={{ $json.video_id }}&key={{ $credentials.googleYoutubeApiKey }}",
"method": "GET",
"options": {}
},
"typeVersion": 4
},
{
"id": "node-prep",
"name": "Prepare Agent Context Data",
"type": "n8n-nodes-base.code",
"position": [
840,
300
],
"parameters": {
"jsCode": "const ytResp = $input.item.json;\nconst input = $('Validate YouTube Video ID').item.json;\nconst snippet = ytResp.items?.[0]?.snippet || {};\n\nconst title = snippet.title || 'Unknown Title';\nconst description = (snippet.description || '').substring(0, 800);\nconst channel = snippet.channelTitle || 'Unknown';\nconst tags = (snippet.tags || []).slice(0, 10).join(', ');\n\nreturn [{\n json: {\n video_id: input.video_id,\n youtube_url: input.youtube_url,\n keyword: input.keyword,\n tone: input.tone,\n title,\n description,\n channel,\n tags,\n video_context: `Title: ${title}\\nChannel: ${channel}\\nDescription: ${description}\\nTags: ${tags}\\nTarget keyword: ${input.keyword || 'not specified'}\\nTone: ${input.tone}`\n }\n}];"
},
"typeVersion": 2
},
{
"id": "node-research-chain",
"name": "Research Structuring Agent",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
1060,
300
],
"parameters": {
"text": "=You are an expert SEO content researcher. Analyse this YouTube video and return a structured research brief as valid JSON only \u2014 no preamble, no markdown fences.\n\nVIDEO CONTEXT:\n{{ $json.video_context }}\n\nReturn ONLY this JSON structure:\n{\n \"main_topic\": \"one sentence\",\n \"seo_title\": \"60 chars max, includes keyword if provided\",\n \"meta_description\": \"155 chars max\",\n \"h2_headings\": [\"heading 1\", \"heading 2\", \"heading 3\", \"heading 4\"],\n \"primary_keyword\": \"main keyword\",\n \"secondary_keywords\": [\"kw1\", \"kw2\", \"kw3\"],\n \"key_insights\": [\"insight 1\", \"insight 2\", \"insight 3\", \"insight 4\"],\n \"target_audience\": \"who should read this\",\n \"content_angle\": \"unique hook or angle\"\n}",
"messages": {
"messageValues": []
},
"promptType": "define",
"hasOutputParser": false
},
"typeVersion": 1.5
},
{
"id": "node-gemini-1",
"name": "Research Assistance with Gemini",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
1060,
480
],
"parameters": {
"options": {
"temperature": 0.3,
"maxOutputTokens": 1000
},
"modelName": "models/gemini-1.5-flash"
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "node-parse-research",
"name": "Analyze Research JSON",
"type": "n8n-nodes-base.code",
"position": [
1280,
300
],
"parameters": {
"jsCode": "const raw = $input.item.json.text || $input.item.json.output || '';\nconst clean = raw.replace(/```json/g,'').replace(/```/g,'').trim();\nlet research;\ntry { research = JSON.parse(clean); }\ncatch(e) { research = { seo_title: $('Prepare Agent Context Data').item.json.title, h2_headings: [], key_insights: [], primary_keyword: '' }; }\n\nconst ctx = $('Prepare Agent Context Data').item.json;\nreturn [{ json: { ...ctx, research } }];"
},
"typeVersion": 2
},
{
"id": "node-writer-chain",
"name": "Blog Content Writer Agent",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
1500,
300
],
"parameters": {
"text": "=You are an expert SEO blog writer. Write a complete, publish-ready blog post. No preamble \u2014 start directly with the title.\n\nRESEARCH BRIEF:\nSEO Title: {{ $json.research.seo_title }}\nContent Angle: {{ $json.research.content_angle }}\nTarget Audience: {{ $json.research.target_audience }}\nH2 Headings: {{ JSON.stringify($json.research.h2_headings) }}\nKey Insights: {{ JSON.stringify($json.research.key_insights) }}\nPrimary Keyword: {{ $json.research.primary_keyword }}\nSecondary Keywords: {{ JSON.stringify($json.research.secondary_keywords) }}\nTone: {{ $json.tone }}\nOriginal Video: {{ $json.youtube_url }}\n\nRULES:\n- Open with a hook that establishes why this matters now\n- Use the H2 headings as section structure\n- 2-3 flowing paragraphs per section (no bullet lists as primary format)\n- Weave primary keyword in naturally 3-5 times\n- Include a concrete example in at least 2 sections\n- Close with a clear takeaway and soft CTA\n- Final line: [Watch the original video]({{ $json.youtube_url }})\n- Target 1,000-1,400 words\n- DO NOT use phrases like 'in conclusion' or 'in today's digital world'",
"messages": {
"messageValues": []
},
"promptType": "define",
"hasOutputParser": false
},
"typeVersion": 1.5
},
{
"id": "node-gemini-2",
"name": "Writing Assistance with Gemini",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
1500,
480
],
"parameters": {
"options": {
"temperature": 0.7,
"maxOutputTokens": 2500
},
"modelName": "models/gemini-1.5-flash"
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "node-editor-chain",
"name": "Quality Review Agent",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
1888,
304
],
"parameters": {
"text": "=You are a senior content editor. Score this blog post and return a JSON review only \u2014 no preamble, no markdown fences.\n\nBLOG POST:\n{{ $json.text || $json.output }}\n\nPRIMARY KEYWORD: {{ $json.research.primary_keyword }}\nTARGET AUDIENCE: {{ $json.research.target_audience }}\n\nReturn ONLY this JSON:\n{\n \"overall_score\": 8,\n \"scores\": {\n \"seo\": 8,\n \"readability\": 9,\n \"depth\": 7,\n \"hook\": 8,\n \"actionability\": 7\n },\n \"publish_ready\": true,\n \"critical_issues\": [],\n \"improvements\": [\"improvement 1\", \"improvement 2\"],\n \"editor_note\": \"one sentence on the article's main strength\"\n}",
"messages": {
"messageValues": []
},
"promptType": "define",
"hasOutputParser": false
},
"typeVersion": 1.5
},
{
"id": "node-gemini-3",
"name": "Editing Assistance with Gemini",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
2048,
480
],
"parameters": {
"options": {
"temperature": 0.2,
"maxOutputTokens": 600
},
"modelName": "models/gemini-1.5-flash"
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "node-parse-review",
"name": "Review and Attach Article",
"type": "n8n-nodes-base.code",
"position": [
2096,
304
],
"parameters": {
"jsCode": "// Editor output\nconst editorRaw = $input.item.json.text || $input.item.json.output || '{}';\nconst editorClean = editorRaw.replace(/```json/g,'').replace(/```/g,'').trim();\nlet review;\ntry { review = JSON.parse(editorClean); }\ncatch(e) { review = { overall_score: 8, publish_ready: true, improvements: [], editor_note: 'Good article', critical_issues: [] }; }\n\n// Article comes from Agent 2 output \u2014 retrieve via node reference\nconst writerOutput = $('Blog Content Writer Agent').item.json;\nconst article = writerOutput.text || writerOutput.output || '';\n\n// Context\nconst ctx = $('Analyze Research JSON').item.json;\n\nreturn [{\n json: {\n ...ctx,\n article,\n word_count: article.split(/\\s+/).filter(w => w).length,\n review,\n quality_score: review.overall_score || 7\n }\n}];"
},
"typeVersion": 2
},
{
"id": "node-gate",
"name": "Check Quality Score \u2265 7",
"type": "n8n-nodes-base.if",
"position": [
2320,
304
],
"parameters": {
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "q1",
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ $json.quality_score }}",
"rightValue": 7
}
]
}
},
"typeVersion": 2
},
{
"id": "node-gdocs",
"name": "Publish Article to Google Docs",
"type": "n8n-nodes-base.googleDocs",
"position": [
2560,
160
],
"parameters": {
"title": "={{ $json.research.seo_title || $json.title }}",
"content": "={{ $json.article }}",
"options": {},
"operation": "create"
},
"typeVersion": 2
},
{
"id": "node-sheets",
"name": "Append Article Log to Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
2784,
160
],
"parameters": {
"columns": {
"value": {
"Date": "={{ new Date().toISOString().split('T')[0] }}",
"Blog Title": "={{ $json.research.seo_title }}",
"Google Doc": "={{ $json.webViewLink || 'created' }}",
"Word Count": "={{ $json.word_count }}",
"Editor Note": "={{ $json.review.editor_note }}",
"Video Title": "={{ $json.title }}",
"YouTube URL": "={{ $json.youtube_url }}",
"Quality Score": "={{ $json.quality_score }}",
"Primary Keyword": "={{ $json.research.primary_keyword }}"
},
"mappingMode": "defineBelow"
},
"operation": "append",
"sheetName": {
"mode": "name",
"value": "Blog Posts"
},
"documentId": {
"mode": "id",
"value": "YOUR_SHEETS_ID"
}
},
"typeVersion": 4
},
{
"id": "node-tg-pass",
"name": "Notify via Telegram - Published",
"type": "n8n-nodes-base.telegram",
"position": [
2992,
160
],
"parameters": {
"text": "=\u2705 *Blog Post Published*\n\n*Title:* {{ $json.research.seo_title }}\n*Score:* {{ $json.quality_score }}/10\n*Words:* {{ $json.word_count }}\n*Keyword:* {{ $json.research.primary_keyword }}\n\n_{{ $json.review.editor_note }}_\n\n\ud83d\udcc4 Saved to Google Docs",
"chatId": "YOUR_TELEGRAM_CHAT_ID",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"typeVersion": 1
},
{
"id": "node-respond-pass",
"name": "Respond with Success Status",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
3216,
160
],
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ status: 'published', title: $json.research.seo_title, score: $json.quality_score, word_count: $json.word_count }) }}"
},
"typeVersion": 1
},
{
"id": "node-tg-fail",
"name": "Notify via Telegram - Needs Review",
"type": "n8n-nodes-base.telegram",
"position": [
2560,
544
],
"parameters": {
"text": "=\u26a0\ufe0f *Draft Needs Review \u2014 Score {{ $json.quality_score }}/10*\n\n*Title:* {{ $json.research.seo_title }}\n\n*Issues:*\n{{ ($json.review.critical_issues || []).map(i => '\u2022 ' + i).join('\\n') || 'None listed' }}\n\n*Suggested improvements:*\n{{ ($json.review.improvements || []).map(i => '\u2022 ' + i).join('\\n') }}\n\nReview and publish manually.",
"chatId": "YOUR_TELEGRAM_CHAT_ID",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"typeVersion": 1
},
{
"id": "node-respond-fail",
"name": "Respond with Review Needed Status",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
2784,
544
],
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ status: 'needs_review', score: $json.quality_score, issues: $json.review.critical_issues }) }}"
},
"typeVersion": 1
}
],
"settings": {
"executionOrder": "v1"
},
"connections": {
"Quality Review Agent": {
"main": [
[
{
"node": "Review and Attach Article",
"type": "main",
"index": 0
}
]
]
},
"Analyze Research JSON": {
"main": [
[
{
"node": "Blog Content Writer Agent",
"type": "main",
"index": 0
}
]
]
},
"Blog Content Writer Agent": {
"main": [
[
{
"node": "Quality Review Agent",
"type": "main",
"index": 0
}
]
]
},
"Check Quality Score \u2265 7": {
"main": [
[
{
"node": "Publish Article to Google Docs",
"type": "main",
"index": 0
}
],
[
{
"node": "Notify via Telegram - Needs Review",
"type": "main",
"index": 0
}
]
]
},
"Review and Attach Article": {
"main": [
[
{
"node": "Check Quality Score \u2265 7",
"type": "main",
"index": 0
}
]
]
},
"Validate YouTube Video ID": {
"main": [
[
{
"node": "Fetch YouTube Video Metadata",
"type": "main",
"index": 0
}
]
]
},
"When YouTube URL Received": {
"main": [
[
{
"node": "Validate YouTube Video ID",
"type": "main",
"index": 0
}
]
]
},
"Prepare Agent Context Data": {
"main": [
[
{
"node": "Research Structuring Agent",
"type": "main",
"index": 0
}
]
]
},
"Research Structuring Agent": {
"main": [
[
{
"node": "Analyze Research JSON",
"type": "main",
"index": 0
}
]
]
},
"Append Article Log to Sheets": {
"main": [
[
{
"node": "Notify via Telegram - Published",
"type": "main",
"index": 0
},
{
"node": "Respond with Success Status",
"type": "main",
"index": 0
}
]
]
},
"Fetch YouTube Video Metadata": {
"main": [
[
{
"node": "Prepare Agent Context Data",
"type": "main",
"index": 0
}
]
]
},
"Editing Assistance with Gemini": {
"ai_languageModel": [
[
{
"node": "Quality Review Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Publish Article to Google Docs": {
"main": [
[
{
"node": "Append Article Log to Sheets",
"type": "main",
"index": 0
}
]
]
},
"Writing Assistance with Gemini": {
"ai_languageModel": [
[
{
"node": "Blog Content Writer Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Research Assistance with Gemini": {
"ai_languageModel": [
[
{
"node": "Research Structuring Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Notify via Telegram - Needs Review": {
"main": [
[
{
"node": "Respond with Review Needed Status",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
googlePalmApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Submit a YouTube URL and get a publish-ready SEO blog post. Three Gemini agents research the video, write the article, and score it. Articles scoring 7+ auto-publish to Google Docs. Lower scores route to Telegram for review. The workflow starts when a POST request hits the…
Source: https://n8n.io/workflows/16652/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
leads. Uses supabase, gmail, formTrigger, httpRequest. Webhook trigger; 62 nodes.
This workflow turns a spreadsheet row into a fully formatted, media-rich WordPress article. It pulls the outline and brand context from Google Sheets/Docs, drafts the article with Anthropic or Gemini,
This workflow creates a multi-talented AI assistant named Simran that interacts with users via Telegram. It can handle text and voice messages, understand the user's intent, and perform various tasks.
ANIS_HUB 1. Uses gmail, googleDrive, googleSheets, httpRequest. Webhook trigger; 89 nodes.
Cyber Zap Public. Uses httpRequest, chainLlm, outputParserStructured, telegram. Scheduled trigger; 69 nodes.