AutomationFlowsEmail & Gmail › Narrative Threat / Opportunity Detection (askrally)

Narrative Threat / Opportunity Detection (askrally)

ByRhys @virtual-rf on n8n.io

This workflow turns news monitoring into an early-warning demand engine. It continuously ingests Google Alert RSS feeds, extracts the full text of every article, and runs real-time purchase-intent modeling to predict which stories will sway your buyers—positively or negatively.…

Cron / scheduled trigger★★★★☆ complexity12 nodesHTTP RequestGmailRSS Feed Read
Email & Gmail Trigger: Cron / scheduled Nodes: 12 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #5234 — we link there as the canonical source.

This workflow follows the Gmail → HTTP Request 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
{
  "id": "bHmHHEt75JaovVaB",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "narrative monitoring",
  "tags": [
    {
      "id": "G8kDAqCFw5rVCOgs",
      "name": "News Pipeline Testing",
      "createdAt": "2025-06-22T22:10:20.475Z",
      "updatedAt": "2025-06-22T22:10:20.475Z"
    }
  ],
  "nodes": [
    {
      "id": "52dc2d74-fe6a-422c-9be9-d14b68250afc",
      "name": "Code",
      "type": "n8n-nodes-base.code",
      "position": [
        600,
        0
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Extract content and prepare Rally API payload\nlet rssData = {};\nlet httpData = {};\n\ntry {\n  // Get RSS data from the dedup + URL extraction step\n  rssData = $('Dedup + url extraction').item.json || {};\n} catch (e) {\n  console.log('No RSS data accessible:', e.message);\n}\n\ntry {\n  // Get HTTP data from the content fetching step\n  httpData = $('Get Content').item.json || {};\n} catch (e) {\n  console.log('No HTTP data accessible:', e.message);\n}\n\n// Extract HTML content\nconst htmlContent = httpData.data || httpData.body || '';\n\n// Extract RSS metadata\nconst title = rssData.title || '';\nconst snippet = rssData.contentSnippet || rssData.content || rssData.summary || '';\nconst articleUrl = rssData.cleanUrl || rssData.link || '';\n\n// Extract article content from HTML\nlet extractedContent = '';\nif (htmlContent) {\n  extractedContent = htmlContent\n    .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n    .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n    .replace(/<[^>]*>/g, ' ')\n    .replace(/&nbsp;/g, ' ')\n    .replace(/&amp;/g, '&')\n    .replace(/&lt;/g, '<')\n    .replace(/&gt;/g, '>')\n    .replace(/&quot;/g, '\"')\n    .replace(/\\s+/g, ' ')\n    .trim();\n\n  // Limit length\n  if (extractedContent.length > 3000) {\n    extractedContent = extractedContent.substring(0, 3000) + '...';\n  }\n}\n\n// Use extracted content if substantial, otherwise RSS snippet\nconst useExtracted = extractedContent.length > 200;\nconst fullContent = useExtracted ? extractedContent : snippet;\nconst memoryContent = [`You've just read this content: ${fullContent}`];\n\n// Rally API payload\nconst rallyPayload = {\n  smart: false,\n  provider: \"openai\",\n  query: \"After reading that content, how has your interest in spending money on synthetic research changed? A) Much more interested, B) Somewhat more interested, C) No change, D) Somewhat less interested, E) Much less interested\",\n  audience_id: \"r8eb276513d8241\",\n  voting_mode: true,\n  mode: \"fast\",\n  manual_memories: memoryContent,\n};\n\nreturn {\n  title: title,\n  content: snippet,\n  url: articleUrl,\n  fullContent: fullContent,\n  rallyPayload: rallyPayload,\n  extractedLength: extractedContent.length,\n  usedExtracted: useExtracted,\n  debug: {\n    hasTitle: !!title,\n    hasUrl: !!articleUrl,\n    htmlLength: htmlContent.length,\n    rssDataFound: Object.keys(rssData).length > 0,\n    httpDataFound: Object.keys(httpData).length > 0\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "8f134637-f907-4be4-bcf0-0e497b7a711e",
      "name": "HTTP Request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        840,
        0
      ],
      "parameters": {
        "url": "ENTER RALLY API END POINT HERE",
        "method": "POST",
        "options": {},
        "jsonBody": "={{$json.rallyPayload}}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "eb63694e-b291-4d24-a1ee-c87a5a4304ce",
      "name": "Gmail",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1840,
        0
      ],
      "parameters": {
        "sendTo": "ENTER YOUR EMAIL HERE",
        "message": "=<div style=\"font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;\">\n\n<!-- Header Section -->\n<div style=\"background-color: #f8f9fa; padding: 15px; border-radius: 8px; margin-bottom: 20px;\">\n  <h2 style=\"margin: 0; color: #333;\">\ud83d\udcf0 AskRally Alert</h2>\n</div>\n\n<!-- Article Information -->\n<div style=\"background-color: #ffffff; border: 1px solid #e9ecef; border-radius: 8px; padding: 20px; margin-bottom: 20px;\">\n  <h3 style=\"color: #495057; margin-top: 0; border-bottom: 2px solid #dee2e6; padding-bottom: 10px;\">\n    \ud83d\udcc4 Article Details\n  </h3>\n  \n  <p><strong>Title:</strong><br/>\n  {{ $('Code').item.json.title }}</p>\n  \n  <p><strong>URL:</strong><br/>\n  <a href=\"{{$node[\"Code\"].json[\"url\"]}}\" style=\"color: #007bff; text-decoration: none;\">\n    {{$node[\"Code\"].json[\"url\"]}}\n  </a></p>\n  \n  <p><strong>Content Snippet:</strong><br/>\n  <em style=\"color: #6c757d;\">{{ $('Dedup + url extraction').item.json.contentSnippet }}</em></p>\n</div>\n\n<!-- Full Content Section -->\n<div style=\"background-color: #f8f9fa; border: 1px solid #e9ecef; border-radius: 8px; padding: 20px; margin-bottom: 20px;\">\n  <h3 style=\"color: #495057; margin-top: 0; border-bottom: 2px solid #dee2e6; padding-bottom: 10px;\">\n    \ud83d\udcd6 Full Content\n  </h3>\n  <div style=\"background-color: #ffffff; padding: 15px; border-radius: 4px; border-left: 4px solid #007bff; font-size: 14px; line-height: 1.6;\">\n    {{$node[\"Code\"].json[\"fullContent\"]}}\n  </div>\n</div>\n\n<!-- Results Section -->\n<div style=\"background-color: #ffffff; border: 1px solid #e9ecef; border-radius: 8px; padding: 20px; margin-bottom: 20px;\">\n  <h3 style=\"color: #495057; margin-top: 0; border-bottom: 2px solid #dee2e6; padding-bottom: 10px;\">\n    \ud83d\udcca Rally Results\n  </h3>\n  \n  <div style=\"background-color: #f8f9fa; padding: 15px; border-radius: 4px; margin-bottom: 15px;\">\n    <p><strong>{{ $json.message }}</strong></p>\n    <p><strong>Total Voters:</strong> {{ $json.totalVoters }}</p>\n    <p><strong>Pro-Narrative:</strong> {{ $json.proNarrative }}% | \n       <strong>Contra-Narrative:</strong> {{ $json.contraNarrative }}% | \n       <strong>Neutral:</strong> {{ $json.neutral }}%</p>\n  </div>\n\n  <!-- Sample Responses -->\n  <div style=\"background-color: #ffffff; border: 1px solid #dee2e6; border-radius: 4px; padding: 15px;\">\n    <h4 style=\"margin-top: 0; color: #495057;\">\ud83d\udcac Sample Responses</h4>\n    \n    {{\n      (() => {\n        const sampleResponses = $json.sampleResponses || [];\n        const notificationType = $json.notificationType;\n        \n        if (sampleResponses.length === 0) {\n          return '<p style=\"color: #6c757d; font-style: italic;\">No sample responses available.</p>';\n        }\n        \n        let sectionTitle = \"\";\n        let sectionColor = \"\";\n        \n        if (notificationType === 'pro-narrative') {\n          sectionTitle = \"\ud83d\udfe2 Pro-Narrative Voices (A & B voters):\";\n          sectionColor = \"#d4edda\";\n        } else if (notificationType === 'contra-narrative') {\n          sectionTitle = \"\ud83d\udd34 Contra-Narrative Voices (D & E voters):\";\n          sectionColor = \"#f8d7da\";\n        } else {\n          sectionTitle = \"\u26aa Mixed Responses:\";\n          sectionColor = \"#fff3cd\";\n        }\n        \n        let html = `<div style=\"background-color: ${sectionColor}; padding: 10px; border-radius: 4px; margin-bottom: 10px;\">\n          <strong>${sectionTitle}</strong>\n        </div>`;\n        \n        sampleResponses.forEach((response, index) => {\n          const optionLabel = {\n            'A': 'Much more interested',\n            'B': 'Somewhat more interested', \n            'C': 'No change',\n            'D': 'Somewhat less interested',\n            'E': 'Much less interested'\n          }[response.option] || response.option;\n          \n          html += `\n          <div style=\"border-left: 3px solid #007bff; padding: 10px; margin: 10px 0; background-color: #f8f9fa;\">\n            <p style=\"margin: 0 0 5px 0;\"><strong>Voter ${index + 1} (${response.option} - ${optionLabel}):</strong></p>\n            <p style=\"margin: 0; font-size: 14px; line-height: 1.4; color: #495057;\">\"${response.thinking}\"</p>\n          </div>`;\n        });\n        \n        return html;\n      })()\n    }}\n  </div>\n</div>\n\n<!-- Footer -->\n<div style=\"background-color: #f8f9fa; padding: 15px; border-radius: 8px; text-align: center; font-size: 12px; color: #6c757d;\">\n  <p style=\"margin: 0;\">\n    Powered by <a href=\"https://askrally.com/?utm_source=api&utm_medium=email&utm_campaign=narrative-alerts-n8n\" style=\"color: #007bff; text-decoration: none;\">AskRally.com</a>\n  </p>\n</div>\n\n</div>",
        "options": {},
        "subject": "={{ $json.message }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "390bb7a2-3e59-4824-af8b-be188c49284d",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -60,
        -120
      ],
      "parameters": {
        "width": 800,
        "height": 300,
        "content": "## Dedup + Extract content from RSS alert"
      },
      "typeVersion": 1
    },
    {
      "id": "3a1c9b9b-192c-499a-be4a-0edaf4b6bf47",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        760,
        -120
      ],
      "parameters": {
        "color": 5,
        "width": 720,
        "height": 300,
        "content": "## Ask Rally"
      },
      "typeVersion": 1
    },
    {
      "id": "c3d20aa4-83f8-4398-8537-9580450f8736",
      "name": "analyze simulation results",
      "type": "n8n-nodes-base.code",
      "position": [
        1160,
        0
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Process each Rally API response individually\nconst item = $input.item.json;\n\nlet voteCounts = { A: 0, B: 0, C: 0, D: 0, E: 0 };\nlet totalVoters = 0;\nlet responses = [];\n\ntry {\n  // The Rally API response is in 'responses' array\n  if (item.responses && Array.isArray(item.responses)) {\n    totalVoters = item.responses.length;\n    \n    for (const personaResponse of item.responses) {\n      try {\n        // Parse each persona's response\n        const data = JSON.parse(personaResponse.response);\n        const option = data.option;\n        \n        if (option && voteCounts.hasOwnProperty(option)) {\n          voteCounts[option]++;\n        }\n        \n        responses.push({\n          persona_id: personaResponse.persona_id,\n          option: option,\n          thinking: data.thinking || '',\n          thoughts: personaResponse.thoughts\n        });\n        \n      } catch (parseErr) {\n        console.log(`Error parsing persona ${personaResponse.persona_id} response:`, parseErr.message);\n      }\n    }\n  }\n} catch (err) {\n  console.log('Error processing Rally response:', err.message);\n}\n\n// Calculate percentages for this individual simulation\nconst percentages = {};\nfor (const opt of Object.keys(voteCounts)) {\n  percentages[opt] = totalVoters > 0\n    ? Math.round((voteCounts[opt] / totalVoters) * 100)\n    : 0;\n}\n\nconst proNarrative = percentages.A + percentages.B;\nconst contraNarrative = percentages.D + percentages.E;\nconst neutral = percentages.C;\n\n// Determine narrative type and select sample responses\nlet notificationType = '';\nlet sampleResponses = [];\n\nif (proNarrative >= 75) {\n  notificationType = 'pro-narrative';\n  // Get 5 random responses from A & B voters\n  const proResponses = responses.filter(r => ['A', 'B'].includes(r.option));\n  sampleResponses = proResponses.sort(() => 0.5 - Math.random()).slice(0, 5);\n} else if (contraNarrative >= 75) {\n  notificationType = 'contra-narrative';\n  // Get 5 random responses from D & E voters\n  const contraResponses = responses.filter(r => ['D', 'E'].includes(r.option));\n  sampleResponses = contraResponses.sort(() => 0.5 - Math.random()).slice(0, 5);\n} else {\n  notificationType = 'mixed';\n  // Get 5 random responses from all voters\n  sampleResponses = responses.sort(() => 0.5 - Math.random()).slice(0, 5);\n}\n\n// Return individual result for this RSS item\nreturn {\n  // Rally simulation metadata\n  session_id: item.session_id || '',\n  title: item.title || 'Interest in Synthetic Research Spending',\n  \n  // Rally simulation results\n  totalVoters,\n  voteCounts,\n  percentages,\n  proNarrative,\n  contraNarrative,\n  neutral,\n  responses,\n  summary: item.summary || '',\n  \n  // Pre-selected sample responses for email\n  sampleResponses,\n  notificationType,\n  \n  // Metadata\n  simulationId: item.session_id || `sim_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,\n  \n  // Debug info\n  debug: {\n    foundResponses: !!item.responses,\n    responseCount: item.responses ? item.responses.length : 0,\n    hasSessionId: !!item.session_id,\n    sampleResponsesCount: sampleResponses.length\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "a0620b7b-b18c-4568-aa67-6b6a180134a1",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1500,
        -120
      ],
      "parameters": {
        "color": 6,
        "width": 540,
        "height": 300,
        "content": "## Send an Alert"
      },
      "typeVersion": 1
    },
    {
      "id": "889d4d3d-3f57-44d8-82c9-87da3fcbd94e",
      "name": "alert trigger",
      "type": "n8n-nodes-base.code",
      "position": [
        1580,
        0
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Process each Rally API response individually\nconst item = $input.item.json;\n\n// The data is already processed - just extract what we need\nconst voteCounts = item.voteCounts || { A: 0, B: 0, C: 0, D: 0, E: 0 };\nconst totalVoters = item.totalVoters || 0;\nconst responses = item.responses || [];\n\n// Calculate percentages\nconst percentages = {};\nfor (const opt of Object.keys(voteCounts)) {\n  percentages[opt] = totalVoters > 0\n    ? Math.round((voteCounts[opt] / totalVoters) * 100)\n    : 0;\n}\n\n// Calculate narrative aggregates\nconst proNarrative = percentages.A + percentages.B;\nconst contraNarrative = percentages.D + percentages.E;\nconst neutral = percentages.C;\n\n// Determine notification type\nlet notificationType, message;\nif (proNarrative >= 75) {\n  notificationType = 'pro-narrative';\n  message = `\ud83d\udfe2 Pro-Narrative Detected: ${proNarrative}% voted A+B ` +\n            `(${percentages.A}% A, ${percentages.B}% B)`;\n} else if (contraNarrative >= 75) {\n  notificationType = 'contra-narrative';\n  message = `\ud83d\udd34 Contra-Narrative Detected: ${contraNarrative}% voted D+E ` +\n            `(${percentages.D}% D, ${percentages.E}% E)`;\n} else {\n  notificationType = 'mixed';\n  message = `\u26aa Mixed Response: Pro=${proNarrative}%, ` +\n            `Contra=${contraNarrative}%, Neutral=${neutral}%`;\n}\n\n// Select sample responses based on narrative type\nlet sampleResponses = [];\nif (responses.length > 0) {\n  let targetOptions = [];\n  \n  if (notificationType === 'pro-narrative') {\n    targetOptions = ['A', 'B'];\n  } else if (notificationType === 'contra-narrative') {\n    targetOptions = ['D', 'E'];\n  } else {\n    // For mixed, get a variety\n    targetOptions = ['A', 'B', 'C', 'D', 'E'];\n  }\n  \n  // Filter responses by target options and take up to 5\n  sampleResponses = responses\n    .filter(r => targetOptions.includes(r.option))\n    .slice(0, 5);\n}\n\n// Return the processed data\nreturn {\n  session_id: item.session_id,\n  title: item.title,\n  totalVoters,\n  voteCounts,\n  percentages,\n  proNarrative,\n  contraNarrative,\n  neutral,\n  responses,\n  summary: item.summary,\n  sampleResponses,\n  notificationType,\n  message,\n  simulationId: item.session_id,\n  shouldNotify: proNarrative >= 75 || contraNarrative >= 75,\n  debug: {\n    foundResponses: responses.length > 0,\n    responseCount: responses.length,\n    hasSessionId: !!item.session_id,\n    sampleResponsesCount: sampleResponses.length\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "8874bf82-4182-4846-ba83-77299a357323",
      "name": "RSS Read",
      "type": "n8n-nodes-base.rssFeedRead",
      "position": [
        0,
        0
      ],
      "parameters": {
        "url": "https://www.google.com/alerts/feeds/12254772602100657129/16808085471630190739",
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "63dcaba0-650d-42fb-b2c5-9ef9447e7f3a",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -260,
        0
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 2
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "a15c2ecb-a6bf-4546-b989-4edf480227f9",
      "name": "Dedup + url extraction",
      "type": "n8n-nodes-base.code",
      "position": [
        200,
        0
      ],
      "parameters": {
        "jsCode": "// Multi-item deduplication + URL extraction\nconst items = $input.all();\nconst hoursThreshold = 3; // Adjust as needed (24 hours = 1 day)\nconst now = new Date();\n\nconsole.log(`Processing ${items.length} RSS items for deduplication and URL extraction`);\n\nconst processedItems = items.filter((item, index) => {\n  try {\n    // First: Check age for deduplication\n    const pubDate = new Date(item.json.pubDate || item.json.isoDate);\n    const ageInHours = (now - pubDate) / (1000 * 60 * 60);\n    \n    console.log(`Item ${index}: \"${item.json.title}\" - Age: ${ageInHours.toFixed(1)} hours`);\n    \n    if (ageInHours <= hoursThreshold) {\n      console.log(`\u2705 Item ${index} passed filter (within ${hoursThreshold} hours)`);\n      return true;\n    } else {\n      console.log(`\u274c Item ${index} filtered out (older than ${hoursThreshold} hours)`);\n      return false;\n    }\n  } catch (error) {\n    console.log(`\u26a0\ufe0f Error processing item ${index}:`, error.message);\n    return false;\n  }\n}).map(item => {\n  // Second: Extract clean URL for items that passed the filter\n  const originalUrl = item.json.link;\n  let cleanUrl = originalUrl;\n  \n  // Handle Google redirect URLs (from Google Alerts)\n  if (originalUrl && originalUrl.includes('google.com/url')) {\n    const match = originalUrl.match(/[&?]url=([^&]+)/);\n    if (match) {\n      try {\n        cleanUrl = decodeURIComponent(match[1]);\n        console.log(`\ud83d\udd17 URL extracted: ${originalUrl} \u2192 ${cleanUrl}`);\n      } catch (error) {\n        console.log('URL decode error:', error);\n        cleanUrl = originalUrl;\n      }\n    }\n  }\n  \n  // Return item with clean URL added\n  return {\n    json: {\n      ...item.json,\n      cleanUrl: cleanUrl\n    }\n  };\n});\n\nconsole.log(`Final result: ${processedItems.length} items processed with clean URLs`);\n\nreturn processedItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "bb7083f6-b6bd-45cb-ba85-9c45457d3af2",
      "name": "Get Content",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        400,
        0
      ],
      "parameters": {
        "url": "={{ \n  (() => {\n    const url = $json.link;\n    if (url.includes('google.com/url')) {\n      const match = url.match(/[&?]url=([^&]+)/);\n      return match ? decodeURIComponent(match[1]) : url;\n    }\n    return url;\n  })() \n}}",
        "options": {}
      },
      "typeVersion": 4.2
    }
  ],
  "active": true,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "78058eb2-dfaa-44bd-8a20-0906b547d699",
  "connections": {
    "Code": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RSS Read": {
      "main": [
        [
          {
            "node": "Dedup + url extraction",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Content": {
      "main": [
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "analyze simulation results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "alert trigger": {
      "main": [
        [
          {
            "node": "Gmail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "RSS Read",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dedup + url extraction": {
      "main": [
        [
          {
            "node": "Get Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "analyze simulation results": {
      "main": [
        [
          {
            "node": "alert trigger",
            "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.

Pro

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

About this workflow

This workflow turns news monitoring into an early-warning demand engine. It continuously ingests Google Alert RSS feeds, extracts the full text of every article, and runs real-time purchase-intent modeling to predict which stories will sway your buyers—positively or negatively.…

Source: https://n8n.io/workflows/5234/ — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →

Related workflows

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

Email & Gmail

YOUR_ID 4. Uses gmail, googleDrive, googleSheets, httpRequest. Scheduled trigger; 53 nodes.

Gmail, Google Drive, Google Sheets +1
Email & Gmail

Instead of providing a routine check, it focuses on significant movements by: Sending a Slack alert only if a query crosses a defined movement threshold. Emailing a structured report with the Top 25 i

HTTP Request, Slack, Gmail
Email & Gmail

Looking for a way to track GitHub bounty issues automatically and get notified in real time? This GitHub Bounty Tracker workflow monitors repositories for issues labeled 💎 Bounty, logs them in Google

Google Sheets, HTTP Request, WhatsApp +1
Email & Gmail

This workflow automatically sends a beautifully designed HTML newsletter every Sunday at 8 AM, featuring products currently on sale from your Algolia-powered e-commerce store.

Google Sheets, HTTP Request, Gmail
Email & Gmail

This workflow automatically identifies your weekly bestselling product from your Algolia-powered e-commerce store and generates a cinematic product video using Google VEO 3.0 AI, helping marketing tea

HTTP Request, Gmail