AutomationFlowsAI & RAG › Business Post Pipeline (postgres)

Business Post Pipeline (postgres)

Business Post Pipeline. Uses postgres, httpRequest, agent, lmChatGoogleGemini. Scheduled trigger; 30 nodes.

Cron / scheduled trigger★★★★★ complexityAI-powered30 nodesPostgresHTTP RequestAgentGoogle Gemini ChatSlackGmailLinkedIn
AI & RAG Trigger: Cron / scheduled Nodes: 30 Complexity: ★★★★★ AI nodes: yes Added:
Business Post Pipeline (postgres) — n8n workflow card showing Postgres, HTTP Request, Agent integration

This workflow follows the Agent → 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 →

Download .json
{
  "name": "Business Post Pipeline",
  "nodes": [
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "UPDATE pipeline_memory \nSET status = '{{ $json.action }}', outcome_at = NOW()\nWHERE hash = '{{ $json.hash }}';\n\nINSERT INTO pipeline_audit (hash, action, actioned_at)\nVALUES ('{{ $json.hash }}', '{{ $json.action }}', NOW())\nON CONFLICT DO NOTHING;",
        "options": {}
      },
      "id": "30098026-0e90-43e9-9b17-139665fd842e",
      "name": "Update Status + Audit Log",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        1792,
        1936
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 6
            }
          ]
        }
      },
      "id": "0989b9b6-212c-4f0a-ac96-4505574c27cb",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [
        608,
        1248
      ]
    },
    {
      "parameters": {
        "jsCode": "const runId = `run_${Date.now()}`;\nconst runAt = new Date().toISOString();\nconsole.log(`[Pipeline] Starting run: ${runId}`);\nreturn [{ json: { runId, runAt } }];"
      },
      "id": "896ff700-05bb-466c-ac93-423423907518",
      "name": "Init Run",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        848,
        1248
      ]
    },
    {
      "parameters": {
        "url": "https://search.cnbc.com/rs/search/combinedcms/view.xml?partnerId=wrss01&id=15839135",
        "options": {
          "timeout": 30000
        }
      },
      "id": "15935881-f283-4cf1-88bd-6a09f95481eb",
      "name": "Fetch Earnings",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1104,
        1104
      ]
    },
    {
      "parameters": {
        "url": "https://www.nist.gov/news-events/news/rss.xml",
        "options": {
          "timeout": 30000
        }
      },
      "id": "579ac864-6889-4fe8-8dd8-844fab7483cc",
      "name": "Fetch Regulatory",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1104,
        1376
      ]
    },
    {
      "parameters": {
        "jsCode": "// Parse RSS XML and extract article metadata\nconst body = $input.item.json.data || '';\nconst MAX = 1;\n\nconst blocks = (body.match(/<item>(.*?)<\\/item>/gs) || []).slice(0, MAX);\nconst items = [];\n\nfor (const block of blocks) {\n  const title   = (block.match(/<title>(?:<!\\[CDATA\\[)?(.*?)(?:\\]\\]>)?<\\/title>/) || [])[1] || '';\n  const url     = (block.match(/<link>(.*?)<\\/link>/) || [])[1] || '';\n  const desc    = (block.match(/<description>(?:<!\\[CDATA\\[)?(.*?)(?:\\]\\]>)?<\\/description>/) || [])[1] || '';\n  const pubDate = (block.match(/<pubDate>(.*?)<\\/pubDate>/) || [])[1] || '';\n\n  if (title && url) {\n    items.push({ json: {\n      title: title.trim(),\n      url: url.trim(),\n      description: desc.replace(/<[^>]+>/g, '').trim().slice(0, 600),\n      pubDate,\n      sourceType: 'earnings',\n      sourceName: 'CNBC Earnings'\n    }});\n  }\n}\n\nconsole.log(`[Pipeline] Parsed ${items.length} earnings items`);\nreturn items;"
      },
      "id": "7e2147d6-fa44-4dc0-8aab-f36d065a0beb",
      "name": "Parse Earnings",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1328,
        1104
      ]
    },
    {
      "parameters": {
        "jsCode": "// Parse RSS and keyword-filter for AI relevance\nconst body = $input.item.json.data || '';\nconst MAX = 1;\n\nconst KEYWORDS = [\n  'artificial intelligence', 'AI', 'machine learning',\n  'automation', 'LLM', 'AI governance', 'AI compliance',\n  'AI regulation', 'AI Act', 'cybersecurity'\n];\n\nconst blocks = body.match(/<item>(.*?)<\\/item>/gs) || [];\nconst items = [];\n\nfor (const block of blocks) {\n  if (items.length >= MAX) break;\n\n  const title   = (block.match(/<title>(?:<!\\[CDATA\\[)?(.*?)(?:\\]\\]>)?<\\/title>/) || [])[1] || '';\n  const url     = (block.match(/<link>(.*?)<\\/link>/) || [])[1] || '';\n  const desc    = (block.match(/<description>(?:<!\\[CDATA\\[)?(.*?)(?:\\]\\]>)?<\\/description>/) || [])[1] || '';\n  const pubDate = (block.match(/<pubDate>(.*?)<\\/pubDate>/) || [])[1] || '';\n\n  const combined = (title + ' ' + desc).toLowerCase();\n  const isRelevant = KEYWORDS.some(kw => combined.includes(kw.toLowerCase()));\n\n  if (isRelevant && title && url) {\n    items.push({ json: {\n      title: title.trim(),\n      url: url.trim(),\n      description: desc.replace(/<[^>]+>/g, '').trim().slice(0, 600),\n      pubDate,\n      sourceType: 'regulatory',\n      sourceName: 'NIST'\n    }});\n  }\n}\n\nconsole.log(`[Pipeline] Parsed ${items.length} regulatory items`);\nreturn items;"
      },
      "id": "148d0e86-795c-43b1-9b7d-c772870c4341",
      "name": "Parse Regulatory",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1328,
        1376
      ]
    },
    {
      "parameters": {
        "jsCode": "// Merge streams + generate hash\nfunction simpleHash(str) {\n  let hash = 0;\n  for (let i = 0; i < str.length; i++) {\n    const char = str.charCodeAt(i);\n    hash = ((hash << 5) - hash) + char;\n    hash = hash & hash;\n  }\n  return Math.abs(hash).toString(16).padStart(8, '0');\n}\n\nconst items = $input.all();\nconst result = [];\n\nfor (const item of items) {\n  const { title, url } = item.json;\n  if (!title || !url) continue;\n  const hash = simpleHash(title + url);\n  result.push({ json: { ...item.json, hash } });\n}\n\nconsole.log(`[Pipeline] ${result.length} items ready for memory check`);\nreturn result;"
      },
      "id": "590dd83b-1f42-40e0-a952-20f369a34e3a",
      "name": "Merge and Hash",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1568,
        1248
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT COUNT(*) as duplicate_count, '{{ $json.hash }}' as hash, '{{ $json.title }}' as title, '{{ $json.url }}' as url, '{{ $json.sourceType }}' as source_type, '{{ $json.sourceName }}' as source_name, '{{ $json.description }}' as description FROM pipeline_memory WHERE hash = '{{ $json.hash }}'",
        "options": {}
      },
      "id": "310ed423-3129-451d-a6ac-5edc4eff258c",
      "name": "Memory Check",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        1792,
        1248
      ],
      "alwaysOutputData": true,
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "// Check duplicate count and pass through original data\nconst item = $input.item.json;\nconst count = parseInt(item.duplicate_count || '0');\n\nif (count > 0) {\n  console.log('[Pipeline] Duplicate, skipping: ' + item.title);\n  return [];\n}\n\nconsole.log('[Pipeline] New item: ' + item.title);\nreturn [{ json: {\n  hash: item.hash,\n  title: item.title,\n  url: item.url,\n  sourceType: item.source_type,\n  sourceName: item.source_name,\n  description: item.description\n} }];"
      },
      "id": "f0acbe14-d4bd-4ff8-9540-3ff0a7257457",
      "name": "Filter Duplicates",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2048,
        1248
      ]
    },
    {
      "parameters": {
        "jsCode": "// Build Brand DNA config and inject into AI prompt\nconst brandDNA = {\n  voice: 'Direct, sharp, slightly contrarian. Practitioner who has seen things fail.',\n  hook: 'Open with a surprising stat or uncomfortable truth. Never start with I or In todays.',\n  structure: 'Short punchy paragraphs. Max 2 sentences each.',\n  cta: 'End with a quiet open question \u2014 not a hard sell.',\n  length: '150-220 words',\n  emoji: 'Maximum 1, only for directional emphasis like arrow down',\n  hashtags: 'Exactly 3, always on the very last line',\n  forbidden: 'leverage, synergy, ecosystem, game-changer, delve, robust, seamless, cutting-edge',\n  rules: [\n    'Always anchor to real data or a named source',\n    'Never make predictions \u2014 focus on what the data shows now',\n    'Every paragraph should feel like it is about the reader, not the writer',\n    'If it sounds like a vendor pitch, rewrite it'\n  ]\n};\n\nconst item = $input.item.json;\n\nconst prompt = `You are a ghostwriter for a senior enterprise AI practitioner publishing on LinkedIn.\n\nBRAND DNA \u2014 NON-NEGOTIABLE RULES:\nVoice: ${brandDNA.voice}\nHook style: ${brandDNA.hook}\nStructure: ${brandDNA.structure}\nClosing: ${brandDNA.cta}\nLength: ${brandDNA.length}\nEmoji: ${brandDNA.emoji}\nHashtags: ${brandDNA.hashtags}\nForbidden words: ${brandDNA.forbidden}\nRules:\n${brandDNA.rules.map(r => '- ' + r).join('\\n')}\n\nWrite a LinkedIn post based on this ${item.sourceType} update:\nTitle: ${item.title}\nSource: ${item.sourceName}\nSummary: ${item.description}\n\nFind the ONE most surprising or counterintuitive truth in this source and build the post around it.\nReturn ONLY the post text. No preamble. No quotation marks. No markdown.`;\n\nreturn [{ json: { ...item, prompt } }];"
      },
      "id": "c29af564-88b5-46c8-a7b9-8518a85fe741",
      "name": "Brand DNA",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2288,
        1248
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $json.prompt }}",
        "options": {
          "systemMessage": "You are a ghostwriter for a senior enterprise AI practitioner. Follow the Brand DNA rules exactly. No preamble, no explanation, no markdown, no quotation marks.\nReturn a raw JSON object containing exactly two keys: \"postText\" and \"visualPrompt\". \n\n\"postText\": Follow the copy instructions exactly. \n\"visualPrompt\": Create a hyper-specific, minimalist 1:1 image prompt for a data visualization stat card based on the copy. Focus on dark mode aesthetics, clean geometry, no people, and a single, striking keyword or data metric."
        }
      },
      "id": "d2d0102c-38d0-4140-9159-311728102bab",
      "name": "AI Draft Post",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [
        2512,
        1248
      ],
      "retryOnFail": true,
      "waitBetweenTries": 2000
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "deda6592-182a-439c-a20b-a2745c262d74",
      "name": "Google Gemini",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "typeVersion": 1.1,
      "position": [
        2512,
        1472
      ],
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Grab the raw text from Gemini\nlet rawOutput = $input.item.json.output || $input.item.json.text || '';\n\n// Clean up markdown formatting just in case Gemini includes ```json tags\nrawOutput = rawOutput.replace(/```json/g, '').replace(/```/g, '').trim();\n\nlet parsedData = { postText: \"Generation failed\", visualPrompt: \"\" };\n\ntry {\n  parsedData = JSON.parse(rawOutput);\n} catch (e) {\n  console.log('[Pipeline] Failed to parse JSON from Gemini:', rawOutput);\n}\n\n// Get original item data from the Inject Brand DNA node\nconst original = $('Brand DNA').first().json;\n\nconsole.log('[Pipeline] Post drafted and visual prompt generated.');\n\nreturn [{ json: {\n  hash: original.hash,\n  title: original.title,\n  url: original.url,\n  sourceType: original.sourceType,\n  sourceName: original.sourceName,\n  description: original.description,\n  postText: parsedData.postText,\n  visualPrompt: parsedData.visualPrompt\n} }];"
      },
      "id": "702b3d82-84a3-4680-9cb2-e8fed544456e",
      "name": "Extract Content",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2848,
        1248
      ]
    },
    {
      "parameters": {
        "jsCode": "const item = $input.item.json;\n\n// Pull the visual prompt we just extracted from Gemini\n// If it failed, fallback to the title\nconst visualPrompt = item.visualPrompt || `Professional LinkedIn post stat card, ${item.title}`;\n\n// Encode the prompt for the Pollinations API\nconst encodedPrompt = encodeURIComponent(visualPrompt);\nconst imageUrl = `https://image.pollinations.ai/prompt/${encodedPrompt}?width=1024&height=1024&nologo=true`;\n\nconsole.log('[Pipeline] Visual URL generated from dynamic prompt');\n\nreturn [{ json: { \n  ...item, \n  imageUrl,\n  visualPrompt \n} }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3056,
        1248
      ],
      "id": "5d66cd14-c1e9-4673-bd0c-813027fce684",
      "name": "Generate Visual"
    },
    {
      "parameters": {
        "jsCode": "const item = $input.item.json;\n\nfunction simpleHash(str) {\n  let hash = 0;\n  for (let i = 0; i < str.length; i++) {\n    const char = str.charCodeAt(i);\n    hash = ((hash << 5) - hash) + char;\n    hash = hash & hash;\n  }\n  return Math.abs(hash).toString(16).padStart(8, '0');\n}\n\nconst token = simpleHash((item.hash || '') + 'dev-secret');\n\nconst approveUrl = `http://localhost:5678/webhook/pipeline-approval?hash=${item.hash}&token=${token}&action=approve`;\nconst rejectUrl  = `http://localhost:5678/webhook/pipeline-approval?hash=${item.hash}&token=${token}&action=reject`;\n\nconst slackBlocks = [\n  { type: 'header', text: { type: 'plain_text', text: '\ud83d\udccb LinkedIn Post \u2014 Ready for Review' } },\n  { type: 'section', fields: [\n    { type: 'mrkdwn', text: `*Source:* ${item.sourceName || 'Unknown'}` },\n    { type: 'mrkdwn', text: `*Type:* ${item.sourceType || 'Unknown'}` }\n  ]},\n  { type: 'section', text: { type: 'mrkdwn', text: `*Article:*\\n${item.title || 'Untitled'}` } },\n  { type: 'divider' },\n  { type: 'section', text: { type: 'mrkdwn', text: `*Draft Post:*\\n\\`\\`\\`${(item.postText || 'Generating...').slice(0, 800)}\\`\\`\\`` } },\n  { type: 'divider' },\n  { type: 'actions', elements: [\n    { type: 'button', text: { type: 'plain_text', text: '\u2705 Approve' }, style: 'primary', url: approveUrl },\n    { type: 'button', text: { type: 'plain_text', text: '\u274c Reject' }, style: 'danger', url: rejectUrl }\n  ]},\n  { type: 'context', elements: [{ type: 'mrkdwn', text: `\u23f1 Expires 48h` }] }\n];\n\nconst gmailHtml = `<html><body style=\"font-family:Arial,sans-serif;max-width:600px;margin:0 auto;padding:20px\">\n  <h2 style=\"color:#1a1a2e\"> LinkedIn Post \u2014 Approval Required</h2>\n  <table style=\"width:100%;border-collapse:collapse;margin-bottom:20px\">\n    <tr><td style=\"padding:8px;background:#f5f5f5;font-weight:bold\">Source</td><td style=\"padding:8px\">${item.sourceName || 'Unknown'}</td></tr>\n    <tr><td style=\"padding:8px;background:#f5f5f5;font-weight:bold\">Type</td><td style=\"padding:8px\">${item.sourceType || 'Unknown'}</td></tr>\n    <tr><td style=\"padding:8px;background:#f5f5f5;font-weight:bold\">Article</td><td style=\"padding:8px\">${item.title || 'Untitled'}</td></tr>\n  </table>\n<h3>Generated Visual</h3>\n  <div style=\"text-align:center;margin-bottom:20px\">\n    <img src=\"${item.imageUrl}\" alt=\"Post Visual\" style=\"max-width:100%;border-radius:8px\" />\n  </div>\n  <h3>Draft Post</h3>\n  <div style=\"background:#f9f9f9;border-left:4px solid #667eea;padding:16px;white-space:pre-wrap;font-size:14px;line-height:1.7\">${item.postText || 'Generating...'}</div>\n  <br/>\n  <div style=\"text-align:center;margin:28px 0\">\n    <a href=\"${approveUrl}\" style=\"background:#28a745;color:white;padding:14px 36px;text-decoration:none;border-radius:6px;margin-right:16px;font-weight:bold;font-size:16px\">\u2705 Approve</a>\n    <a href=\"${rejectUrl}\" style=\"background:#dc3545;color:white;padding:14px 36px;text-decoration:none;border-radius:6px;font-weight:bold;font-size:16px\">\u274c Reject</a>\n  </div>\n  <p style=\"color:#aaa;font-size:11px;text-align:center\">Expires in 48 hours \u00b7 business_post_pipeline</p>\n</body></html>`;\n\nreturn [{ json: { ...item, token, approveUrl, rejectUrl, slackBlocks, gmailHtml } }];"
      },
      "id": "2d8053aa-d4fe-4265-8a98-10958daa69fc",
      "name": "Build Notification",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3264,
        1248
      ]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "#content-approvals",
          "mode": "name"
        },
        "text": "\ud83d\udccb New post ready for approval. Check Gmail for details and approve/reject buttons.",
        "otherOptions": {}
      },
      "id": "b213eb3d-1e42-43a1-a757-fa357cbe205d",
      "name": "Slack Notify",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [
        3664,
        1088
      ],
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO pipeline_memory (hash, title, url, source_name, source_type, date_fetched, status, post_text, image_url)\nVALUES ('{{ $json.hash }}', '{{ $json.title.replace(/'/g, \"''\") }}', '{{ $json.url }}', '{{ $json.sourceName }}', '{{ $json.sourceType }}', NOW(), 'pending_approval', '{{ $json.postText.replace(/'/g, \"''\") }}', '{{ $json.imageUrl }}')\nON CONFLICT (hash) DO UPDATE \nSET post_text = EXCLUDED.post_text, image_url = EXCLUDED.image_url;",
        "options": {}
      },
      "id": "b1c65524-868b-4f66-a7fd-783d12a260c2",
      "name": "Save to Memory",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        3456,
        1088
      ],
      "alwaysOutputData": true,
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "sendTo": "=agentutk@gmail.com",
        "subject": "=\ud83d\udccb Approve Post: {{ $json.title }}",
        "message": "={{ $json.gmailHtml }}",
        "options": {}
      },
      "id": "80c79d4b-85a9-4688-b42b-68800df0daa6",
      "name": "Gmail",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        3456,
        1408
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "path": "pipeline-approval",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "58087f86-29ef-4556-847a-dfd0b2fe6f77",
      "name": "Approval Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        1040,
        1936
      ]
    },
    {
      "parameters": {
        "jsCode": "// Validate HMAC token\nconst { hash, token, action } = $input.item.json.query || {};\n\nif (!hash || !token || !['approve','reject'].includes(action)) {\n  return [{ json: { valid: false, reason: 'Bad parameters' } }];\n}\n\nfunction simpleHash(str) {\n  let h = 0;\n  for (let i = 0; i < str.length; i++) {\n    const char = str.charCodeAt(i);\n    h = ((h << 5) - h) + char;\n    h = h & h;\n  }\n  return Math.abs(h).toString(16).padStart(8, '0');\n}\n\nconst expected = simpleHash(hash + 'dev-secret');\n\nif (token !== expected) {\n  return [{ json: { valid: false, reason: 'Invalid token' } }];\n}\n\nreturn [{ json: { valid: true, hash, action } }];"
      },
      "id": "791802ce-c69e-48fd-80e8-40dda1212124",
      "name": "Validate Token",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1280,
        1936
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "cond-approve",
              "leftValue": "={{ $json.action }}",
              "rightValue": "approve",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "1978eb0a-10b5-4059-ab58-91b232597afc",
      "name": "Approve or Reject",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1520,
        1936
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "6546b369-08e3-423e-983b-d4351b642128",
              "leftValue": "={{ $('Validate Token').first().json.action }}",
              "rightValue": "approve",
              "operator": {
                "type": "string",
                "operation": "equals",
                "name": "filter.operator.equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        2256,
        1936
      ],
      "id": "24819ba6-e856-4701-9519-5d69e8adf249",
      "name": "If Approved"
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT post_text, image_url\nFROM pipeline_memory\nWHERE hash = '{{ $('Validate Token').item.json.hash }}';",
        "options": {}
      },
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        2528,
        1840
      ],
      "id": "c84d5409-1c9d-46d1-a248-6c8bca3f99ce",
      "name": "Fetch Post Assets",
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "url": "={{ $json.image_url }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        2768,
        1840
      ],
      "id": "3fb3d3b3-f0ec-4465-ad68-bc99839a1c72",
      "name": "Download Image"
    },
    {
      "parameters": {
        "person": "qazgZ95WIL",
        "text": "={{ $('Fetch Post Assets').item.json.post_text }}",
        "shareMediaCategory": "IMAGE",
        "additionalFields": {
          "visibility": "PUBLIC"
        }
      },
      "type": "n8n-nodes-base.linkedIn",
      "typeVersion": 1,
      "position": [
        2976,
        1840
      ],
      "id": "f218e34e-14e6-4c92-89e8-2e5d2d7f38e3",
      "name": "Post on LinkedIn",
      "credentials": {
        "linkedInOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const err = $input.item.json.error || 'Unknown error';\nconsole.error(`[Pipeline] Earnings feed failed: ${err}`);\nreturn [];"
      },
      "id": "3f239ceb-a2f6-4691-90aa-cfe5db37a42a",
      "name": "Earnings Error",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1328,
        928
      ]
    },
    {
      "parameters": {
        "jsCode": "const err = $input.item.json.error || 'Unknown error';\nconsole.error(`[Pipeline] Regulatory feed failed: ${err}`);\nreturn [];"
      },
      "id": "72d798bf-719e-492b-9589-f6f3518c2a2d",
      "name": "Regulatory Error",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1312,
        1584
      ]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "#new-posts",
          "mode": "name"
        },
        "text": "Success! The approved post is now live on LinkedIn.",
        "otherOptions": {}
      },
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [
        3200,
        1840
      ],
      "id": "16b86e84-e7b3-45d2-af40-c41903e74812",
      "name": "Post Notification",
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "text",
        "responseBody": "=\u2705 Decision recorded: {{ $json.action }}. Pipeline updated.",
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "text/html"
              }
            ]
          }
        }
      },
      "id": "0f006f05-d135-4af8-9d28-197692ae2c41",
      "name": "Send Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        2032,
        1936
      ]
    }
  ],
  "connections": {
    "Update Status + Audit Log": {
      "main": [
        [
          {
            "node": "Send Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Init Run",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Init Run": {
      "main": [
        [
          {
            "node": "Fetch Earnings",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Regulatory",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Earnings": {
      "main": [
        [
          {
            "node": "Parse Earnings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Regulatory": {
      "main": [
        [
          {
            "node": "Parse Regulatory",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Earnings": {
      "main": [
        [
          {
            "node": "Merge and Hash",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Regulatory": {
      "main": [
        [
          {
            "node": "Merge and Hash",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge and Hash": {
      "main": [
        [
          {
            "node": "Memory Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Memory Check": {
      "main": [
        [
          {
            "node": "Filter Duplicates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Duplicates": {
      "main": [
        [
          {
            "node": "Brand DNA",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Brand DNA": {
      "main": [
        [
          {
            "node": "AI Draft Post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Draft Post": {
      "main": [
        [
          {
            "node": "Extract Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini": {
      "ai_languageModel": [
        [
          {
            "node": "AI Draft Post",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Extract Content": {
      "main": [
        [
          {
            "node": "Generate Visual",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Visual": {
      "main": [
        [
          {
            "node": "Build Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Notification": {
      "main": [
        [
          {
            "node": "Save to Memory",
            "type": "main",
            "index": 0
          },
          {
            "node": "Gmail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack Notify": {
      "main": [
        []
      ]
    },
    "Save to Memory": {
      "main": [
        [
          {
            "node": "Slack Notify",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Approval Webhook": {
      "main": [
        [
          {
            "node": "Validate Token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Token": {
      "main": [
        [
          {
            "node": "Approve or Reject",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Approve or Reject": {
      "main": [
        [
          {
            "node": "Update Status + Audit Log",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update Status + Audit Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Approved": {
      "main": [
        [
          {
            "node": "Fetch Post Assets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Post Assets": {
      "main": [
        [
          {
            "node": "Download Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Image": {
      "main": [
        [
          {
            "node": "Post on LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post on LinkedIn": {
      "main": [
        [
          {
            "node": "Post Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Response": {
      "main": [
        [
          {
            "node": "If Approved",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": false,
    "timeSavedMode": "fixed",
    "errorWorkflow": "aQ4pRJ43HvubpN2W",
    "callerPolicy": "workflowsFromSameOwner"
  },
  "versionId": "c7c1bfe9-69d2-4dd7-9723-2b39faedab3d",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodeGroups": [],
  "id": "SkUAnXnyWZSLT87x",
  "tags": []
}

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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Business Post Pipeline. Uses postgres, httpRequest, agent, lmChatGoogleGemini. Scheduled trigger; 30 nodes.

Source: https://github.com/utkarsh-ankit/business_post_pipeline/blob/main/workflows/workflow_5.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Business Post Pipeline. Uses httpRequest, postgres, agent, lmChatGoogleGemini. Scheduled trigger; 27 nodes.

HTTP Request, Postgres, Agent +3
AI & RAG

Business Post Pipeline. Uses agent, lmChatGoogleGemini, slack, postgres. Scheduled trigger; 27 nodes.

Agent, Google Gemini Chat, Slack +3
AI & RAG

Business Post Pipeline. Uses agent, lmChatGoogleGemini, slack, httpRequest. Scheduled trigger; 24 nodes.

Agent, Google Gemini Chat, Slack +3
AI & RAG

Automates sales data analysis and strategic insight generation for sales managers and strategists needing actionable intelligence. Fetches multi-source data from sales, marketing, and financial system

HTTP Request, Agent, OpenAI Chat +6
AI & RAG

Scheduled runs collect data from oil markets, global shipping movements, news sources, and official reports. The system performs statistical checks to detect anomalies and volatility shifts. An AI-dri

HTTP Request, Agent, Gmail +3