This workflow corresponds to n8n.io template #6713 — we link there as the canonical source.
This workflow follows the Chainllm → Gmail 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": {
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "e5c20787-21b2-4fc5-ab07-1d94d1c19f22",
"name": "Create Google Doc",
"type": "n8n-nodes-base.googleDocs",
"position": [
3140,
1140
],
"parameters": {
"title": "={{ $json.title }}",
"folderId": "1uOuxdhJ2w64JPJGBZhRo8-k4_cd8B_JA"
},
"credentials": {
"googleDocsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "df72645e-cee2-4196-8dec-0d66fd843ed0",
"name": "Check Approval Status",
"type": "n8n-nodes-base.if",
"position": [
5100,
1060
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition-approval",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.data.approved }}",
"rightValue": "true"
}
]
}
},
"typeVersion": 2
},
{
"id": "6adb13c6-c613-49e0-8da5-21a2f1cf3de5",
"name": "Publish to Medium",
"type": "n8n-nodes-base.httpRequest",
"disabled": true,
"position": [
5380,
1060
],
"parameters": {
"url": "http://your-vps-ip:8000/rpc",
"method": "POST",
"options": {},
"sendBody": true,
"sendHeaders": true,
"bodyParameters": {
"parameters": [
{
"name": "jsonrpc",
"value": "2.0"
},
{
"name": "method",
"value": "createMediumPost"
},
{
"name": "params",
"value": "={{ {\n \"title\": $('The Verge \u2013 Tech').item.json.title,\n \"content\": $('Generate Blog Article').item.json.choices[0].message.content + '<br><img src=\"' + $('Image Generator').item.json.data[0].url + '\">',\n \"tags\": [\"AI\", \"Automation\", \"Tech\"],\n \"publishStatus\": \"public\"\n} }}"
},
{
"name": "id",
"value": "1"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.1
},
{
"id": "a1026202-fb3d-45e4-ae3f-748d57ca1e59",
"name": "Rejection Notification",
"type": "n8n-nodes-base.slack",
"position": [
5320,
1260
],
"parameters": {
"text": "\u274c **Article Rejected**\n\n",
"user": {
"__rl": true,
"mode": "username",
"value": ""
},
"select": "user",
"otherOptions": {}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "49301421-b71f-4851-b29a-9e28d9b93d53",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-120,
940
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 6,
"triggerAtMinute": 10
}
]
}
},
"typeVersion": 1.2
},
{
"id": "a50ac409-0d3f-42a0-8815-fed78c470b5a",
"name": "TechCrunch",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
480,
760
],
"parameters": {
"url": "https://techcrunch.com/feed",
"options": {}
},
"typeVersion": 1
},
{
"id": "7c0cbeb1-b3db-4d84-aacc-6d8d69496b1a",
"name": "The Verge \u2013 Tech",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
80,
840
],
"parameters": {
"url": "https://www.theverge.com/rss/index.xml",
"options": {}
},
"typeVersion": 1
},
{
"id": "cf7b1583-091a-44f8-90a4-b6a2bcdcbafe",
"name": "Ars Technica \u2013 Technology Lab",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
280,
780
],
"parameters": {
"url": "https://feeds.arstechnica.com/arstechnica/technology-lab",
"options": {}
},
"typeVersion": 1
},
{
"id": "36c0f639-cd05-42a8-bc17-f7a64caf0d40",
"name": "Basic LLM Chain",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
1360,
920
],
"parameters": {
"text": "=Generate a well formatted Medium Post with the content.\n {{ $json.title }}{{ $json.content }}{{ $json.contentSnippet }}{{ $json.link }}",
"batching": {},
"messages": {
"messageValues": [
{
"message": "=[{\"role\": \"system\", \"content\": \"You are a professional tech blogger. Transform the RSS feed content into an engaging Medium article with proper formatting, introduction, and conclusion. Keep it informative yet accessible.\"}, {\"role\": \"user\", \"content\": \"Title: {{ $json.title }}\\n\\nSummary: {{ $json.contentSnippet }}\\n\\nWrite a comprehensive blog post about this topic.\"}]"
}
]
},
"promptType": "define"
},
"typeVersion": 1.7
},
{
"id": "dc564a60-340f-44f1-9411-a2b0ccc137dc",
"name": "Groq Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGroq",
"position": [
1420,
1100
],
"parameters": {
"options": {}
},
"credentials": {
"groqApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "06abfd6e-7352-401a-8695-21c435d0c9ad",
"name": "Google Gemini Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
2020,
1100
],
"parameters": {
"options": {},
"modelName": "models/gemini-2.5-flash-lite-preview-06-17"
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "71de7319-6d01-4366-8dce-752056ee5d4c",
"name": "Generate Image Prompt (Gemini)",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
2060,
920
],
"parameters": {
"text": "=Based on the following article, generate a descriptive image prompt for the cover of a medium post: it should be for marketing and professional style image\n{{ $json.text }}",
"messages": {
"messageValues": [
{
"message": "You are a professional image prompt engineer. Your task is to generate concise, highly descriptive prompts suitable for AI image generation based on article content. Always create a vivid visual concept that aligns with the tone, subject, and theme of the input text. Use stylistic and visual descriptors relevant to photography, illustration, painting, or graphic design. Follow these rules strictly: Start with the main subject of the article. Describe setting, mood, and style. Use artistic keywords (e.g., \u201cvector art\u201d, \u201cdigital painting\u201d, \u201ccinematic lighting\u201d). Output only the image prompt \u2014 no introduction, explanation, or formatting. The final prompt should be clear, specific, and optimized for tools like Midjourney or DALL\u00b7E. Keep length between 25\u201375 words."
}
]
},
"promptType": "define"
},
"typeVersion": 1.6
},
{
"id": "224265b0-368f-43fb-93b6-55987e0b54f7",
"name": "Split Out",
"type": "n8n-nodes-base.splitOut",
"position": [
1800,
920
],
"parameters": {
"include": "allOtherFields",
"options": {},
"fieldToSplitOut": "text"
},
"typeVersion": 1
},
{
"id": "e58d0ba8-99e1-43de-9025-37de102975be",
"name": "Get row(s) in sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
280,
940
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1s8BaF5C88tDXgH0YxVEzsFsEJEmsrd6cC_a9ERcroAk/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1s8BaF5C88tDXgH0YxVEzsFsEJEmsrd6cC_a9ERcroAk",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1s8BaF5C88tDXgH0YxVEzsFsEJEmsrd6cC_a9ERcroAk/edit?usp=drivesdk",
"cachedResultName": "Mediuedium Pipeline"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.6
},
{
"id": "7f491390-b343-4d46-9eab-39572602d24a",
"name": "Merge",
"type": "n8n-nodes-base.merge",
"position": [
560,
920
],
"parameters": {
"mode": "combine",
"options": {},
"joinMode": "keepEverything",
"fieldsToMatchString": "link"
},
"typeVersion": 3.2
},
{
"id": "4812fe1b-7999-46fd-87ac-4c501fb0ce00",
"name": "Pick Random Post",
"type": "n8n-nodes-base.code",
"position": [
1040,
920
],
"parameters": {
"jsCode": "if (items.length === 0) {\n throw new Error(\"No fresh posts available to pick from.\");\n}\n\nconst randomIndex = Math.floor(Math.random() * items.length);\nreturn [items[randomIndex]];\n"
},
"typeVersion": 2
},
{
"id": "50133553-12b7-41b0-ae5d-948cb0e85b7a",
"name": "Check if Already Posted",
"type": "n8n-nodes-base.code",
"position": [
800,
920
],
"parameters": {
"jsCode": "// Separate merged items into RSS posts and Sheet URLs\nconst rssItems = [];\nconst usedUrls = new Set();\n\nfor (const item of items) {\n const link = item.json.link;\n const url = item.json.url;\n\n if (link && !rssItems.find(i => i.json.link === link)) {\n // This is from RSS\n rssItems.push(item);\n } else if (url) {\n // This is from Sheet\n usedUrls.add(url);\n }\n}\n\n// Filter out RSS posts whose link already exists in the sheet\nconst freshPosts = rssItems.filter(item => !usedUrls.has(item.json.link));\n\nreturn freshPosts;\n"
},
"typeVersion": 2
},
{
"id": "9e2d4a36-a825-4b2a-8418-741308038a40",
"name": "Edit Fields",
"type": "n8n-nodes-base.set",
"position": [
3640,
880
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "9bfad590-ce07-469b-a875-06c0452b977b",
"name": "text",
"type": "string",
"value": "={{ $json.text }}"
},
{
"id": "e75780c5-42ff-464e-8d91-60e7dad705bb",
"name": "drive_url",
"type": "string",
"value": "={{ $json.webViewLink }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "09b483fb-f264-4be7-93ed-82894509065a",
"name": "Code1",
"type": "n8n-nodes-base.code",
"position": [
3820,
940
],
"parameters": {
"jsCode": "const articleText = $json.text;\nconst driveImageURL = $json.drive_url || 'Image URL not found';\nconst date = new Date().toLocaleString('en-US', { timeZone: 'Africa/Lagos' });\nconst creator = '@OBISDEV';\n\n// Extract title from the first **bolded** line\nconst titleMatch = articleText.match(/\\*\\*(.*?)\\*\\*/);\nconst title = titleMatch ? titleMatch[1] : 'Untitled Article';\n\n// Prepare final formatted text\nconst formattedArticle = \n`${title}\\n\\n` +\n`[IMAGE ATTACHED BELOW]\\n${driveImageURL}\\n\\n` +\n`[DATE]\\n${date}\\n\\n` +\n`[CREATOR]\\nWritten by ${creator}\\n\\n` +\n`[ARTICLE]\\n${articleText}\\n\\n` +\n`[FOLLOW]\\nStay updated with more insights and tools from Obisdev:\\n` +\n`X: @OBISDEV\\nInstagram: @OBISDEV\\nGumroad: @OBISDEV NWUDE`;\n\nreturn [\n {\n json: {\n title,\n formattedArticle,\n driveImageURL\n }\n }\n];\n"
},
"typeVersion": 2
},
{
"id": "1826d383-0f4c-4ce7-9967-af63e29dfd0e",
"name": "Split Out1",
"type": "n8n-nodes-base.splitOut",
"position": [
2880,
1180
],
"parameters": {
"include": "allOtherFields",
"options": {},
"fieldToSplitOut": "title"
},
"typeVersion": 1
},
{
"id": "d83446f1-8905-444f-b1e1-0b6df0c14377",
"name": "Merge1",
"type": "n8n-nodes-base.merge",
"position": [
3360,
1180
],
"parameters": {},
"typeVersion": 3.2
},
{
"id": "1dba58b0-e518-4fb8-888c-f43d985a9d7f",
"name": "Wait1",
"type": "n8n-nodes-base.wait",
"position": [
3140,
1340
],
"parameters": {},
"typeVersion": 1.1
},
{
"id": "3ff425ca-3aa6-41f3-86b7-846c565ff6dc",
"name": "Code2",
"type": "n8n-nodes-base.code",
"position": [
3600,
1180
],
"parameters": {
"jsCode": "// Filter out the item with the doc ID and the one with the article\nlet idItem = items.find(item => item.json.id);\nlet articleItem = items.find(item => item.json.formattedArticle);\n\n// Output a single object with id, formatted article, and drive image URL\nreturn [\n {\n json: {\n id: idItem.json.id,\n formattedArticle: articleItem.json.formattedArticle,\n driveImageURL: articleItem.json.driveImageURL\n }\n }\n];\n"
},
"typeVersion": 2
},
{
"id": "402776a8-6a12-45d4-a259-a68e12b73f7a",
"name": "REFORMAT ARTICLE",
"type": "n8n-nodes-base.code",
"position": [
3800,
1180
],
"parameters": {
"jsCode": "// Safely find input parts\nconst docId = items.find(item => item.json.id)?.json.id;\nconst article = items.find(item => item.json.formattedArticle)?.json.formattedArticle || '';\n\n// Try to get title from a dedicated field, fallback to bolded headline\nlet title = items.find(item => item.json.title)?.json.title;\nif (!title) {\n const match = article.match(/\\*\\*(.+?)\\*\\*/);\n title = match ? match[1] : 'Untitled Article';\n}\n\n// Remove bolded title if it appears again inside the content\nconst cleanedArticle = article.replace(new RegExp(`\\\\*\\\\*${title}\\\\*\\\\*`, 'i'), '');\n\n// Remove all structural [TAGS] except [Link: ...]\nconst mainContent = cleanedArticle\n .replace(/\\[(?!Link:).*?\\]/g, '')\n .trim();\n\n// Compose final content\nconst content = `${title}\n\n[Image will be inserted here]\n\nPublished: ${new Date().toLocaleString('en-US', { timeZone: 'Africa/Lagos' })}\nBy: @OBISDEV\n\n${mainContent}\n`;\n\nreturn [\n {\n json: {\n id: docId,\n formattedArticle: content\n }\n }\n];\n"
},
"typeVersion": 2
},
{
"id": "1b9bdcb8-239a-43c9-a7be-57d32c47c7db",
"name": "Upload file",
"type": "n8n-nodes-base.googleDrive",
"position": [
2860,
760
],
"parameters": {
"name": "={{$json[\"fileName\"] || \"image.jpg\"}}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"options": {},
"folderId": {
"__rl": true,
"mode": "list",
"value": "1uOuxdhJ2w64JPJGBZhRo8-k4_cd8B_JA",
"cachedResultUrl": "https://drive.google.YOUR_AWS_SECRET_KEY_HERE-k4_cd8B_JA",
"cachedResultName": "MEDIUM PIPELINE"
}
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "ea031055-b133-4a0e-b172-7c7751ea0994",
"name": "Merge2",
"type": "n8n-nodes-base.merge",
"position": [
3020,
900
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "8e4f4d04-41a4-4276-8581-4c044dbd3f31",
"name": "Split Out2",
"type": "n8n-nodes-base.splitOut",
"position": [
4020,
1040
],
"parameters": {
"include": "allOtherFields",
"options": {},
"fieldToSplitOut": "formattedArticle"
},
"typeVersion": 1
},
{
"id": "48fd5492-391e-45c6-940d-f0eec99b927c",
"name": "Merge3",
"type": "n8n-nodes-base.merge",
"position": [
4400,
1060
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineAll"
},
"typeVersion": 3.2
},
{
"id": "c4e4faa4-7964-4e43-acd6-8c6534309099",
"name": "No Operation, do nothing",
"type": "n8n-nodes-base.noOp",
"position": [
4220,
1160
],
"parameters": {},
"typeVersion": 1
},
{
"id": "2bf68eb8-f554-4da1-ab62-1b5519307156",
"name": "Update Doc",
"type": "n8n-nodes-base.googleDocs",
"position": [
4240,
920
],
"parameters": {
"actionsUi": {
"actionFields": [
{
"text": "={{ $json.formattedArticle }}",
"action": "insert",
"locationChoice": "location"
}
]
},
"operation": "update",
"documentURL": "={{ $json.id }}"
},
"credentials": {
"googleDocsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "41801c0d-9f05-4c12-a483-5b188a2823df",
"name": "Send message and wait for response",
"type": "n8n-nodes-base.slack",
"position": [
4880,
1060
],
"parameters": {
"user": {
"__rl": true,
"mode": "username",
"value": ""
},
"message": "=\ud83d\ude80 *New Article Ready for Review*\n\nTo approve, reply in this channel with: *APPROVE ARTICLE*\n",
"options": {},
"operation": "sendAndWait",
"approvalOptions": {
"values": {
"approvalType": "double",
"buttonApprovalStyle": "secondary"
}
}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.3
},
{
"id": "647de0dd-4da4-4e92-afa5-d37b0306cd4f",
"name": "Send a message1",
"type": "n8n-nodes-base.gmail",
"position": [
5320,
860
],
"parameters": {
"message": "=THE ARTICLE\n\n{{ $('Split Out2').item.json.formattedArticle }}",
"options": {},
"subject": "Medium Content",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "8d47451f-0181-4456-96f7-9acd85885936",
"name": "Edit Fields1",
"type": "n8n-nodes-base.set",
"position": [
4640,
1060
],
"parameters": {
"options": {
"dotNotation": true,
"ignoreConversionErrors": true
},
"assignments": {
"assignments": [
{
"id": "5fad9b34-9bae-4d70-a3a1-dc9fe7177292",
"name": "formattedArticle",
"type": "string",
"value": "={{ $json.formattedArticle }}"
}
]
}
},
"executeOnce": false,
"typeVersion": 3.4,
"alwaysOutputData": true
},
{
"id": "ce9a0b0f-8e8f-4394-a8fc-9c97e402a375",
"name": "Send a message2",
"type": "n8n-nodes-base.slack",
"position": [
3260,
960
],
"parameters": {
"text": "=REVIEW THE ARTICLE FOR MEDIUM POST\n\n{{ $json.text }}\n\n{{ $json.webViewLink }}",
"user": {
"__rl": true,
"mode": "username",
"value": ""
},
"select": "user",
"otherOptions": {}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.3
},
{
"id": "0fe455a9-8bf6-4978-8791-73d202fe1bf6",
"name": "Merge4",
"type": "n8n-nodes-base.merge",
"position": [
3440,
920
],
"parameters": {},
"typeVersion": 3.2
},
{
"id": "5ad5719d-ded8-4044-bd3d-547a56a8b711",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-260,
60
],
"parameters": {
"width": 1520,
"height": 1420,
"content": "## \ud83d\udcf0 RSS for Content Fetching\n\nNodes involved:\n\nSchedule Trigger\nThe Verge - Tech RSS\nTechCrunch RSS\n\nThis section uses a scheduled trigger to pull in fresh content from two tech-focused RSS feeds: The Verge and TechCrunch. These feeds act as the primary sources for news articles. It ensures that every run starts with up-to-date, relevant tech content from reputable platforms.\n\n\n\n## \ud83e\uddee Sort, Merge, and Pick a Post\nNodes involved:\n\nGet row(s) in sheet : TITLE, IMAGE_URL, CREATOR, & DESCRIPTION\n\nMerge\n\nPick Random Post[Customize to your preferred Niche]\n\nNote:\nFetched articles are merged into a single list. The workflow then checks a Google Sheet to verify which articles were already posted before. One new, unique article is picked randomly from the valid entries to be used for further processing. This ensures no duplicate publishing and supports content diversity.\n\n\n\ud83d\udeab Check If Already Posted\nNodes involved:\n\nCheck If Already Posted\n\nNote:\nThis node verifies if the selected article has already been used in a past run by comparing its title or link with existing records in the Google Sheet. If already posted, it either halts or skips the duplicate to maintain uniqueness in published content."
},
"typeVersion": 1
},
{
"id": "5730e2ba-de00-41ff-9edb-32d6e92107ec",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
1280,
60
],
"parameters": {
"width": 1520,
"height": 1420,
"content": "\ud83e\udde0 Agent Article & Image Generation\nNodes involved:\n\nGoogle Gemini Model (text generation)\n\nBasic LLM Chain (structured content)\n\nGroq Chat Model (content expansion)\n\nGenerate Image Prompt\n\nCreate Image with Pollinations and get the URL\n\nHTTP Request (fetch/download image)\n\nDownload Image file \n\nNote:\nThis is the AI generation stage:\n\nText: Google Gemini and Groq LLM models are used to generate a well-written article based on the RSS content.\n\nImage: Pollinations AI generates a relevant visual image from a text prompt. An HTTP node is used to fetch the image, which is then uploaded to the cloud (possibly Drive or another host).\n\nOutput is split to handle both article and image streams for better control downstream.\n\n"
},
"typeVersion": 1
},
{
"id": "b98fd668-02cb-4f71-9b82-a52fc5277f4b",
"name": "Create Pollinations + URL",
"type": "n8n-nodes-base.code",
"position": [
2440,
840
],
"parameters": {
"jsCode": "const width = 1024;\nconst height = 800;\nconst randomSeed = Math.floor(Math.random() * 100000);\n\nconst finalPrompt = $json.text;\nconst imageUrl = `https://image.pollinations.ai/prompt/${encodeURIComponent(finalPrompt)}.jpg?width=${width}&height=${height}&seed=${randomSeed}&model=flux&nologo=true`;\n\nreturn [\n {\n json: {\n text: finalPrompt,\n imageUrl\n }\n }\n];\n"
},
"typeVersion": 2
},
{
"id": "9e652275-2271-47ba-83bb-f6106fcbd92b",
"name": "DOWNLOAD IMAGE",
"type": "n8n-nodes-base.httpRequest",
"position": [
2660,
760
],
"parameters": {
"url": "={{ $json.imageUrl }}",
"options": {
"response": {
"response": {
"responseFormat": "file"
}
}
}
},
"typeVersion": 4.2
},
{
"id": "e661e000-c91f-4c80-b295-d2eca71a4cb4",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
2820,
60
],
"parameters": {
"width": 1980,
"height": 1420,
"content": "\u2601\ufe0f Upload to Google Drive\nNodes involved:\n\nCreate Google Doc\n\nMerge4\n\nUpdate Doc\n\nEdit Fields\n\nNote:\nThe generated article and image are uploaded and stored in Google Drive. A Google Doc is created, updated with the AI-generated content, and linked back to the system. This provides a centralized place for human reviewers and stores the article for archiving or reuse.\n\nHere We Send the post to Slack DM or Channel for the user to Review the Article."
},
"typeVersion": 1
},
{
"id": "27b27f67-ec8c-4a67-b509-23bad83afaac",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
4820,
60
],
"parameters": {
"width": 820,
"height": 1420,
"content": "\ud83d\udce4 Slack Notification for Approval\nNodes involved:\n\nSend a message2\n\nSend a message and wait for response\n\nCheck Approval Status\n\nNote:\nSlack is used as the manual review checkpoint. The system sends a message with the content preview and waits for a response (approval or rejection) from a designated reviewer or group. This introduces human judgment before public publishing.\n\n\u2705 Approved = Publish to Medium + Gmail\nNodes involved:\n\nPublish to Medium\n\nSend a message1 (Gmail)\n\nNote:\nOnce content is approved via Slack, it's automatically published to Medium through an API call. At the same time, an email is sent via Gmail, which could be used to notify stakeholders, send to a mailing list, or archive the content. This step ensures distribution across multiple channels.\n\n\u274c Rejected = Notify on Slack\nNodes involved:\n\nRejection Notification\n\nNote:\nIf the reviewer rejects the article in Slack, a rejection notification is triggered and sent to the same Slack user or group. This keeps the content creation team informed and ready to revise or skip the rejected content. It closes the loop with proper rejection feedback.\n\n"
},
"typeVersion": 1
}
],
"connections": {
"Code1": {
"main": [
[
{
"node": "Split Out1",
"type": "main",
"index": 0
}
]
]
},
"Code2": {
"main": [
[
{
"node": "REFORMAT ARTICLE",
"type": "main",
"index": 0
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Check if Already Posted",
"type": "main",
"index": 0
}
]
]
},
"Wait1": {
"main": [
[
{
"node": "Merge1",
"type": "main",
"index": 1
}
]
]
},
"Merge1": {
"main": [
[
{
"node": "Code2",
"type": "main",
"index": 0
}
]
]
},
"Merge2": {
"main": [
[
{
"node": "Send a message2",
"type": "main",
"index": 0
},
{
"node": "Merge4",
"type": "main",
"index": 0
}
]
]
},
"Merge3": {
"main": [
[
{
"node": "Edit Fields1",
"type": "main",
"index": 0
}
]
]
},
"Merge4": {
"main": [
[
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"Split Out": {
"main": [
[
{
"node": "Generate Image Prompt (Gemini)",
"type": "main",
"index": 0
},
{
"node": "Merge2",
"type": "main",
"index": 1
}
]
]
},
"Split Out1": {
"main": [
[
{
"node": "Create Google Doc",
"type": "main",
"index": 0
},
{
"node": "Wait1",
"type": "main",
"index": 0
}
]
]
},
"Split Out2": {
"main": [
[
{
"node": "Update Doc",
"type": "main",
"index": 0
},
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
]
]
},
"TechCrunch": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Update Doc": {
"main": [
[
{
"node": "Merge3",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "Code1",
"type": "main",
"index": 0
}
]
]
},
"Upload file": {
"main": [
[
{
"node": "Merge2",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields1": {
"main": [
[
{
"node": "Send message and wait for response",
"type": "main",
"index": 0
}
]
]
},
"DOWNLOAD IMAGE": {
"main": [
[
{
"node": "Upload file",
"type": "main",
"index": 0
}
]
]
},
"Basic LLM Chain": {
"main": [
[
{
"node": "Split Out",
"type": "main",
"index": 0
}
]
]
},
"Groq Chat Model": {
"ai_languageModel": [
[
{
"node": "Basic LLM Chain",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Send a message2": {
"main": [
[
{
"node": "Merge4",
"type": "main",
"index": 1
}
]
]
},
"Pick Random Post": {
"main": [
[
{
"node": "Basic LLM Chain",
"type": "main",
"index": 0
}
]
]
},
"REFORMAT ARTICLE": {
"main": [
[
{
"node": "Split Out2",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Get row(s) in sheet",
"type": "main",
"index": 0
},
{
"node": "The Verge \u2013 Tech",
"type": "main",
"index": 0
}
]
]
},
"Create Google Doc": {
"main": [
[
{
"node": "Merge1",
"type": "main",
"index": 0
}
]
]
},
"The Verge \u2013 Tech": {
"main": [
[
{
"node": "Ars Technica \u2013 Technology Lab",
"type": "main",
"index": 0
}
]
]
},
"Get row(s) in sheet": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Check Approval Status": {
"main": [
[
{
"node": "Send a message1",
"type": "main",
"index": 0
}
],
[
{
"node": "Rejection Notification",
"type": "main",
"index": 0
}
]
]
},
"Check if Already Posted": {
"main": [
[
{
"node": "Pick Random Post",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "Generate Image Prompt (Gemini)",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"No Operation, do nothing": {
"main": [
[
{
"node": "Merge3",
"type": "main",
"index": 1
}
]
]
},
"Create Pollinations + URL": {
"main": [
[
{
"node": "DOWNLOAD IMAGE",
"type": "main",
"index": 0
}
]
]
},
"Generate Image Prompt (Gemini)": {
"main": [
[
{
"node": "Create Pollinations + URL",
"type": "main",
"index": 0
}
]
]
},
"Ars Technica \u2013 Technology Lab": {
"main": [
[
{
"node": "TechCrunch",
"type": "main",
"index": 0
}
]
]
},
"Send message and wait for response": {
"main": [
[
{
"node": "Check Approval 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.
gmailOAuth2googleDocsOAuth2ApigoogleDriveOAuth2ApigooglePalmApigoogleSheetsOAuth2ApigroqApislackApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Categories Content Creation AI Automation Publishing Social Media
Source: https://n8n.io/workflows/6713/ — 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.
This workflow is designed for Japanese-speaking professionals, and learners who want to efficiently stay up to date with practical productivity, lifehack, and efficiency-related insights from Japanese
This n8n template shows you how to turn outbound sales into a fully automated machine: scrape verified leads, research them with AI, and fire off personalized cold emails while you sleep.
Automatically identifies overdue sales leads and generates personalized follow-up emails using AI. Runs every weekday Reads leads from Google Sheets Filters leads with no contact for 5+ days Downloads
SMB finance teams, SaaS companies, and accounting professionals who need to automate transaction reconciliation between Stripe payments and their accounting ledgers. Perfect for businesses processing
DevOps engineers, site reliability teams, and business owners who need to know the moment their website goes down — and want AI-powered diagnostics instead of just a ping alert.