This workflow corresponds to n8n.io template #11969 — we link there as the canonical source.
This workflow follows the Agent → 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 →
{
"id": "FRfUJmvevN8j6X3k",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Schedule and optimize social media posts to Twitter and LinkedIn using AI",
"tags": [],
"nodes": [
{
"id": "40b15783-6b4b-4ba9-ae47-cf1ad563e85a",
"name": "Main Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-880,
880
],
"parameters": {
"width": 380,
"height": 474,
"content": "## How it works\nThis workflow automates your social media presence. It monitors a Google Sheet for scheduled posts, uses AI to optimize captions and hashtags for specific platforms (Twitter and LinkedIn), and publishes them automatically. Finally, it updates the post status and notifies you via Slack.\n\n## Setup steps\n1. **Spreadsheet**: Create a Google Sheet with columns: `status`, `content`, `platforms`, `scheduled_time`, and `hashtags`.\n2. **Credentials**: Connect your Google Sheets, OpenAI, Twitter, LinkedIn, and Slack accounts.\n3. **Node Config**: Select your specific spreadsheet in both the 'Fetch' and 'Update' Google Sheets nodes.\n4. **Test**: Use the 'Manual Post Trigger' to verify the flow before enabling the 'Hourly' schedule."
},
"typeVersion": 1
},
{
"id": "7481371a-76bf-4f28-9660-f64fcb85b9d2",
"name": "Section Sticky 1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-288,
1216
],
"parameters": {
"color": 7,
"width": 712,
"height": 528,
"content": "## 1. Data Retrieval\nTriggers the workflow hourly or via webhook and pulls the latest content queue from Google Sheets."
},
"typeVersion": 1
},
{
"id": "a3ad0557-0cd1-4e98-95c2-10be485522c4",
"name": "Section Sticky 2",
"type": "n8n-nodes-base.stickyNote",
"position": [
464,
1200
],
"parameters": {
"color": 7,
"width": 800,
"height": 528,
"content": "## 2. AI Optimization\nFilters posts ready for publishing. The AI Agent then rewrites content to fit platform constraints (e.g., character limits) and generates hashtags."
},
"typeVersion": 1
},
{
"id": "0bd3a028-f758-4654-9f6a-c6c4e13caf0c",
"name": "Section Sticky 3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1440,
1088
],
"parameters": {
"color": 7,
"width": 260,
"height": 592,
"content": "## 3. Social Publishing\nDistributes the optimized content to Twitter and LinkedIn simultaneously."
},
"typeVersion": 1
},
{
"id": "5ba26121-7b57-4ecd-b073-ed05f98eda96",
"name": "Section Sticky 4",
"type": "n8n-nodes-base.stickyNote",
"position": [
2080,
1088
],
"parameters": {
"color": 7,
"width": 268,
"height": 544,
"content": "## 4. Reporting & Response\nAggregates results, logs post URLs back to the spreadsheet, and sends a summary report to your Slack channel."
},
"typeVersion": 1
},
{
"id": "e6fa39a6-3eb5-4896-b0e8-f178efdae2ed",
"name": "Hourly Content Check",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-192,
1392
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "f601cb08-1506-4e66-be27-0c9ac5db491c",
"name": "Manual Post Trigger",
"type": "n8n-nodes-base.webhook",
"onError": "continueRegularOutput",
"position": [
-192,
1600
],
"parameters": {
"path": "social-post",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "448d52f3-7481-4fef-a4cc-109fbbf18457",
"name": "Merge Triggers",
"type": "n8n-nodes-base.merge",
"position": [
32,
1488
],
"parameters": {
"mode": "chooseBranch"
},
"typeVersion": 3
},
{
"id": "42458dfc-9d2e-4fb6-b0ad-47b9f8a69c96",
"name": "Fetch Content Queue",
"type": "n8n-nodes-base.googleSheets",
"position": [
256,
1488
],
"parameters": {
"sheetName": {
"__rl": true,
"mode": "list",
"value": ""
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"typeVersion": 4.5
},
{
"id": "f8b72bff-8883-4e4f-834c-56763a6fa9aa",
"name": "Filter Ready Posts",
"type": "n8n-nodes-base.code",
"position": [
480,
1488
],
"parameters": {
"jsCode": "const items = $input.all();\nconst now = new Date();\nconst readyPosts = [];\n\nfor (const item of items) {\n const row = item.json;\n \n // Check if post is scheduled and not yet published\n const status = (row.status || '').toLowerCase();\n if (status !== 'scheduled' && status !== 'ready') continue;\n \n // Check scheduled time\n const scheduledTime = row.scheduled_time || row.scheduledTime;\n if (scheduledTime) {\n const schedDate = new Date(scheduledTime);\n if (schedDate > now) continue; // Not yet time\n }\n \n // Validate required fields\n if (!row.content && !row.text && !row.message) continue;\n \n const platforms = (row.platforms || row.platform || 'twitter,linkedin')\n .split(',')\n .map(p => p.trim().toLowerCase());\n \n readyPosts.push({\n json: {\n id: row.id || row.row_number || Date.now(),\n content: row.content || row.text || row.message,\n platforms: platforms,\n imageUrl: row.image_url || row.imageUrl || null,\n scheduledTime: scheduledTime,\n category: row.category || 'general',\n campaign: row.campaign || '',\n hashtags: row.hashtags || '',\n linkUrl: row.link_url || row.linkUrl || '',\n tone: row.tone || 'professional'\n }\n });\n}\n\nif (readyPosts.length === 0) {\n return [{ json: { noContent: true, message: 'No posts ready to publish' } }];\n}\n\nreturn readyPosts;"
},
"typeVersion": 2
},
{
"id": "fced1dc4-1620-413b-ab3f-4b3d3debdf3c",
"name": "Has Content to Post?",
"type": "n8n-nodes-base.if",
"position": [
704,
1488
],
"parameters": {
"options": {},
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "content-check",
"operator": {
"type": "boolean",
"operation": "notEquals"
},
"leftValue": "={{ $json.noContent }}",
"rightValue": true
}
]
}
},
"typeVersion": 2
},
{
"id": "69efadfa-627c-41f3-ba12-3c57df88b1a2",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
1008,
1584
],
"parameters": {
"model": "gpt-4o-mini",
"options": {
"temperature": 0.7
}
},
"typeVersion": 1.2
},
{
"id": "38e02a47-f9b7-4b73-ac91-e3491affe50a",
"name": "AI Content Optimizer",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
928,
1360
],
"parameters": {
"text": "=Optimize this social media post for maximum engagement:\n\nOriginal Content: {{ $json.content }}\nPlatforms: {{ $json.platforms.join(', ') }}\nTone: {{ $json.tone }}\nCategory: {{ $json.category }}\nExisting Hashtags: {{ $json.hashtags }}\nLink: {{ $json.linkUrl }}\n\nProvide optimized versions in this exact JSON format:\n{\n \"twitter\": {\n \"text\": \"optimized text for Twitter (max 280 chars)\",\n \"hashtags\": [\"relevant\", \"hashtags\", \"max5\"]\n },\n \"linkedin\": {\n \"text\": \"optimized text for LinkedIn (professional tone, can be longer)\",\n \"hashtags\": [\"professional\", \"hashtags\"]\n },\n \"engagementTips\": [\"tip1\", \"tip2\"],\n \"bestPostTime\": \"suggested time\",\n \"contentScore\": 0-100\n}",
"options": {
"systemMessage": "You are a social media marketing expert. Optimize content for engagement while maintaining brand voice. Keep Twitter posts concise and punchy, LinkedIn posts professional and insightful. Include relevant trending hashtags when appropriate."
}
},
"typeVersion": 1.7
},
{
"id": "eb6268f6-9603-43f5-9c03-e5ccd0ae98e4",
"name": "Parse AI Content",
"type": "n8n-nodes-base.code",
"position": [
1280,
1360
],
"parameters": {
"jsCode": "const input = $input.first().json;\nconst originalData = $('Filter Ready Posts').item.json;\n\nlet optimized;\ntry {\n const responseText = input.output || input.text || '';\n const jsonMatch = responseText.match(/\\{[\\s\\S]*\\}/);\n optimized = jsonMatch ? JSON.parse(jsonMatch[0]) : null;\n} catch (e) {\n optimized = null;\n}\n\nif (!optimized) {\n // Fallback to original content\n optimized = {\n twitter: {\n text: originalData.content.substring(0, 280),\n hashtags: originalData.hashtags ? originalData.hashtags.split(' ') : []\n },\n linkedin: {\n text: originalData.content,\n hashtags: originalData.hashtags ? originalData.hashtags.split(' ') : []\n },\n engagementTips: [],\n bestPostTime: 'Now',\n contentScore: 70\n };\n}\n\n// Build final posts\nconst twitterText = optimized.twitter.text + \n (optimized.twitter.hashtags?.length > 0 ? ' ' + optimized.twitter.hashtags.map(h => h.startsWith('#') ? h : '#' + h).join(' ') : '');\n\nconst linkedinText = optimized.linkedin.text + \n (optimized.linkedin.hashtags?.length > 0 ? '\\n\\n' + optimized.linkedin.hashtags.map(h => h.startsWith('#') ? h : '#' + h).join(' ') : '');\n\nreturn [{\n json: {\n ...originalData,\n optimized: optimized,\n posts: {\n twitter: {\n text: twitterText.substring(0, 280),\n ready: originalData.platforms.includes('twitter')\n },\n linkedin: {\n text: linkedinText,\n ready: originalData.platforms.includes('linkedin')\n }\n },\n contentScore: optimized.contentScore || 70,\n processedAt: new Date().toISOString()\n }\n}];"
},
"typeVersion": 2
},
{
"id": "4d39d9f2-9aef-4bb1-8f2b-bb2c20895f34",
"name": "Post to Twitter",
"type": "n8n-nodes-base.twitter",
"position": [
1504,
1264
],
"parameters": {
"text": "={{ $json.posts.twitter.text }}",
"additionalFields": {}
},
"typeVersion": 2
},
{
"id": "4c609576-3906-4822-9c82-ed064fe5b6c6",
"name": "Post to LinkedIn",
"type": "n8n-nodes-base.linkedIn",
"position": [
1504,
1488
],
"parameters": {
"text": "={{ $json.posts.linkedin.text }}",
"person": {
"__rl": true,
"mode": "id",
"value": "={{ $json.linkedinPersonId || '' }}"
},
"additionalFields": {}
},
"typeVersion": 1
},
{
"id": "f0a19a81-e1db-40ee-92e4-0ecfee3d8474",
"name": "Aggregate Post Results",
"type": "n8n-nodes-base.aggregate",
"position": [
1728,
1360
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData"
},
"typeVersion": 1
},
{
"id": "3533a23e-9b66-4b93-bb00-990c00f15dec",
"name": "Format Results",
"type": "n8n-nodes-base.code",
"position": [
1952,
1360
],
"parameters": {
"jsCode": "const results = $input.first().json.data || [];\nconst originalData = $('Parse AI Content').first().json;\n\nconst postResults = {\n twitter: null,\n linkedin: null\n};\n\nfor (const result of results) {\n if (result.id_str || result.data?.id) {\n postResults.twitter = {\n success: true,\n postId: result.id_str || result.data?.id,\n url: `https://twitter.com/i/status/${result.id_str || result.data?.id}`\n };\n }\n if (result.id && String(result.id).includes('urn:li:share')) {\n postResults.linkedin = {\n success: true,\n postId: result.id,\n url: `https://linkedin.com/feed/update/${result.id}`\n };\n }\n}\n\nconst successCount = Object.values(postResults).filter(p => p?.success).length;\nconst totalPlatforms = originalData.platforms.length;\n\nreturn [{\n json: {\n contentId: originalData.id,\n originalContent: originalData.content.substring(0, 100) + '...',\n platforms: originalData.platforms,\n postResults: postResults,\n summary: {\n successCount: successCount,\n totalPlatforms: totalPlatforms,\n allSuccessful: successCount === totalPlatforms\n },\n contentScore: originalData.contentScore,\n publishedAt: new Date().toISOString()\n }\n}];"
},
"typeVersion": 2
},
{
"id": "b86abd85-723d-49bb-b82e-4500aeeed486",
"name": "Update Content Status",
"type": "n8n-nodes-base.googleSheets",
"position": [
2176,
1264
],
"parameters": {
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": ""
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"typeVersion": 4.5
},
{
"id": "11bce6ef-c71e-4762-b573-853e8ecf138f",
"name": "Post Summary to Slack",
"type": "n8n-nodes-base.slack",
"position": [
2176,
1456
],
"parameters": {
"text": "=:mega: *Social Media Post Published*\n\n*Platforms:* {{ $json.platforms.join(', ') }}\n*Success:* {{ $json.summary.successCount }}/{{ $json.summary.totalPlatforms }}\n*Content Score:* {{ $json.contentScore }}/100\n\n*Preview:*\n{{ $json.originalContent }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "name",
"value": "#social-media"
},
"otherOptions": {}
},
"typeVersion": 2.2
},
{
"id": "a43c1f57-6383-426c-92a3-07a3c7b61bad",
"name": "No Content Response",
"type": "n8n-nodes-base.set",
"position": [
2176,
1680
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "1",
"name": "message",
"type": "string",
"value": "No content scheduled for posting"
},
{
"id": "2",
"name": "timestamp",
"type": "string",
"value": "={{ $now.toISO() }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "896c50a0-3c34-466a-a5d3-5655a732033d",
"name": "Merge Final Paths",
"type": "n8n-nodes-base.merge",
"position": [
2400,
1456
],
"parameters": {
"mode": "chooseBranch"
},
"typeVersion": 3
},
{
"id": "975e2adf-b180-4333-a226-6f6d451999a5",
"name": "Respond to Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
2624,
1456
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={{ {success: true, published: $json.summary ? $json.summary.successCount : 0, message: $json.message || 'Posts published successfully'} }}"
},
"typeVersion": 1.1
}
],
"active": false,
"settings": {
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "934cc699-ab8d-48c6-b96b-7785202073da",
"connections": {
"Format Results": {
"main": [
[
{
"node": "Update Content Status",
"type": "main",
"index": 0
},
{
"node": "Post Summary to Slack",
"type": "main",
"index": 0
}
]
]
},
"Merge Triggers": {
"main": [
[
{
"node": "Fetch Content Queue",
"type": "main",
"index": 0
}
]
]
},
"Post to Twitter": {
"main": [
[
{
"node": "Aggregate Post Results",
"type": "main",
"index": 0
}
]
]
},
"Parse AI Content": {
"main": [
[
{
"node": "Post to Twitter",
"type": "main",
"index": 0
},
{
"node": "Post to LinkedIn",
"type": "main",
"index": 0
}
]
]
},
"Post to LinkedIn": {
"main": [
[
{
"node": "Aggregate Post Results",
"type": "main",
"index": 0
}
]
]
},
"Merge Final Paths": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Content Optimizer",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Filter Ready Posts": {
"main": [
[
{
"node": "Has Content to Post?",
"type": "main",
"index": 0
}
]
]
},
"Fetch Content Queue": {
"main": [
[
{
"node": "Filter Ready Posts",
"type": "main",
"index": 0
}
]
]
},
"Manual Post Trigger": {
"main": [
[
{
"node": "Merge Triggers",
"type": "main",
"index": 1
}
]
]
},
"No Content Response": {
"main": [
[
{
"node": "Merge Final Paths",
"type": "main",
"index": 1
}
]
]
},
"AI Content Optimizer": {
"main": [
[
{
"node": "Parse AI Content",
"type": "main",
"index": 0
}
]
]
},
"Has Content to Post?": {
"main": [
[
{
"node": "AI Content Optimizer",
"type": "main",
"index": 0
}
],
[
{
"node": "No Content Response",
"type": "main",
"index": 0
}
]
]
},
"Hourly Content Check": {
"main": [
[
{
"node": "Merge Triggers",
"type": "main",
"index": 0
}
]
]
},
"Post Summary to Slack": {
"main": [
[
{
"node": "Merge Final Paths",
"type": "main",
"index": 0
}
]
]
},
"Update Content Status": {
"main": [
[
{
"node": "Merge Final Paths",
"type": "main",
"index": 0
}
]
]
},
"Aggregate Post Results": {
"main": [
[
{
"node": "Format Results",
"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 automates the entire lifecycle of social media management—from fetching draft content to AI-driven optimization and multi-platform publishing.
Source: https://n8n.io/workflows/11969/ — 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.
Whether you’re a product manager, developer, or simply curious about workflow automation, you’re in the right place. This n8n workflow is designed to help you streamline and automate your social media
This workflow is for beauty salons who want consistent, high‑quality social media content without writing every post manually. It also suits agencies and automation builders who manage multiple beauty
Created by: Peyton Leveillee Last updated: October 2025
This workflow is designed for marketers, content creators, agencies, and solo founders who want to publish long‑form posts with visuals on autopilot using n8n and AI agents.
Who Is This For?