This workflow corresponds to n8n.io template #14780 — we link there as the canonical source.
This workflow follows the Googlegemini → 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 →
{
"nodes": [
{
"id": "a5ef9b3a-452c-4ccb-b6d9-203721828019",
"name": "Daily at Noon",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-864,
-48
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 12
}
]
}
},
"typeVersion": 1.3
},
{
"id": "b80adf60-3946-46a0-aaeb-a5bccb31cbf0",
"name": "Generate Carousel Content",
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"position": [
-144,
368
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "models/gemini-3-flash-preview",
"cachedResultName": "models/gemini-3-flash-preview"
},
"options": {
"temperature": 0.9,
"thinkingBudget": 0,
"maxOutputTokens": 2048
},
"messages": {
"values": [
{
"content": "=You are the Lead Content Strategist for [BRAND_NAME], an elite tech/AI/automation brand.\n\nGenerate an Instagram carousel about a fascinating Did You Know? style tech topic.\nPick ONE from: AI breakthroughs, automation secrets, DevOps tricks, cybersecurity facts, mind-blowing tech history, or futuristic tech predictions.\n\nThe carousel MUST be exactly 6 slides. Output ONLY valid JSON (no markdown, no code fences) Important, dont use this topic's, since they alrdy been used:{{ $json.history }}\n\n{\n \"topic\": \"Main topic title (max 40 chars)\",\n \"slides\": [\n {\"num\": 1, \"type\": \"cover\", \"headline\": \"Bold hook question or shocking statement (max 60 chars)\", \"subtext\": \"Short teaser line (max 30 chars)\"},\n {\"num\": 2, \"type\": \"fact\", \"label\": \"DID YOU KNOW?\", \"body\": \"Mind-blowing fact #1. Must be genuinely surprising. (max 120 chars)\"},\n {\"num\": 3, \"type\": \"fact\", \"label\": \"THE REALITY\", \"body\": \"Deeper insight that builds on fact #1. (max 120 chars)\"},\n {\"num\": 4, \"type\": \"fact\", \"label\": \"WHY IT MATTERS\", \"body\": \"Real-world impact or consequence. (max 120 chars)\"},\n {\"num\": 5, \"type\": \"fact\", \"label\": \"PRO TIP\", \"body\": \"Actionable takeaway the reader can use today. (max 120 chars)\"},\n {\"num\": 6, \"type\": \"cta\", \"headline\": \"Want more byte-sized tech?\", \"subtext\": \"Follow @[HANDLE]\"}\n ],\n \"caption\": \"Write a punchy Instagram caption: emoji hook + 2 sentences of insight + a question to drive comments + line break + 5 niche hashtags like xxxxxxx, xxxx , xxxxx\n}\n\nQuality rules:\n- Each fact MUST be genuinely surprising and technically accurate\n- Facts should build on each other telling a mini-story\n- Tone: Smart, slightly witty, insider knowledge vibe\n- Make it feel like forbidden knowledge most people dont have access to\n- Topics should be niche enough to impress engineers but accessible enough for curious beginners"
}
]
},
"jsonOutput": true,
"builtInTools": {
"urlContext": false,
"googleSearch": true,
"codeExecution": false
}
},
"typeVersion": 1.1
},
{
"id": "e8044e16-cfc8-4bf1-949f-55d4e4f263ce",
"name": "Build Slide Image Prompts",
"type": "n8n-nodes-base.code",
"position": [
208,
368
],
"parameters": {
"jsCode": "const raw = items[0].json;\nlet content;\n\ntry {\n if (typeof raw === 'object' && raw.slides) {\n content = raw;\n } else if (raw.content && raw.content.parts && Array.isArray(raw.content.parts)) {\n let jsonText = '';\n for (const part of raw.content.parts) {\n if (part.thought) continue;\n if (part.text) jsonText = part.text;\n }\n jsonText = jsonText.replace(/```json\\s*/gi, '').replace(/```\\s*/g, '').trim();\n content = JSON.parse(jsonText);\n } else if (typeof raw === 'string') {\n const cleaned = raw.replace(/```json\\s*/gi, '').replace(/```\\s*/g, '').trim();\n content = JSON.parse(cleaned);\n } else if (typeof raw.content === 'string') {\n const cleaned = raw.content.replace(/```json\\s*/gi, '').replace(/```\\s*/g, '').trim();\n content = JSON.parse(cleaned);\n } else if (raw.text && typeof raw.text === 'string') {\n const cleaned = raw.text.replace(/```json\\s*/gi, '').replace(/```\\s*/g, '').trim();\n content = JSON.parse(cleaned);\n } else {\n const str = JSON.stringify(raw);\n const slidesMatch = str.match(/\"slides\"\\s*:\\s*\\[/);\n if (slidesMatch) {\n let startIdx = str.indexOf(slidesMatch[0]);\n for (let i = startIdx; i >= 0; i--) {\n if (str[i] === '{') { startIdx = i; break; }\n }\n let depth = 0, endIdx = startIdx;\n for (let i = startIdx; i < str.length; i++) {\n if (str[i] === '{') depth++;\n if (str[i] === '}') { depth--; if (depth === 0) { endIdx = i + 1; break; } }\n }\n content = JSON.parse(str.substring(startIdx, endIdx));\n } else {\n throw new Error('No slides data found in input');\n }\n }\n} catch (e) {\n throw new Error('Failed to parse carousel content: ' + e.message + '. Raw keys: ' + Object.keys(raw));\n}\n\nif (!content || !content.slides || content.slides.length === 0) {\n throw new Error('No slides found. Parsed content keys: ' + (content ? Object.keys(content) : 'null'));\n}\n\nconst avatar = 'a small 8-bit pixel art avatar character with neutral styling, dark hair, expressive eyes, slight confident smirk, wearing a dark hoodie with a simple emblem, anime pixel-art style';\n\nconst brandBase = 'Create a perfectly square 1080x1080 pixel Instagram carousel slide. ABSOLUTE RULES: Pure solid black (#000000) background filling the entire canvas. ALL text rendered in clean white (#FFFFFF) sans-serif font. Accent colors ONLY for thin decorative elements: cyber-purple (#7B2FBE) and electric blue (#00D4FF). Design must be ultra-clean, modern, minimal. NO photographs, NO 3D renders, NO gradients on background. Only flat graphic design, clean typography, and subtle geometric accents (thin neon lines, small dots, angular corner decorations). ';\n\nconst slides = content.slides;\nconst output = [];\n\nfor (const slide of slides) {\n let prompt = brandBase;\n\n if (slide.type === 'cover') {\n prompt += 'Include ' + avatar + ' positioned in the bottom-right area, sized about 25% of the slide, looking excited and pointing upward. ';\n prompt += 'LAYOUT: Large bold white headline text ' + (slide.headline || '') + ' centered in the upper 60% of the slide. ';\n prompt += 'Smaller text below in electric blue: ' + (slide.subtext || '') + '. ';\n prompt += 'A thin glowing purple (#7B2FBE) accent line under the headline. Very thin purple border around entire slide. ';\n prompt += 'Tiny @[HANDLE] watermark at bottom-left in dark gray. Subtle dot grid pattern barely visible in corners.';\n } else if (slide.type === 'cta') {\n prompt += 'Include ' + avatar + ' centered in the middle of the slide, larger (about 35% of slide), waving with a friendly expression. ';\n prompt += 'LAYOUT: Bold text ' + (slide.headline || '') + ' at the top in white. ';\n prompt += 'Below the avatar: ' + (slide.subtext || '') + ' in electric blue (#00D4FF). ';\n prompt += 'A subtle right-pointing arrow icon (swipe indicator). Glowing thin purple border. Subtle futuristic grid pattern in background.';\n } else {\n prompt += 'Include ' + avatar + ' very small (about 10% of slide) in the top-right corner. ';\n prompt += 'LAYOUT: Label text ' + (slide.label || 'DID YOU KNOW?') + ' at the top in electric blue (#00D4FF), smaller font. ';\n prompt += 'A thin purple horizontal line separator below the label. ';\n prompt += 'Main body text: ' + (slide.body || '') + ' in large bold white text, centered vertically in the middle 60% of the slide. ';\n prompt += 'Slide number ' + (slide.num || '') + '/6 very small at the bottom-center in dark gray. Subtle angular decorations in bottom corners.';\n }\n\n output.push({\n json: {\n slideNum: slide.num,\n slideType: slide.type,\n imagePrompt: prompt,\n caption: content.caption || '',\n topic: content.topic || ''\n }\n });\n}\n\nreturn output;"
},
"typeVersion": 2
},
{
"id": "1adbef92-6c5f-4f68-88b5-75b71912330c",
"name": "Generate Slide Images",
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"position": [
480,
-64
],
"parameters": {
"prompt": "={{ $json.imagePrompt }}",
"modelId": {
"__rl": true,
"mode": "list",
"value": "models/gemini-3-pro-image-preview",
"cachedResultName": "models/gemini-3-pro-image-preview"
},
"options": {},
"resource": "image"
},
"typeVersion": 1.1
},
{
"id": "5e7196fe-fdff-4de6-b02b-bdf317dc636f",
"name": "Prepare Slack Package",
"type": "n8n-nodes-base.code",
"position": [
704,
-64
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const slideNum = $json.slideNum || ($itemIndex + 1);\nconst caption = $json.caption || '';\nconst topic = $json.topic || '';\n\nconst comment = slideNum === 1\n ? '\\u{1F4F1} *NEW CAROUSEL READY* \\u2014 ' + topic + '\\n\\n' + caption + '\\n\\n_Slide ' + slideNum + '/6_'\n : '_Slide ' + slideNum + '/6_';\n\nreturn {\n json: {\n ...$json,\n slackComment: comment,\n fileName: 'carousel_slide_' + String(slideNum).padStart(2, '0') + '.png'\n },\n binary: $binary\n};"
},
"typeVersion": 2
},
{
"id": "ce0d2eb2-64f3-460c-b873-395eb837e625",
"name": "Upload Slides to Slack",
"type": "n8n-nodes-base.slack",
"position": [
928,
-64
],
"parameters": {
"options": {
"fileName": "={{ $json.fileName }}",
"channelId": "[SLACK_CHANNEL_ID]",
"initialComment": "={{ $json.slackComment }}"
},
"resource": "file",
"authentication": "oAuth2"
},
"typeVersion": 2.4
},
{
"id": "f6a5d0c7-8db2-420a-8c49-e0c7ae15be2a",
"name": "Get row(s) in sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
-640,
-48
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "[GOOGLE_SHEET_TAB_URL]",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "[GOOGLE_SHEET_ID]",
"cachedResultUrl": "[GOOGLE_SHEET_URL]",
"cachedResultName": "[SHEET_NAME]"
}
},
"typeVersion": 4.7
},
{
"id": "dc4bc875-93e0-44e8-bbb9-4b0401ecd871",
"name": "Code in JavaScript",
"type": "n8n-nodes-base.code",
"position": [
-416,
-48
],
"parameters": {
"jsCode": "// Map the input items into a single string\n// Note: \"Topic\" must match your Google Sheet column name exactly!\nconst history = items.map(i => i.json.Topic).filter(t => t).join(', ');\n\nreturn { \n history: history || \"None yet\" \n};"
},
"typeVersion": 2
},
{
"id": "9d0cb24d-8a9f-429c-a215-e4bdb6df9c5d",
"name": "Append row in sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
1152,
-64
],
"parameters": {
"columns": {
"value": {
"Topic": "={{ $('Build Slide Image Prompts').item.json.topic }}"
},
"schema": [
{
"id": "Topic",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Topic",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"row_number"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "[GOOGLE_SHEET_TAB_URL]",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "[GOOGLE_SHEET_ID]",
"cachedResultUrl": "[GOOGLE_SHEET_URL]",
"cachedResultName": "[SHEET_NAME]"
}
},
"executeOnce": true,
"typeVersion": 4.7
},
{
"id": "7b70adb3-bc64-4b25-ac6c-782cbc999d54",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
464,
-256
],
"parameters": {
"color": 4,
"width": 880,
"height": 400,
"content": "### \ud83d\ude80 03: RENDERING & DELIVERY\n**Goal:** Final media output and logging.\n* **Rendering:** Gemini Pro Image creates the final 1080x1080 PNGs.\n* **Slack:** Pushes the carousel as a thread/package for review.\n* **Loop Closure:** Updates the Google Sheet so the AI knows not to repeat this topic."
},
"typeVersion": 1
},
{
"id": "5ffddb14-ca81-4b8f-9618-18636f37f7cf",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-752,
-208
],
"parameters": {
"color": 6,
"width": 496,
"height": 304,
"content": "### \ud83d\udd0d 01: DATA & CONTEXT\n**Goal:** Ensure content is unique and niche-relevant.\n* **Deduplication:** The workflow reads the Sheet to see what has already been \"done.\"\n* **Prompting:** The history is injected into the AI prompt as \"Negative Context.\""
},
"typeVersion": 1
},
{
"id": "3cf87288-e465-44ac-9726-43c2b2c666aa",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-160,
160
],
"parameters": {
"width": 528,
"height": 352,
"content": "### \u270d\ufe0f 02: THE CREATIVE ENGINE\n**Goal:** Content and Design logic.\n* **Copywriting:** Generates slides with a specific narrative arc (Hook \u2192 Fact \u2192 Reality \u2192 CTA).\n* **Image Logic:** A custom JS bridge translates slide text into detailed visual prompts for the Image model."
},
"typeVersion": 1
},
{
"id": "2c2c70a7-6edd-4f35-8dc5-9f0593708f8a",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1248,
-352
],
"parameters": {
"width": 352,
"height": 832,
"content": "# \ud83d\ude80 AI Carousel Generator\n**Automated Instagram Content Engine**\n\n### \ud83d\udee0\ufe0f HOW IT WORKS\n1. **Trigger:** Fires daily at noon.\n2. **Memory:** Checks a Google Sheet for previously used topics to avoid duplicates.\n3. **Ideation:** Gemini 3 Flash generates a 6-slide carousel structure in JSON format.\n4. **Visuals:** A custom JavaScript engine builds branding-specific image prompts.\n5. **Creation:** Gemini 3 Pro Image generates 6 unique high-fidelity 1080x1080 slides.\n6. **Delivery:** Packages slides and the caption, then sends them to Slack.\n7. **Logging:** Saves the new topic back to Google Sheets.\n\n### \u2699\ufe0f HOW TO SET UP\n* **Google Sheets:** Create a sheet with a column header named `Topic`.\n* **Credentials:** Connect your Google Service Account, Gemini API, and Slack OAuth.\n* **Slack:** Replace `[SLACK_CHANNEL_ID]` with your target channel ID.\n* **Variables:** Update the `[BRAND_NAME]` and `[HANDLE]` in the Gemini prompt.\n\n### \ud83c\udfa8 CUSTOMIZATION\n* **Visual Style:** Edit the `brandBase` constant in the **Build Slide Image Prompts** node to change colors or themes.\n* **Avatar:** Change the `avatar` constant in the code node to match your brand persona.\n* **Schedule:** Change the **Daily at Noon** trigger to your preferred posting frequency."
},
"typeVersion": 1
},
{
"id": "9d8cfd69-146a-47b0-96ee-659a6b65e636",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
336,
384
],
"parameters": {
"color": 3,
"width": 256,
"height": 208,
"content": "### \u26a0\ufe0f IMPORTANT\n**JSON Parsing:** Gemini occasionally adds markdown code fences (```json). \nThis node contains a robust regex cleaner to prevent the workflow from crashing if the AI returns non-standard formatting."
},
"typeVersion": 1
}
],
"connections": {
"Daily at Noon": {
"main": [
[
{
"node": "Get row(s) in sheet",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "Generate Carousel Content",
"type": "main",
"index": 0
}
]
]
},
"Get row(s) in sheet": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
]
]
},
"Generate Slide Images": {
"main": [
[
{
"node": "Prepare Slack Package",
"type": "main",
"index": 0
}
]
]
},
"Prepare Slack Package": {
"main": [
[
{
"node": "Upload Slides to Slack",
"type": "main",
"index": 0
}
]
]
},
"Upload Slides to Slack": {
"main": [
[
{
"node": "Append row in sheet",
"type": "main",
"index": 0
}
]
]
},
"Build Slide Image Prompts": {
"main": [
[
{
"node": "Generate Slide Images",
"type": "main",
"index": 0
}
]
]
},
"Generate Carousel Content": {
"main": [
[
{
"node": "Build Slide Image Prompts",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow automatically generates a 6-slide Instagram carousel about tech topics(you can edit the prompt how you like to make it for best for your usecase) every day at noon(you can set whatever time you want or manually use it), creates images for each slide using AI,…
Source: https://n8n.io/workflows/14780/ — 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.
Automatically monitors restaurant ratings on Google Places daily, detects meaningful changes, uses Google Gemini AI to diagnose the root cause from real customer reviews, and delivers smart alerts to
AI Institutional Stock Valuation Engine with Risk Scoring & Scenario Targets
Overview This is a production-grade, fully automated stock analysis system built entirely in n8n. It combines institutional-level financial analysis, dual AI model consensus, and a self-improving back
This workflow is a complete outbound automation system that discovers local businesses, extracts contact emails, generates personalized cold emails using AI, and runs a multi-step follow-up sequence —
A professional AI equity analysis automation built on n8n that transforms structured financial data and real-time news into disciplined, risk-adjusted price targets and actionable BUY/HOLD/SELL signal