AutomationFlowsAI & RAG › Send a Weekly Ai‑curated RSS Digest Email with Openai and Gmail

Send a Weekly Ai‑curated RSS Digest Email with Openai and Gmail

ByJulian Abt @automationsmanufaktur on n8n.io

This workflow runs every week, reads articles from a configurable list of RSS feeds, deduplicates and ranks recent items, uses OpenAI to curate them into a themed HTML digest, and sends the finished newsletter to your inbox via Gmail. Runs weekly on Monday at 08:00. Generates…

Cron / scheduled trigger★★★★☆ complexityAI-powered12 nodesRSS Feed ReadOpenAIGmail
AI & RAG Trigger: Cron / scheduled Nodes: 12 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Gmail → 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": "AI Content Digest \u2192 Email",
  "nodes": [
    {
      "id": "sticky-01-overview",
      "name": "Overview & Setup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -780,
        100
      ],
      "parameters": {
        "color": 3,
        "width": 560,
        "height": 560,
        "content": "## \ud83d\udcf0 AI Content Digest \u2192 Email\n\nTurns your own list of RSS feeds into one clean, AI-curated HTML briefing in your inbox \u2014 on a schedule. Zero manual reading.\n\n### \ud83d\udc64 Who's it for\nFounders, marketers and researchers who want a personalised weekly briefing instead of doomscrolling feeds.\n\n### \u2699\ufe0f How it works\n1. **Schedule** fires weekly (Mon 08:00).\n2. **RSS Read** pulls every feed; articles are merged, deduped, filtered to the last 7 days and ranked.\n3. **OpenAI** groups them into 2\u20134 themes and writes a one-sentence summary per item as ready-to-send HTML.\n4. **Gmail** sends the branded email.\n\n### \ud83d\udd27 Set up (~5 min)\n- Add an **OpenAI** credential (default `gpt-4o-mini`) and a **Gmail OAuth2** credential.\n- Put your feeds in **Define Feed Sources** and the recipient in **Build Email HTML**.\n- Tune `LOOKBACK_DAYS` / `MAX_ARTICLES` to taste."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-01-s1",
      "name": "Section 1 \u00b7 Sources",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -150,
        100
      ],
      "parameters": {
        "color": 7,
        "width": 520,
        "height": 360,
        "content": "### 1. Sources\nRuns weekly, then emits one item per feed. Edit the feed list in **Define Feed Sources**."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-01-s2",
      "name": "Section 2 \u00b7 Fetch & Rank",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        510,
        100
      ],
      "parameters": {
        "color": 7,
        "width": 540,
        "height": 360,
        "content": "### 2. Fetch & Rank\nRSS Read runs once per feed and fans out into articles, which are merged, filtered to the last 7 days and ranked newest-first."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-01-s3",
      "name": "Section 3 \u00b7 AI Curation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1190,
        100
      ],
      "parameters": {
        "color": 7,
        "width": 200,
        "height": 360,
        "content": "### 3. AI Curation\nOpenAI acts as an editor: groups articles into 2\u20134 themes and writes a one-sentence summary per item as HTML."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-01-s4",
      "name": "Section 4 \u00b7 Send",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1530,
        100
      ],
      "parameters": {
        "color": 7,
        "width": 540,
        "height": 360,
        "content": "### 4. Send\nThe AI HTML is wrapped in a branded email shell and sent via Gmail. Set the recipient in **Build Email HTML**."
      },
      "typeVersion": 1
    },
    {
      "id": "schedule-trigger",
      "name": "Weekly Monday 08:00",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -100,
        220
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 8,
              "weeksInterval": 1,
              "triggerAtMinute": 0
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "define-feeds",
      "name": "Define Feed Sources",
      "type": "n8n-nodes-base.code",
      "position": [
        220,
        220
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// EDIT ME: your RSS feed sources.\n// Add one entry per feed you want in your digest.\n// `source` is a friendly label shown in the email; `url` is the feed.\n// The next node (RSS Read) runs ONCE PER ITEM returned here.\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst FEEDS = [\n  { source: 'TechCrunch',     url: 'https://techcrunch.com/feed/' },\n  { source: 'The Verge',      url: 'https://www.theverge.com/rss/index.xml' },\n  { source: 'Hacker News',    url: 'https://hnrss.org/frontpage' },\n];\n\nreturn FEEDS.map((feed) => ({ json: feed }));"
      },
      "typeVersion": 2
    },
    {
      "id": "rss-read",
      "name": "Read RSS Feed",
      "type": "n8n-nodes-base.rssFeedRead",
      "onError": "continueRegularOutput",
      "position": [
        560,
        220
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "merge-rank",
      "name": "Merge, Filter & Rank Articles",
      "type": "n8n-nodes-base.code",
      "position": [
        900,
        220
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// Combine every article from every feed, keep only recent ones,\n// rank newest-first, take the top N, and build a compact text block\n// that the AI can summarize cheaply.\n\nconst LOOKBACK_DAYS = 7;   // only keep articles newer than this\nconst MAX_ARTICLES  = 15;  // cap to control token cost\n\nconst cutoff = Date.now() - LOOKBACK_DAYS * 24 * 60 * 60 * 1000;\n\nconst stripHtml = (s) => String(s || '').replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim();\n\nconst seen = new Set();\nconst articles = [];\n\nfor (const item of $input.all()) {\n  const a = item.json || {};\n  const title = stripHtml(a.title);\n  const link = a.link || a.guid || '';\n  if (!title || !link) continue;\n\n  // Dedupe by link, then by lowercased title\n  const key = String(link).split('?')[0].toLowerCase();\n  const titleKey = title.toLowerCase();\n  if (seen.has(key) || seen.has(titleKey)) continue;\n  seen.add(key);\n  seen.add(titleKey);\n\n  // Parse publish date; if missing/invalid, keep the article (treat as 'now')\n  const raw = a.isoDate || a.pubDate || a.published || a.date;\n  const ts = raw ? Date.parse(raw) : NaN;\n  const ms = Number.isNaN(ts) ? Date.now() : ts;\n  if (!Number.isNaN(ts) && ms < cutoff) continue;\n\n  const source = a.source || a.creator || (a.meta && a.meta.title) || 'Source';\n  const snippet = stripHtml(a.contentSnippet || a.content || a.summary || a.description).slice(0, 220);\n\n  articles.push({ title, link, source, snippet, ms });\n}\n\narticles.sort((x, y) => y.ms - x.ms);\nconst top = articles.slice(0, MAX_ARTICLES);\n\nconst articlesText = top\n  .map((a, i) => `${i + 1}. ${a.title} \u2014 ${a.source} \u2014 ${a.link}\\n   ${a.snippet}`)\n  .join('\\n\\n');\n\nreturn [{\n  json: {\n    articleCount: top.length,\n    generatedFor: new Date().toISOString().slice(0, 10),\n    articlesText: articlesText || 'No new articles were found in the lookback window.',\n  },\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "openai-curate",
      "name": "Curate & Summarize Digest",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        1240,
        220
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "gpt-4o-mini"
        },
        "options": {
          "maxTokens": 1800,
          "temperature": 0.4,
          "instructions": "You are an expert newsletter editor. From the provided list of articles, produce a concise weekly digest as VALID HTML (a fragment, no <html>/<body> wrapper).\n\nRequirements:\n- Start with a single <p> one-line intro summarizing the week's theme.\n- Group the items into 2\u20134 thematic sections. Each section starts with an <h2> heading.\n- Under each heading, output a <ul> where every <li> is: <a href=\"LINK\">Title</a> \u2014 a one-sentence summary in your own words, then the source in parentheses.\n- Drop near-duplicate stories; keep the strongest version.\n- Do NOT invent links or facts; only use the provided articles.\n- Output ONLY the HTML fragment, with no markdown code fences and no commentary."
        },
        "simplify": true,
        "responses": {
          "values": [
            {
              "role": "user",
              "type": "text",
              "content": "=Here are this week's candidate articles ({{ $json.articleCount }} items). Curate them into the HTML digest as instructed.\n\n--- ARTICLES ---\n{{ $json.articlesText }}\n--- END ARTICLES ---"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "build-email",
      "name": "Build Email HTML",
      "type": "n8n-nodes-base.set",
      "position": [
        1580,
        220
      ],
      "parameters": {
        "mode": "manual",
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "assign-recipient",
              "name": "recipient",
              "type": "string",
              "value": "you@example.com"
            },
            {
              "id": "assign-digestDate",
              "name": "digestDate",
              "type": "string",
              "value": "={{ $now.format('cccc, dd LLLL yyyy') }}"
            },
            {
              "id": "assign-html",
              "name": "emailHtml",
              "type": "string",
              "value": "=<!DOCTYPE html>\n<html>\n<body style=\"margin:0;padding:0;background:#f4f5f7;font-family:Arial,Helvetica,sans-serif;color:#1f2933;\">\n  <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#f4f5f7;padding:24px 0;\">\n    <tr><td align=\"center\">\n      <table role=\"presentation\" width=\"600\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#ffffff;border-radius:12px;overflow:hidden;\">\n        <tr><td style=\"background:#0f172a;padding:24px 32px;\">\n          <div style=\"color:#ffffff;font-size:20px;font-weight:bold;\">\ud83d\udcf0 Your Weekly Content Digest</div>\n          <div style=\"color:#94a3b8;font-size:13px;margin-top:4px;\">{{ $json.digestDate }}</div>\n        </td></tr>\n        <tr><td style=\"padding:28px 32px;font-size:15px;line-height:1.6;\">\n          {{ $('Curate & Summarize Digest').item.json.content }}\n        </td></tr>\n        <tr><td style=\"padding:18px 32px;border-top:1px solid #e5e7eb;color:#94a3b8;font-size:12px;line-height:1.5;\">\n          Curated automatically with n8n + OpenAI. To change the feeds or schedule, edit your workflow.\n        </td></tr>\n      </table>\n    </td></tr>\n  </table>\n</body>\n</html>"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "send-email",
      "name": "Send Digest Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1920,
        220
      ],
      "parameters": {
        "sendTo": "={{ $json.recipient }}",
        "message": "={{ $json.emailHtml }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "=\ud83d\udcf0 Your Weekly Content Digest \u2014 {{ $json.digestDate }}",
        "resource": "message",
        "emailType": "html",
        "operation": "send"
      },
      "typeVersion": 2.2
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Read RSS Feed": {
      "main": [
        [
          {
            "node": "Merge, Filter & Rank Articles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Email HTML": {
      "main": [
        [
          {
            "node": "Send Digest Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Define Feed Sources": {
      "main": [
        [
          {
            "node": "Read RSS Feed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Weekly Monday 08:00": {
      "main": [
        [
          {
            "node": "Define Feed Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Curate & Summarize Digest": {
      "main": [
        [
          {
            "node": "Build Email HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge, Filter & Rank Articles": {
      "main": [
        [
          {
            "node": "Curate & Summarize Digest",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

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

About this workflow

This workflow runs every week, reads articles from a configurable list of RSS feeds, deduplicates and ranks recent items, uses OpenAI to curate them into a themed HTML digest, and sends the finished newsletter to your inbox via Gmail. Runs weekly on Monday at 08:00. Generates…

Source: https://n8n.io/workflows/16037/ — 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

Workflow runs at 08:00 (UK), AI stories from TechCrunch, The Verge, and the OpenAI Blog via RSS, AI to select and editorially shape one signal into a post and image prompt, generates and QA-checks a p

RSS Feed Read, OpenAI, Airtable +2
AI & RAG

This workflow runs on a daily schedule, pulls articles from one or more RSS feeds, filters out items already stored in Microsoft Excel, uses OpenAI to generate an HTML newsletter from the newest links

Gmail, RSS Feed Read, Microsoft Excel +2
AI & RAG

What this workflow does Pulls free security/tech headlines from multiple RSS feeds (e.g., CISA, BleepingComputer, Krebs, SecurityWeek, Ars Technica, TechCrunch, Hacker News). De-duplicates stories, ke

RSS Feed Read, Gmail, OpenAI
AI & RAG

This automation fetches daily AI-related articles from trusted RSS feeds, summarizes them using OpenAI (GPT), and generates a ready-to-post LinkedIn update in your writing style. It then emails the po

RSS Feed Read, OpenAI, Gmail
AI & RAG

Pulls free business and economic headlines from multiple publicly available RSS feeds (e.g., Reuters, Wall Street Journal, Federal Reserve, St. Louis Fed, BNP Paribas, WTO). De-duplicates stories, kee

Gmail, OpenAI, RSS Feed Read