AutomationFlowsAI & RAG › Unified Marketing Automation — Master Scraper / AI Factory / Publisher /…

Unified Marketing Automation — Master Scraper / AI Factory / Publisher /…

Original n8n title: Unified Marketing Automation — Master Scraper / AI Factory / Publisher / Analytics / Control

Unified Marketing Automation — Master Scraper / AI Factory / Publisher / Analytics / Control. Uses httpRequest, openai, telegram. Scheduled trigger; 24 nodes.

Cron / scheduled trigger★★★★☆ complexityAI-powered24 nodesHTTP RequestOpenAITelegram
AI & RAG Trigger: Cron / scheduled Nodes: 24 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow follows the HTTP Request → OpenAI 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": "Unified Marketing Automation \u2014 Master Scraper / AI Factory / Publisher / Analytics / Control",
  "active": false,
  "nodes": [
    {
      "id": "1",
      "name": "Scrape Cron Trigger",
      "type": "n8n-nodes-base.cron",
      "typeVersion": 1,
      "position": [
        260,
        120
      ],
      "parameters": {
        "triggerTimes": {
          "item": [
            {
              "mode": "everyHour",
              "value": 1
            }
          ]
        }
      }
    },
    {
      "id": "2",
      "name": "ClickBank HTTP",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        520,
        40
      ],
      "parameters": {
        "requestMethod": "GET",
        "url": "={{$env.CLICKBANK_API_URL || 'https://api.clickbank.example/marketplace'}}",
        "responseFormat": "json",
        "headerParametersJson": "={\"Authorization\": \"Bearer {{$env.CLICKBANK_API_KEY || 'YOUR_CLICKBANK_API_KEY'}}\",\"Accept\":\"application/json\"}",
        "options": {}
      }
    },
    {
      "id": "3",
      "name": "Digistore24 HTTP",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        520,
        160
      ],
      "parameters": {
        "requestMethod": "GET",
        "url": "={{$env.DIGISTORE24_API_URL || 'https://api.digistore24.example/products'}}",
        "responseFormat": "json",
        "headerParametersJson": "={\"Authorization\": \"Bearer {{$env.DIGISTORE24_API_KEY || 'YOUR_DIGISTORE24_API_KEY'}}\",\"Accept\":\"application/json\"}",
        "options": {}
      }
    },
    {
      "id": "4",
      "name": "Impact HTTP",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        520,
        280
      ],
      "parameters": {
        "requestMethod": "GET",
        "url": "={{$env.IMPACT_API_URL || 'https://api.impact.example/offers'}}",
        "responseFormat": "json",
        "headerParametersJson": "={\"Authorization\": \"Bearer {{$env.IMPACT_API_KEY || 'YOUR_IMPACT_API_KEY'}}\",\"Accept\":\"application/json\"}",
        "options": {}
      }
    },
    {
      "id": "5",
      "name": "PartnerStack HTTP",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        520,
        400
      ],
      "parameters": {
        "requestMethod": "GET",
        "url": "={{$env.PARTNERSTACK_API_URL || 'https://api.partnerstack.example/programs'}}",
        "responseFormat": "json",
        "headerParametersJson": "={\"Authorization\": \"Bearer {{$env.PARTNERSTACK_API_KEY || 'YOUR_PARTNERSTACK_API_KEY'}}\",\"Accept\":\"application/json\"}",
        "options": {}
      }
    },
    {
      "id": "6",
      "name": "Merge & Normalize Scraped Data",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        800,
        220
      ],
      "parameters": {
        "functionCode": "const staticData = getWorkflowStaticData('global');\n\n// Collect data from multiple inputs (ClickBank, Digistore24, Impact, PartnerStack)\nconst out = { offers: [] };\n\n// Each input will be in items; iterate through all input streams\nfor (const item of items) {\n  // item.json could be raw API response; attempt to normalize common fields\n  const src = item.json;\n  // Heuristic normalization: if 'offers' or 'products' exist, map them\n  if (src && Array.isArray(src.offers)) {\n    src.offers.forEach(o => out.offers.push(Object.assign({source: src.source || 'unknown'}, o)));\n  } else if (src && Array.isArray(src.products)) {\n    src.products.forEach(p => out.offers.push(Object.assign({source: src.source || 'unknown'}, p)));\n  } else if (src && src.items && Array.isArray(src.items)) {\n    src.items.forEach(i => out.offers.push(Object.assign({source: src.source || 'unknown'}, i)));\n  } else {\n    // Fallback: wrap the whole json as one offer\n    out.offers.push(Object.assign({source: src.source || 'unknown'}, src));\n  }\n}\n\n// Optional: deduplicate by 'id' or 'title'\nconst seen = new Set();\nout.offers = out.offers.filter(o => {\n  const key = o.id || o.title || JSON.stringify(o).slice(0,80);\n  if (seen.has(key)) return false;\n  seen.add(key);\n  return true;\n});\n\n// Save a simple heartbeat/status so control dashboard can report it\nstaticData.lastScrape = new Date().toISOString();\nstaticData.lastScrapeCount = out.offers.length;\n\nreturn [ { json: out } ];"
      }
    },
    {
      "id": "7",
      "name": "Build AI Prompt",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        1080,
        220
      ],
      "parameters": {
        "functionCode": "const staticData = getWorkflowStaticData('global');\nconst scraped = items[0].json;\n\n// Build a concise prompt for the AI to create captions/headlines and a short script\nconst offers = (scraped.offers || []).slice(0,6);\nlet prompt = `You are a creative marketing copywriter. For each of the following affiliate offers produce: 1) 5 short social captions (<= 120 chars), 2) 1 punchy headline, 3) 1 short 20-30s video script outline, and 4) 3 hashtag suggestions. Return JSON array 'results'.\\n\\n`;\n\noffers.forEach((o, i) => {\n  const title = o.title || o.name || o.program || `Offer ${i+1}`;\n  const desc = o.description || o.summary || o.short_description || 'No description available';\n  prompt += `Offer ${i+1}: Title: ${title}\\nDescription: ${desc}\\nCommission: ${o.commission || o.payout || 'unknown'}\\nURL: ${o.url || o.link || 'N/A'}\\n\\n`;\n});\n\nprompt += `Provide only valid JSON, with top-level key 'results' and each result containing keys: title, headline, captions (array), script, hashtags (array), source.`;\n\nstaticData.lastPrompt = new Date().toISOString();\n\nreturn [ { json: { prompt } } ];"
      }
    },
    {
      "id": "8",
      "name": "OpenAI \u2014 Content (ChatCompletion)",
      "type": "n8n-nodes-base.openai",
      "typeVersion": 1,
      "position": [
        1360,
        220
      ],
      "credentials": {
        "openAIApi": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "resource": "chatCompletion",
        "operation": "create",
        "model": "gpt-4o-mini",
        "messages": [
          {
            "role": "system",
            "content": "You are a concise, structured marketing writer. Output JSON only."
          },
          {
            "role": "user",
            "content": "={{$json.prompt}}"
          }
        ],
        "options": {}
      },
      "notes": "If you prefer Google Gemini, replace this node with a HTTP Request to the Gemini endpoint and set Authorization to Bearer $env.GOOGLE_API_KEY. (Gemini response parsing will need adjusting.)"
    },
    {
      "id": "9",
      "name": "Parse AI Output \u2192 Captions + Scripts",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        1640,
        220
      ],
      "parameters": {
        "functionCode": "const staticData = getWorkflowStaticData('global');\n\n// Try to parse AI text into JSON safely\nconst aiText = $json[\"choices\"] ? $json.choices[0].message.content : ($json.choicesText || $json.text || $json);\nlet results = [];\ntry {\n  // If it's already an object\n  if (typeof aiText === 'object') {\n    results = aiText.results || aiText;\n  } else {\n    // Clean common prefixes and parse\n    const cleaned = aiText.toString().trim().replace(/^\\uFEFF/, '');\n    // Try to find first '{' to robustly extract JSON\n    const firstBrace = cleaned.indexOf('{');\n    const jsonString = firstBrace !== -1 ? cleaned.slice(firstBrace) : cleaned;\n    const parsed = JSON.parse(jsonString);\n    results = parsed.results || parsed;\n  }\n} catch (err) {\n  // If parsing fails, fallback: return the text as one item\n  results = [{ title: 'AI-parsing-fallback', headline: '', captions: [aiText.toString().slice(0,120)], script: aiText.toString().slice(0,800), hashtags: [] }];\n  staticData.aiParseError = (staticData.aiParseError || 0) + 1;\n}\n\nstaticData.lastAiCount = results.length;\n\nreturn [ { json: { results } } ];"
      }
    },
    {
      "id": "10",
      "name": "Optional AI Video Gen (placeholder)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        1920,
        220
      ],
      "parameters": {
        "requestMethod": "POST",
        "url": "={{$env.AI_VIDEO_API_URL || 'https://api.aivideo.example/generate'}}",
        "jsonParameters": true,
        "options": {},
        "bodyParametersJson": "={\"script\": {{$json.results}}, \"api_key\": \"{{$env.AI_VIDEO_API_KEY || 'YOUR_AI_VIDEO_API_KEY'}}\", \"callback_url\": \"{{$env.AI_VIDEO_CALLBACK || 'https://your-callback.example/ai-video'}}\"}"
      },
      "notes": "This is a placeholder. If you use a community node (e.g., 'n8n-nodes-community/aivideo'), set that node up and mark this node as a toggle/fallback."
    },
    {
      "id": "11",
      "name": "Publisher \u2014 TikTok (HTTP fallback)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        2200,
        80
      ],
      "parameters": {
        "requestMethod": "POST",
        "url": "={{$env.TIKTOK_API_URL || 'https://api.tiktok.example/post'}}",
        "jsonParameters": true,
        "bodyParametersJson": "={\"caption\": {{$json.results[0].captions[0] || 'Auto caption'}}, \"video_url\": \"{{$json.video_url || ''}}\", \"access_token\": \"{{$env.TIKTOK_ACCESS_TOKEN || 'YOUR_TIKTOK_TOKEN'}}\"}",
        "responseFormat": "json"
      },
      "notes": "Community node available: 'n8n-nodes-community/tiktok'. This HTTP node is the fallback when community is not installed."
    },
    {
      "id": "12",
      "name": "Publisher \u2014 Instagram (HTTP fallback)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        2200,
        200
      ],
      "parameters": {
        "requestMethod": "POST",
        "url": "={{$env.INSTAGRAM_API_URL || 'https://graph.facebook.com/v15.0/' + $env.INSTAGRAM_BUSINESS_ID + '/media'}}",
        "jsonParameters": true,
        "bodyParametersJson": "={\"caption\": {{$json.results[0].captions[0] || 'Auto caption'}}, \"image_url\": \"{{$json.image_url || ''}}\", \"access_token\": \"{{$env.INSTAGRAM_ACCESS_TOKEN || 'YOUR_INSTAGRAM_TOKEN'}}\"}",
        "responseFormat": "json"
      },
      "notes": "For Instagram publishing use official Graph API; you may need to chain to a second publish endpoint to create_post after uploading media."
    },
    {
      "id": "13",
      "name": "Publisher \u2014 YouTube (HTTP fallback)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        2200,
        320
      ],
      "parameters": {
        "requestMethod": "POST",
        "url": "={{$env.YOUTUBE_UPLOAD_URL || 'https://www.googleapis.com/upload/youtube/v3/videos'}}",
        "jsonParameters": true,
        "bodyParametersJson": "={\"snippet\": {\"title\": {{$json.results[0].headline || 'Auto Title'}}, \"description\": {{$json.results[0].captions.join('\\n') || ''}}}, \"status\": {\"privacyStatus\": \"unlisted\"}}",
        "headerParametersJson": "={\"Authorization\": \"Bearer {{$env.YOUTUBE_OAUTH_TOKEN || 'YOUR_YOUTUBE_OAUTH_TOKEN'}}\",\"Content-Type\":\"application/json\"}",
        "responseFormat": "json"
      },
      "notes": "Prefer the official 'n8n-nodes-base.googleDrive' + Google OAuth credentials for authenticated uploads. This is a simplified fallback."
    },
    {
      "id": "14",
      "name": "Publisher \u2014 LinkedIn (HTTP fallback)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        2200,
        440
      ],
      "parameters": {
        "requestMethod": "POST",
        "url": "={{$env.LINKEDIN_API_URL || 'https://api.linkedin.com/v2/ugcPosts'}}",
        "jsonParameters": true,
        "bodyParametersJson": "={\"author\": \"urn:li:person:\" + ($env.LINKEDIN_PERSON_ID || 'YOUR_PERSON_ID'), \"lifecycleState\": \"PUBLISHED\", \"specificContent\": {\"com.linkedin.ugc.ShareContent\": {\"shareCommentary\": {\"text\": {{$json.results[0].captions[0] || 'Auto caption'}}}, \"shareMediaCategory\": \"NONE\"}}, \"visibility\": {\"com.linkedin.ugc.MemberNetworkVisibility\": \"PUBLIC\"}}",
        "headerParametersJson": "={\"Authorization\": \"Bearer {{$env.LINKEDIN_ACCESS_TOKEN || 'YOUR_LINKEDIN_TOKEN'}}\",\"Content-Type\":\"application/json\"}",
        "responseFormat": "json"
      }
    },
    {
      "id": "15",
      "name": "Publisher \u2014 Twitter / X (HTTP fallback)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        2200,
        560
      ],
      "parameters": {
        "requestMethod": "POST",
        "url": "={{$env.TWITTER_API_URL || 'https://api.twitter.com/2/tweets'}}",
        "jsonParameters": true,
        "bodyParametersJson": "={\"text\": {{$json.results[0].captions[0] || 'Auto caption'}}}",
        "headerParametersJson": "={\"Authorization\": \"Bearer {{$env.TWITTER_BEARER_TOKEN || 'YOUR_TWITTER_BEARER_TOKEN'}}\",\"Content-Type\":\"application/json\"}",
        "responseFormat": "json"
      }
    },
    {
      "id": "16",
      "name": "Publisher \u2014 Pinterest (HTTP fallback)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        2200,
        680
      ],
      "parameters": {
        "requestMethod": "POST",
        "url": "={{$env.PINTEREST_API_URL || 'https://api.pinterest.com/v5/pins'}}",
        "jsonParameters": true,
        "bodyParametersJson": "={\"board_id\": $env.PINTEREST_BOARD_ID, \"title\": {{$json.results[0].headline || 'Auto Title'}}, \"alt_text\": {{$json.results[0].captions[0] || ''}}, \"link\": {{$json.results[0].url || ''}}}",
        "headerParametersJson": "={\"Authorization\": \"Bearer {{$env.PINTEREST_ACCESS_TOKEN || 'YOUR_PINTEREST_TOKEN'}}\",\"Content-Type\":\"application/json\"}",
        "responseFormat": "json"
      }
    },
    {
      "id": "17",
      "name": "Analytics Cron Trigger",
      "type": "n8n-nodes-base.cron",
      "typeVersion": 1,
      "position": [
        260,
        520
      ],
      "parameters": {
        "triggerTimes": {
          "item": [
            {
              "mode": "everyDay",
              "value": 1
            }
          ]
        }
      }
    },
    {
      "id": "18",
      "name": "Fetch Platform Analytics",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        520,
        520
      ],
      "parameters": {
        "requestMethod": "POST",
        "url": "={{$env.ANALYTICS_AGGREGATOR_URL || 'https://api.analytics.example/aggregate'}}",
        "jsonParameters": true,
        "bodyParametersJson": "={\"platforms\": [\"tiktok\",\"instagram\",\"youtube\",\"linkedin\",\"twitter\",\"pinterest\"], \"credentials\": {\"tiktok\": $env.TIKTOK_ANALYTICS_KEY}}",
        "responseFormat": "json"
      }
    },
    {
      "id": "19",
      "name": "Evaluate Analytics & Thresholds",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        800,
        520
      ],
      "parameters": {
        "functionCode": "const staticData = getWorkflowStaticData('global');\nconst analytics = items[0].json || {};\n\n// Simple rule: if any platform saw a drop > threshold, alert\nconst alerts = [];\nconst threshold = Number($env.ANALYTICS_ALERT_THRESHOLD || 0.2); // 20% default\n\nfor (const [platform, data] of Object.entries(analytics)) {\n  const change = data.change || 0;\n  if (change <= -threshold) {\n    alerts.push({ platform, change, details: data });\n  }\n}\n\nstaticData.lastAnalytics = analytics;\n\nreturn [ { json: { alerts } } ];"
      }
    },
    {
      "id": "20",
      "name": "Telegram Alert",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1,
      "position": [
        1080,
        520
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "operation": "sendMessage",
        "chatId": "={{$env.TELEGRAM_CHAT_ID || 'YOUR_TELEGRAM_CHAT_ID'}}",
        "text": "={{JSON.stringify($json.alerts, null, 2)}}"
      }
    },
    {
      "id": "21",
      "name": "Control Webhook \u2014 START",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        260,
        760
      ],
      "parameters": {
        "path": "control/start",
        "httpMethod": "POST",
        "responseMode": "lastNode",
        "options": {}
      }
    },
    {
      "id": "22",
      "name": "Control Webhook \u2014 STOP",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        520,
        760
      ],
      "parameters": {
        "path": "control/stop",
        "httpMethod": "POST",
        "responseMode": "lastNode",
        "options": {}
      }
    },
    {
      "id": "23",
      "name": "Control Action (Function)",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        800,
        760
      ],
      "parameters": {
        "functionCode": "const staticData = getWorkflowStaticData('global');\n\n// Determine action from webhook body or query\nconst body = items[0].json || {};\nconst action = body.action || (items[0].binary ? 'unknown' : (items[0].json && items[0].json._query && items[0].json._query.action) || 'status');\n\nif (action === 'start') {\n  staticData.enabled = true;\n  staticData.lastControl = { action: 'start', at: new Date().toISOString() };\n  return [ { json: { status: 'started' } } ];\n}\nif (action === 'stop') {\n  staticData.enabled = false;\n  staticData.lastControl = { action: 'stop', at: new Date().toISOString() };\n  return [ { json: { status: 'stopped' } } ];\n}\n// Default: return current status\nreturn [ { json: { status: staticData.enabled ? 'running' : 'stopped', lastControl: staticData.lastControl || null } } ];"
      }
    },
    {
      "id": "24",
      "name": "Control Dashboard \u2014 Status (Function)",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        1080,
        760
      ],
      "parameters": {
        "functionCode": "const staticData = getWorkflowStaticData('global');\nreturn [ { json: { enabled: staticData.enabled || false, lastScrape: staticData.lastScrape || null, lastScrapeCount: staticData.lastScrapeCount || 0, lastAiCount: staticData.lastAiCount || 0, lastAnalytics: staticData.lastAnalytics || null } } ];"
      }
    }
  ],
  "connections": {
    "Scrape Cron Trigger": {
      "main": [
        [
          {
            "node": "ClickBank HTTP",
            "type": "main",
            "index": 0
          },
          {
            "node": "Digistore24 HTTP",
            "type": "main",
            "index": 0
          },
          {
            "node": "Impact HTTP",
            "type": "main",
            "index": 0
          },
          {
            "node": "PartnerStack HTTP",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ClickBank HTTP": {
      "main": [
        [
          {
            "node": "Merge & Normalize Scraped Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Digistore24 HTTP": {
      "main": [
        [
          {
            "node": "Merge & Normalize Scraped Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Impact HTTP": {
      "main": [
        [
          {
            "node": "Merge & Normalize Scraped Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PartnerStack HTTP": {
      "main": [
        [
          {
            "node": "Merge & Normalize Scraped Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge & Normalize Scraped Data": {
      "main": [
        [
          {
            "node": "Build AI Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build AI Prompt": {
      "main": [
        [
          {
            "node": "OpenAI \u2014 Content (ChatCompletion)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI \u2014 Content (ChatCompletion)": {
      "main": [
        [
          {
            "node": "Parse AI Output \u2192 Captions + Scripts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse AI Output \u2192 Captions + Scripts": {
      "main": [
        [
          {
            "node": "Optional AI Video Gen (placeholder)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Publisher \u2014 TikTok (HTTP fallback)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Publisher \u2014 Instagram (HTTP fallback)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Publisher \u2014 YouTube (HTTP fallback)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Publisher \u2014 LinkedIn (HTTP fallback)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Publisher \u2014 Twitter / X (HTTP fallback)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Publisher \u2014 Pinterest (HTTP fallback)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analytics Cron Trigger": {
      "main": [
        [
          {
            "node": "Fetch Platform Analytics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Platform Analytics": {
      "main": [
        [
          {
            "node": "Evaluate Analytics & Thresholds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Evaluate Analytics & Thresholds": {
      "main": [
        [
          {
            "node": "Telegram Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Control Webhook \u2014 START": {
      "main": [
        [
          {
            "node": "Control Action (Function)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Control Webhook \u2014 STOP": {
      "main": [
        [
          {
            "node": "Control Action (Function)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Control Action (Function)": {
      "main": [
        [
          {
            "node": "Control Dashboard \u2014 Status (Function)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {},
  "meta": {
    "credentials_to_create_in_n8n": {
      "openai": "Create an OpenAI credential in n8n and set as credential referenced by 'YOUR_OPENAI_CREDENTIAL_ID' (or set $env.OPENAI_API_KEY and update node to use env variable).",
      "telegram": "Create a Telegram Bot credential (bot token) and set as 'YOUR_TELEGRAM_BOT_CREDENTIAL_ID'. Also set TELEGRAM_CHAT_ID env var.",
      "google_oauth": "For YouTube / Drive: create Google OAuth credentials in n8n and set environment vars: YOUTUBE_OAUTH_TOKEN or use the Google Drive/YouTube built-in nodes.",
      "tiktok": "Set TIKTOK_ACCESS_TOKEN and/or create a TikTok credential in n8n if community node available.",
      "instagram": "Set INSTAGRAM_ACCESS_TOKEN and INSTAGRAM_BUSINESS_ID (or use Facebook Graph credentials).",
      "linkedin": "Set LINKEDIN_ACCESS_TOKEN (or create credential).",
      "twitter": "Set TWITTER_BEARER_TOKEN in env or create credential.",
      "pinterest": "Set PINTEREST_ACCESS_TOKEN and PINTEREST_BOARD_ID in env.",
      "clickbank/digistore24/impact/partnerstack": "Set CLICKBANK_API_KEY, DIGISTORE24_API_KEY, IMPACT_API_KEY, PARTNERSTACK_API_KEY and their API_URLs if available."
    },
    "import_and_enable_steps": [
      "1) Save this JSON to a file named e.g., 'marketing-automation-n8n.json'.",
      "2) In n8n UI: Top-right \u25b6 Import > From File > choose the saved JSON file.",
      "3) After import, visit Credentials and create the credentials listed above (OpenAI, Telegram, Google OAuth, platform tokens).",
      "4) Set required environment variables in your n8n host (or replace $env.* placeholders with credential ids in nodes if you prefer).",
      "5) Enable the workflow (toggle active).",
      "6) Test modules step-by-step as described below."
    ],
    "how_to_test_each_module": [
      "MASTER SCRAPER ENGINE: After importing and configuring API keys, in Workflow Execute > select the 'Scrape Cron Trigger' node and 'Execute Node' to run a single scrape. Inspect results in 'Merge & Normalize Scraped Data' output.",
      "AI CONTENT FACTORY: With sample scraped data present, execute 'Build AI Prompt' -> 'OpenAI \u2014 Content (ChatCompletion)' -> 'Parse AI Output \u2192 Captions + Scripts' to verify JSON is produced. If using Gemini, replace OpenAI node with HTTP Request to Gemini endpoint and set $env.GOOGLE_API_KEY.",
      "MULTI-PLATFORM PUBLISHER: Configure platform tokens. Execute 'Parse AI Output \u2192 Captions + Scripts' and then execute the specific publisher node (e.g., 'Publisher \u2014 TikTok (HTTP fallback)') to test the HTTP call to your platform or staging endpoint.",
      "ANALYTICS TRACKER: Configure ANALYTICS_AGGREGATOR_URL or set up each platform's analytics endpoints, then 'Execute Node' for 'Fetch Platform Analytics' and check 'Evaluate Analytics & Thresholds' for alerts. If alerts are created, 'Telegram Alert' will send messages to configured chat.",
      "CONTROL DASHBOARD: Use HTTP POST to <your-n8n-host>/webhook/control/start with JSON body {\"action\":\"start\"} to enable the workflow (sets static flag). Similarly POST {\"action\":\"stop\"} to /webhook/control/stop. Call /webhook/control/start with action 'status' or invoke without action to get current status. The 'Control Dashboard \u2014 Status (Function)' also returns status when 'Control Action' runs."
    ],
    "community_nodes_and_self_hosting_notes": [
      "This workflow intentionally uses core HTTP Request nodes as fallbacks for community nodes.",
      "If you wish to use community nodes (faster platform integrations or scrapers):",
      " - Install the community n8n nodes on your self-hosted instance (e.g., n8n-nodes-community/tiktok, scrape-ninja, blotato, etc.).",
      " - Replace the HTTP fallback nodes with the community nodes and use the provided credentials.",
      "Self-hosting tips for heavy scraping: Use a self-hosted proxy pool or Scraping service, obey robots.txt and terms of service of target sites, and rotate IPs. If using a community scraper that needs a local binary, follow that project's README and add a toggle in the workflow to switch between 'HTTP fallback' and 'community node' (we left the placeholder 'notes' in those nodes)."
    ],
    "notes_and_safety": [
      "All secret values are referenced via environment variables ($env.*) or labeled 'YOUR_*_CREDENTIAL_ID' to avoid embedding secrets in the workflow JSON.",
      "This workflow is a template and needs platform-specific adjustments (scopes, OAuth flows, upload steps for media).",
      "Respect each platform's API rate limits and Terms of Service. For scraping marketplaces that disallow scraping, prefer official APIs or partner programs."
    ]
  }
}

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

Unified Marketing Automation — Master Scraper / AI Factory / Publisher / Analytics / Control. Uses httpRequest, openai, telegram. Scheduled trigger; 24 nodes.

Source: https://gist.github.com/lhayeostora-cmyk/a0f5fd6688dec262cf86b2063b78c115 — 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

AI Institutional Stock Valuation Engine with Risk Scoring & Scenario Targets

Google Sheets, XML, HTTP Request +3
AI & RAG

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

Google Sheets, XML, HTTP Request +3
AI & RAG

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

Google Sheets, OpenAI, XML +3
AI & RAG

AI Animated Shorts Factory (9:16) - 10/day - 5 Characters - TG Approve - YT Upload. Uses dataStore, httpRequest, openAi, telegram. Scheduled trigger; 40 nodes.

Data Store, HTTP Request, OpenAI +3
AI & RAG

This n8n workflow is a comprehensive crypto intelligence system that monitors multiple data sources simultaneously to identify alpha opportunities, whale movements, and emerging trends before they bec

Reddit, HTTP Request, OpenAI +1