This workflow corresponds to n8n.io template #15136 — we link there as the canonical source.
This workflow follows the Agent → Googlegemini recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "lRHRDtEnZtPurZ0P",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "N8N-Sports Digest Newsletter",
"tags": [
{
"id": "DrJfO3FIj4VGV9fL",
"name": "Newsletter",
"createdAt": "2025-06-14T22:02:24.176Z",
"updatedAt": "2025-06-14T22:02:24.176Z"
},
{
"id": "p7d66niWAG5ijZY7",
"name": "AI",
"createdAt": "2025-10-19T20:35:12.051Z",
"updatedAt": "2026-03-22T18:22:29.537Z"
},
{
"id": "hP3XwSnYlr76mfgu",
"name": "Sports",
"createdAt": "2026-04-18T05:49:56.619Z",
"updatedAt": "2026-04-18T05:49:56.619Z"
}
],
"nodes": [
{
"id": "5e36beab-5978-4abd-94f5-f5e044496216",
"name": "Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
0
],
"parameters": {
"width": 580,
"height": 1176,
"content": "## \ud83c\udfc6 Sports Digest \u2014 AI-Curated Weekly Newsletter\n\nAutomatically aggregates sports news for a **configurable topic** (e.g., \"University of Florida Football\" or \"Atlanta Falcons\") from Reddit, Google News, Yahoo Sports, NCAA.com, and BBC Sport, then curates and delivers a branded HTML email newsletter.\n\n### How it works\n1. **Schedule Trigger** fires weekly on Friday at 9 AM.\n2. **Config \u2014 Topic & Recipients** \u2014 Set your `topic`, `subreddit`, and `recipient_email` in one node.\n3. **News Collection** \u2014 Five sources run in parallel: Reddit, Google News RSS, Yahoo Sports RSS, NCAA.com RSS, and BBC Sport RSS.\n4. **Normalization** \u2014 Each source is standardized into a uniform schema (title, date, link, source, score, description).\n5. **Article Selection** \u2014 All sources are merged, deduplicated, filtered to the last 60 days, and 7 articles are randomly selected (\u22651 per source where available).\n6. **AI Curation** \u2014 GPT-4o-mini summarizes each article with a headline, 2-sentence summary, and \"why it matters\" note. Output is structured JSON.\n7. **Image Generation** \u2014 Gemini 2.5 Flash generates 2 AI images (for the top 2 articles) in parallel, each uploaded to your WordPress media library.\n8. **HTML Assembly** \u2014 Images are attached to the first 2 articles; all articles are rendered into a responsive branded HTML email.\n9. **Delivery** \u2014 Newsletter is sent via Microsoft Outlook to the configured recipient email.\n\n### Setup\n- **Config node**: Set `topic`, `subreddit`, and `recipient_email` before activating.\n- **Reddit**: Configure Reddit OAuth2 credential.\n- **OpenAI**: Add OpenAI API credential \u2014 requires GPT-4o-mini access.\n- **Gemini**: Add Google PaLM API credential for Gemini 2.5 Flash image generation.\n- **WordPress**: Add HTTP Basic Auth credential for your WordPress site (for image upload).\n- **Outlook**: Add Microsoft Outlook OAuth2 credential for email delivery.\n\n### Customization\n- Change the schedule in the trigger node (daily, weekly, etc.).\n- Adjust the article count (default: 7) in the **Select Articles** node.\n- Swap the WordPress upload for any image hosting service.\n- Update the HTML template in **Build Newsletter HTML** to match your brand colors.\n\n### Community Nodes\n\u26a0\ufe0f This workflow uses `@n8n/n8n-nodes-langchain.googleGemini` for AI image generation. This is a **LangChain community node** \u2014 requires a **self-hosted** n8n instance.\n\nvisit https://iportgpt.com/n8n_assets/sportsdig.html for instructions"
},
"typeVersion": 1
},
{
"id": "7ff99a55-e0e6-487a-9ba1-b0d199c0080a",
"name": "Section \u2014 Config",
"type": "n8n-nodes-base.stickyNote",
"position": [
592,
0
],
"parameters": {
"color": 7,
"width": 352,
"height": 440,
"content": "## \u2699\ufe0f Configuration\nEdit the **Config \u2014 Topic & Recipients** Set node.\nAll downstream nodes read `topic`, `subreddit`, and `recipient_email` from this single source."
},
"typeVersion": 1
},
{
"id": "b1af7d49-50a2-4e28-b14b-2a5c440f3d3e",
"name": "Section \u2014 News Collection",
"type": "n8n-nodes-base.stickyNote",
"position": [
960,
0
],
"parameters": {
"color": 7,
"width": 220,
"height": 1112,
"content": "## \ud83d\udce5 News Collection\nFetches up to 25 posts from each of 5 parallel sources: Reddit, Google News RSS, Yahoo Sports RSS, NCAA.com RSS, and BBC Sport RSS."
},
"typeVersion": 1
},
{
"id": "6792ee90-3a66-44e2-8a12-f27baed61b3a",
"name": "Section \u2014 Normalize & Select",
"type": "n8n-nodes-base.stickyNote",
"position": [
1184,
0
],
"parameters": {
"color": 7,
"width": 584,
"height": 1112,
"content": "## \ud83d\udd04 Normalize & Select\nEach source is normalized to a uniform schema. All items are merged, deduplicated, filtered to 60 days, and 7 articles are randomly selected (\u22651 per source where available)."
},
"typeVersion": 1
},
{
"id": "96c99b81-c3da-40a9-b732-565cdbf42bf6",
"name": "Section \u2014 AI Generation",
"type": "n8n-nodes-base.stickyNote",
"position": [
1776,
0
],
"parameters": {
"color": 7,
"width": 1312,
"height": 840,
"content": "## \ud83e\udd16 AI Content Generation\nGPT-4o-mini summarizes selected articles into newsletter-ready JSON. Gemini 2.5 Flash generates **2 images** (top 2 articles only) in parallel and uploads them to WordPress."
},
"typeVersion": 1
},
{
"id": "39f035ee-3b27-4b81-b305-5a18dc15552b",
"name": "Section \u2014 Delivery",
"type": "n8n-nodes-base.stickyNote",
"position": [
3104,
0
],
"parameters": {
"color": 7,
"width": 800,
"height": 840,
"content": "## \ud83d\udce4 Delivery\nSends the assembled HTML newsletter via Microsoft Outlook to the `recipient_email` configured in the Config node. Replace with Gmail or SMTP as needed."
},
"typeVersion": 1
},
{
"id": "229b384a-8985-4964-9e86-4c7572ddf32c",
"name": "Warning \u2014 Community Node",
"type": "n8n-nodes-base.stickyNote",
"position": [
2288,
592
],
"parameters": {
"color": 3,
"width": 300,
"height": 148,
"content": "\u26a0\ufe0f **Community Node Required**\n`googleGemini` is a LangChain community node.\n**Self-hosted n8n only.**\nUpdate the model ID if `gemini-2.5-flash-image` is unavailable in your instance."
},
"typeVersion": 1
},
{
"id": "c5be9576-f160-44ca-9e06-e4993b64844d",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
640,
256
],
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"triggerAtDay": [
5
],
"triggerAtHour": 9
}
]
}
},
"typeVersion": 1.3
},
{
"id": "e7032b11-d87b-4b71-b0f6-8c8bbeb2ca7e",
"name": "Config \u2014 Topic & Recipients",
"type": "n8n-nodes-base.set",
"position": [
832,
256
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "config-topic",
"name": "topic",
"type": "string",
"value": "Your Sports Topic Here"
},
{
"id": "config-subreddit",
"name": "subreddit",
"type": "string",
"value": "your_subreddit"
},
{
"id": "config-recipient",
"name": "recipient_email",
"type": "string",
"value": "your@email.com"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "b72145e5-3f2a-4129-b27d-9f806bed3053",
"name": "Fetch \u2014 Reddit Posts",
"type": "n8n-nodes-base.reddit",
"position": [
1024,
256
],
"parameters": {
"limit": 25,
"filters": {
"category": "new"
},
"operation": "getAll",
"subreddit": "={{ $('Config \u2014 Topic & Recipients').first().json.subreddit }}"
},
"credentials": {
"redditOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "6fc7e10e-5a5d-480c-83c0-f34096b89ecb",
"name": "Fetch \u2014 Google News RSS",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
1024,
416
],
"parameters": {
"url": "={{ 'https://news.google.com/rss/search?q=' + encodeURIComponent($('Config \u2014 Topic & Recipients').first().json.topic) + '&hl=en-US&gl=US&ceid=US:en' }}",
"options": {}
},
"typeVersion": 1.2
},
{
"id": "3d8d1b51-edae-419c-8529-06379c426900",
"name": "Fetch \u2014 NCAA RSS",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
1024,
768
],
"parameters": {
"url": "https://www.ncaa.com/news/ncaa/d1/rss.xml",
"options": {}
},
"typeVersion": 1.2
},
{
"id": "1141aa5c-4815-4636-ad48-459c35a1e5f1",
"name": "Normalize \u2014 Reddit",
"type": "n8n-nodes-base.code",
"position": [
1280,
256
],
"parameters": {
"jsCode": "// Format Reddit posts to standard structure\nconst items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n const j = item.json;\n if (j.selftext === '[removed]' || j.removed_by_category) continue;\n const link = j.url || (j.permalink ? 'https://reddit.com' + j.permalink : '');\n if (!j.title || !link) continue;\n let date = '';\n if (j.created_utc) date = new Date(j.created_utc * 1000).toISOString().split('T')[0];\n else if (j.created) date = new Date(j.created * 1000).toISOString().split('T')[0];\n results.push({\n json: {\n title: j.title,\n date: date,\n link: link,\n source: 'Reddit',\n score: j.score || j.ups || 0,\n description: (j.selftext || '').substring(0, 300)\n }\n });\n}\n\nreturn results.length > 0 ? results : [{ json: { empty: true, source: 'Reddit' } }];"
},
"typeVersion": 2
},
{
"id": "6326982f-7525-4ca7-90e6-00e7e2db64e0",
"name": "Normalize \u2014 Google News",
"type": "n8n-nodes-base.code",
"position": [
1280,
416
],
"parameters": {
"jsCode": "// Format Google News RSS items to standard structure\nconst items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n const j = item.json;\n const link = j.link || j.url || j.guid || '';\n if (!j.title || !link) continue;\n let date = '';\n if (j.pubDate) date = new Date(j.pubDate).toISOString().split('T')[0];\n else if (j.isoDate) date = new Date(j.isoDate).toISOString().split('T')[0];\n let description = (j.description || j.content || j.contentSnippet || '')\n .replace(/<[^>]*>/g, '').substring(0, 300);\n results.push({\n json: { title: j.title, date: date, link: link, source: 'Google News', score: 0, description: description }\n });\n}\n\nreturn results.length > 0 ? results : [{ json: { empty: true, source: 'Google News' } }];"
},
"typeVersion": 2
},
{
"id": "5cc2e4de-53a5-4d16-b99d-018d084fc56d",
"name": "Normalize \u2014 NCAA",
"type": "n8n-nodes-base.code",
"position": [
1280,
768
],
"parameters": {
"jsCode": "// Normalize NCAA.com RSS feed\nconst items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n const j = item.json;\n const link = j.link || j.url || j.guid || '';\n if (!j.title || !link) continue;\n let date = '';\n if (j.pubDate) date = new Date(j.pubDate).toISOString().split('T')[0];\n else if (j.isoDate) date = new Date(j.isoDate).toISOString().split('T')[0];\n let description = (j.description || j.contentSnippet || '').replace(/<[^>]*>/g, '').substring(0, 300);\n results.push({\n json: { title: j.title, date: date, link: link, source: 'NCAA.com', score: 0, description: description }\n });\n}\n\nreturn results.length > 0 ? results : [{ json: { empty: true, source: 'NCAA.com' } }];"
},
"typeVersion": 2
},
{
"id": "6c047b36-a3e6-410e-8684-4369c2520d2e",
"name": "Merge \u2014 All Sources",
"type": "n8n-nodes-base.merge",
"position": [
1520,
272
],
"parameters": {
"numberInputs": 5
},
"typeVersion": 3.2
},
{
"id": "5859c918-1e0d-49cb-a174-d62fc23dc472",
"name": "Select \u2014 7 Articles",
"type": "n8n-nodes-base.code",
"position": [
1664,
320
],
"parameters": {
"jsCode": "// Randomly select 7 articles with representation from all available sources\n// ONLY include articles from the last 60 days\nconst items = $input.all();\nconst topic = ($('Config \u2014 Topic & Recipients').first().json.topic || '').toLowerCase();\nconst seen = new Set();\n\nconst now = new Date();\nconst cutoffDate = new Date(now.getTime() - (60 * 24 * 60 * 60 * 1000));\nconst cutoffStr = cutoffDate.toISOString().split('T')[0];\n\nfunction shuffle(arr) {\n const a = [...arr];\n for (let i = a.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [a[i], a[j]] = [a[j], a[i]];\n }\n return a;\n}\n\nconst sources = ['Reddit', 'Google News', 'Yahoo Sports', 'NCAA.com', 'BBC Sport'];\nconst bySource = {};\nfor (const s of sources) bySource[s] = [];\n\nfor (const item of items) {\n const j = item.json;\n if (j.empty || !j.title || !j.link || seen.has(j.link)) continue;\n seen.add(j.link);\n if (j.date && j.date < cutoffStr) continue;\n const article = { title: j.title, date: j.date || '', link: j.link, source: j.source };\n if (bySource[j.source]) bySource[j.source].push(article);\n}\n\n// Take up to 1 guaranteed from each source, then fill remainder randomly\nconst selected = [];\nfor (const s of sources) {\n const shuffled = shuffle(bySource[s]);\n if (shuffled.length > 0) selected.push(shuffled[0]);\n bySource[s] = shuffled.slice(1);\n}\n\nconst pool = shuffle([].concat(...sources.map(s => bySource[s])));\nwhile (selected.length < 7 && pool.length > 0) selected.push(pool.shift());\n\nreturn [{ json: {\n articles: shuffle(selected),\n topic: topic,\n cutoffDate: cutoffStr,\n articlesSelected: selected.length\n} }];"
},
"typeVersion": 2
},
{
"id": "cf011e05-5e05-43d7-8a55-dd649ec26bfc",
"name": "LLM \u2014 GPT-4o-mini",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
1856,
480
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4o-mini",
"cachedResultName": "gpt-4o-mini"
},
"options": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "2eea4858-9a72-4044-8807-1a0d9e19b1e9",
"name": "AI \u2014 Summarize Articles",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
1856,
320
],
"parameters": {
"text": "=Summarize these {{ $json.articles.length }} articles for a sports newsletter about: {{ $('Config \u2014 Topic & Recipients').first().json.topic }}\n\n{{ JSON.stringify($json.articles) }}\n\nReturn JSON:\n{\"articles\":[{\"headline\":\"...\",\"summary\":\"2 sentences\",\"relevance\":\"Why it matters for fans\",\"source_url\":\"use exact link from input\",\"source_platform\":\"Reddit/Google News/Yahoo Sports/NCAA.com/BBC Sport\"}],\"newsletter_date\":\"{{ $now.format('MMMM d, yyyy') }}\",\"intro_summary\":\"2 sentence overview\"}",
"options": {
"systemMessage": "You are a sports journalist. Write concise summaries relevant to the specified topic. Return valid JSON only, no markdown."
},
"promptType": "define"
},
"typeVersion": 2.2
},
{
"id": "6d5b1429-af86-42ca-9ef6-bf534b02f421",
"name": "Parse AI \u2014 Build Image Prompts",
"type": "n8n-nodes-base.code",
"position": [
2128,
320
],
"parameters": {
"jsCode": "// Extract first 2 articles and create image prompts (only 2 images)\nconst aiResponse = $input.item.json.output || $input.item.json.data || '';\nlet data;\n\ntry {\n const match = aiResponse.match(/\\{[\\s\\S]*\\}/);\n data = match ? JSON.parse(match[0]) : null;\n if (!data || !data.articles) throw new Error('No articles found');\n} catch (e) {\n return [{ json: { error: 'Could not parse AI response', articles: [] } }];\n}\n\nconst articlesWithImages = data.articles.map((article, index) => ({\n ...article,\n imageIndex: index,\n hasImage: index < 2,\n imagePrompt: index < 2 ? 'Dynamic sports action illustration for: \"' + article.headline + '\". Style: vivid, athletic, high energy, photorealistic. Aspect ratio 16:9. Sports theme.' : null\n}));\n\nreturn [{\n json: {\n ...data,\n articles: articlesWithImages,\n articlesForImages: articlesWithImages.filter(a => a.hasImage),\n newsletter_date: data.newsletter_date,\n intro_summary: data.intro_summary\n }\n}];"
},
"typeVersion": 2
},
{
"id": "258aaa8d-be26-4af1-907b-c5a20a242cb2",
"name": "Image Prompt \u2014 Article 1",
"type": "n8n-nodes-base.code",
"position": [
2320,
240
],
"parameters": {
"jsCode": "// Extract first article's image prompt\nconst articles = $input.item.json.articles || [];\nconst firstArticle = articles.find(a => a.imageIndex === 0);\nif (!firstArticle || !firstArticle.imagePrompt) {\n return [{ json: { prompt: 'Dynamic sports action illustration. Style: vivid, athletic, photorealistic.', articleIndex: 0 } }];\n}\nreturn [{ json: { prompt: firstArticle.imagePrompt, articleIndex: 0 } }];"
},
"typeVersion": 2
},
{
"id": "6d904989-b20a-4561-b40b-95088447cab6",
"name": "Image Prompt \u2014 Article 2",
"type": "n8n-nodes-base.code",
"position": [
2320,
400
],
"parameters": {
"jsCode": "// Extract second article's image prompt\nconst articles = $input.item.json.articles || [];\nconst secondArticle = articles.find(a => a.imageIndex === 1);\nif (!secondArticle || !secondArticle.imagePrompt) {\n return [{ json: { prompt: 'Dynamic sports action illustration. Style: vivid, athletic, photorealistic.', articleIndex: 1 } }];\n}\nreturn [{ json: { prompt: secondArticle.imagePrompt, articleIndex: 1 } }];"
},
"typeVersion": 2
},
{
"id": "3e4eaaff-de3a-4047-b71c-c0a290fdb40a",
"name": "Gemini \u2014 Generate Image 1",
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"position": [
2480,
240
],
"parameters": {
"prompt": "={{ $json.prompt }}",
"modelId": {
"__rl": true,
"mode": "list",
"value": "models/gemini-2.5-flash-image",
"cachedResultName": "models/gemini-2.5-flash-image"
},
"options": {
"sampleCount": 1,
"binaryPropertyOutput": "image1"
},
"resource": "image"
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "b1f507c7-f5c6-4c32-9368-ef9f945810c9",
"name": "Gemini \u2014 Generate Image 2",
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"position": [
2480,
400
],
"parameters": {
"prompt": "={{ $json.prompt }}",
"modelId": {
"__rl": true,
"mode": "list",
"value": "models/gemini-2.5-flash-image",
"cachedResultName": "models/gemini-2.5-flash-image"
},
"options": {
"sampleCount": 1,
"binaryPropertyOutput": "image2"
},
"resource": "image"
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "7d00b595-76de-4337-b2bd-63a60f792829",
"name": "Merge \u2014 Generated Images",
"type": "n8n-nodes-base.merge",
"position": [
2640,
320
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2,
"alwaysOutputData": true
},
{
"id": "05fe2af7-c1ef-4bbc-aef9-2f2a94463f9d",
"name": "Prep \u2014 Images for Upload",
"type": "n8n-nodes-base.code",
"position": [
2800,
320
],
"parameters": {
"jsCode": "// Prepare the 2 generated images for WordPress upload (one item per image)\nconst merged = $input.first();\nconst binaries = merged.binary || {};\nconst keys = ['image1','image2'];\nconst out = [];\n\nfor (let i = 0; i < keys.length; i++) {\n const k = keys[i];\n const b = binaries[k];\n if (!b) continue;\n const fileName = 'sports-digest-img-' + (i+1) + '.png';\n out.push({\n json: { image_index: i, filename: fileName },\n binary: { data: { ...b, fileName } }\n });\n}\n\nreturn out.length ? out : [{ json: { image_index: -1, filename: 'sports-digest-img-0.png', no_images: true } }];"
},
"typeVersion": 2
},
{
"id": "5411d07f-0eef-4708-9901-6ac77cf950de",
"name": "WP \u2014 Upload Image",
"type": "n8n-nodes-base.httpRequest",
"position": [
2992,
320
],
"parameters": {
"url": "https://YOUR-WORDPRESS-SITE.com/wp-json/wp/v2/media",
"method": "POST",
"options": {},
"sendBody": true,
"contentType": "binaryData",
"sendHeaders": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Disposition",
"value": "=attachment; filename=\"{{ $json.filename }}\""
}
]
},
"inputDataFieldName": "data"
},
"credentials": {
"httpBasicAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "2e7e7364-f17b-497b-89eb-f0fccbd8d8d6",
"name": "Collect \u2014 Image URLs",
"type": "n8n-nodes-base.code",
"position": [
3168,
320
],
"parameters": {
"jsCode": "// Collect WordPress media URLs back into an imageMap keyed by article index\nconst items = $input.all();\nconst imageMap = {};\n\nfor (const item of items) {\n const j = item.json || {};\n const url = j.source_url || (j.guid && j.guid.rendered) || (j.media_details && j.media_details.sizes && j.media_details.sizes.full && j.media_details.sizes.full.source_url);\n const slug = j.slug || '';\n const title = (j.title && (j.title.rendered || j.title)) || '';\n const m = slug.match(/sports-digest-img-(\\d+)/i) || title.match(/sports-digest-img-(\\d+)/i);\n if (!url || !m) continue;\n const idx = parseInt(m[1], 10) - 1;\n if (Number.isFinite(idx) && idx >= 0 && idx <= 1) imageMap['image_' + idx] = url;\n}\n\nreturn [{ json: { imageMap } }];"
},
"typeVersion": 2
},
{
"id": "a6d66f8b-d31c-4f6f-a3ad-be4498b121a0",
"name": "Attach \u2014 Images to Articles",
"type": "n8n-nodes-base.code",
"position": [
3360,
320
],
"parameters": {
"jsCode": "// Merge article data with uploaded image URLs\nconst articleData = $('Parse AI \u2014 Build Image Prompts').first().json;\nconst imageData = $input.item.json.imageMap || {};\n\nconst articlesWithImages = (articleData.articles || []).map((article, index) => {\n const url = imageData['image_' + index];\n if (index < 2 && url) {\n return { ...article, imageUrl: url, imageWidth: '180px', imageHeight: 'auto' };\n }\n return article;\n});\n\nreturn [{\n json: {\n articles: articlesWithImages,\n newsletter_date: articleData.newsletter_date,\n intro_summary: articleData.intro_summary,\n topic: $('Config \u2014 Topic & Recipients').first().json.topic\n }\n}];"
},
"typeVersion": 2
},
{
"id": "35db228f-1c32-4be8-9c0f-9bc8be5c7b7b",
"name": "Build \u2014 Newsletter HTML",
"type": "n8n-nodes-base.code",
"position": [
3552,
320
],
"parameters": {
"jsCode": "// Build branded HTML newsletter\n// Update colors and branding below to match your organization\nconst data = $input.item.json;\nconst topic = data.topic || 'Sports Digest';\n\n// === BRANDING \u2014 customize these values ===\nconst brandPrimary = '#002657'; // Navy\nconst brandAccent = '#FA4616'; // Orange\nconst brandLight = '#c9c7c8'; // Silver\nconst logoUrl = 'YOUR-LOGO-URL-HERE';\nconst logoAlt = 'Your Organization';\nconst footerText = 'Curated by Your Organization \u00b7 Powered by AI';\n// ==========================================\n\nconst articlesHtml = (data.articles || []).map((a, idx) => {\n let imageHtml = '';\n if (a.imageUrl) {\n imageHtml = '<div style=\"margin-bottom:16px;overflow:hidden;border-radius:8px;\"><img src=\"' + a.imageUrl + '\" alt=\"' + (a.headline || '') + '\" style=\"width:100%;height:160px;object-fit:cover;object-position:center;display:block;\"></div>';\n }\n const borderColor = idx === 0 ? brandAccent : brandPrimary;\n return '<tr><td style=\"padding:20px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;border-left:4px solid ' + borderColor + ';\">' +\n imageHtml +\n '<h3 style=\"margin:0 0 12px;color:' + brandPrimary + ';font-size:17px;line-height:1.4;\">' + (a.headline || '') + '</h3>' +\n '<p style=\"margin:0 0 12px;color:#374151;font-size:14px;line-height:1.6;\">' + (a.summary || '') + '</p>' +\n '<p style=\"margin:0 0 12px;color:#6b7280;font-size:13px;font-style:italic;\"><b>Why it matters:</b> ' + (a.relevance || '') + '</p>' +\n '<p style=\"margin:0;font-size:12px;\"><a href=\"' + (a.source_url || '#') + '\" style=\"color:' + brandAccent + ';text-decoration:none;font-weight:600;\">Read full article \u2192</a> <span style=\"color:#9ca3af;\">[' + (a.source_platform || 'Source') + ']</span></p>' +\n '</td></tr><tr><td style=\"height:16px;\"></td></tr>';\n}).join('');\n\nconst html = '<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>' + topic + ' Digest</title></head>' +\n '<body style=\"font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Arial,sans-serif;padding:20px;background:#f5f5f5;margin:0;\">' +\n '<div style=\"max-width:680px;margin:0 auto;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 4px 12px rgba(0,0,0,0.15);\">' +\n '<div style=\"background:' + brandPrimary + ';padding:28px 24px 20px;text-align:center;\">' +\n '<h1 style=\"margin:0;color:#ffffff;font-size:24px;letter-spacing:1px;text-transform:uppercase;font-weight:700;\">' + topic + ' Digest</h1>' +\n '<p style=\"margin:6px 0 0;color:' + brandLight + ';font-size:13px;letter-spacing:0.5px;\">' + (data.newsletter_date || '') + '</p>' +\n '</div>' +\n '<div style=\"padding:20px 24px;background:' + brandAccent + ';border-bottom:3px solid ' + brandPrimary + ';\">' +\n '<p style=\"margin:0;color:#ffffff;font-size:15px;line-height:1.6;\">' + (data.intro_summary || '') + '</p>' +\n '</div>' +\n '<div style=\"padding:24px;\">' +\n '<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"width:100%;border-collapse:collapse;\">' +\n articlesHtml +\n '</table></div>' +\n '<div style=\"padding:24px;background:' + brandPrimary + ';border-top:3px solid ' + brandAccent + ';text-align:center;\">' +\n '<p style=\"margin:4px 0 0;color:' + brandLight + ';font-size:12px;font-weight:600;\">' + footerText + '</p>' +\n '</div>' +\n '</div></body></html>';\n\nreturn [{ json: { newsletter_html: html, newsletter_date: data.newsletter_date, topic: topic, article_count: (data.articles || []).length } }];"
},
"typeVersion": 2
},
{
"id": "cdb76e88-2609-4088-8e99-98e00662cb3d",
"name": "Outlook \u2014 Send Newsletter",
"type": "n8n-nodes-base.microsoftOutlook",
"position": [
3760,
320
],
"parameters": {
"subject": "={{ $('Config \u2014 Topic & Recipients').first().json.topic }} Digest \u2014 {{ $now.toFormat('MMMM d, yyyy') }}",
"bodyContent": "={{ $('Build \u2014 Newsletter HTML').first().json.newsletter_html }}",
"toRecipients": "={{ $('Config \u2014 Topic & Recipients').first().json.recipient_email }}",
"additionalFields": {
"bodyContentType": "html"
}
},
"credentials": {
"microsoftOutlookOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "b012cace-ab94-4942-8837-ad5459627551",
"name": "Parse \u2014 Yahoo Sports XML",
"type": "n8n-nodes-base.code",
"position": [
1280,
576
],
"parameters": {
"jsCode": "const xml = $input.first().json.data;\n\nconst items = [];\nconst itemRegex = /<item[^>]*>([\\s\\S]*?)<\\/item>/gi;\nconst titleRegex = /<title><!\\[CDATA\\[([\\s\\S]*?)\\]\\]><\\/title>|<title>([\\s\\S]*?)<\\/title>/i;\nconst linkRegex = /<link>([\\s\\S]*?)<\\/link>/i;\nconst descRegex = /<description><!\\[CDATA\\[([\\s\\S]*?)\\]\\]><\\/description>|<description>([\\s\\S]*?)<\\/description>/i;\nconst pubDateRegex = /<pubDate>([\\s\\S]*?)<\\/pubDate>/i;\nconst guidRegex = /<guid[^>]*>([\\s\\S]*?)<\\/guid>/i;\nconst categoryRegex = /<category><!\\[CDATA\\[([\\s\\S]*?)\\]\\]><\\/category>|<category>([\\s\\S]*?)<\\/category>/gi;\n\nlet match;\nwhile ((match = itemRegex.exec(xml)) !== null) {\n const itemXml = match[1];\n const titleMatch = titleRegex.exec(itemXml);\n const linkMatch = linkRegex.exec(itemXml);\n const descMatch = descRegex.exec(itemXml);\n const pubDateMatch = pubDateRegex.exec(itemXml);\n const guidMatch = guidRegex.exec(itemXml);\n const categories = [];\n let catMatch;\n while ((catMatch = categoryRegex.exec(itemXml)) !== null) {\n categories.push(catMatch[1] || catMatch[2] || '');\n }\n items.push({\n json: {\n title: (titleMatch && (titleMatch[1] || titleMatch[2]) || '').trim(),\n link: (linkMatch && linkMatch[1] || '').trim(),\n description: (descMatch && (descMatch[1] || descMatch[2]) || '').replace(/<[^>]+>/g, '').trim(),\n pubDate: (pubDateMatch && pubDateMatch[1] || '').trim(),\n guid: (guidMatch && guidMatch[1] || '').trim(),\n categories: categories,\n source: 'Yahoo Sports'\n }\n });\n}\n\nif (items.length === 0) {\n return [{ json: { error: 'No items parsed', rawSnippet: xml.substring(0, 500) } }];\n}\nreturn items;"
},
"typeVersion": 2
},
{
"id": "6cf88b96-1f9f-440d-ae9a-70770677a522",
"name": "Fetch \u2014 Yahoo Sports RSS",
"type": "n8n-nodes-base.httpRequest",
"position": [
1024,
576
],
"parameters": {
"url": "https://sports.yahoo.com/rss/",
"options": {
"response": {
"response": {
"responseFormat": "text"
}
}
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "User-Agent",
"value": "Mozilla/5.0 (compatible; n8n-bot/1.0)"
},
{
"name": "Accept",
"value": "application/rss+xml, application/xml, text/xml"
}
]
}
},
"typeVersion": 4.4
},
{
"id": "98769e5f-b7c9-415b-b4bd-b68cbafe370b",
"name": "Parse \u2014 BBC Sport XML",
"type": "n8n-nodes-base.code",
"position": [
1280,
928
],
"parameters": {
"jsCode": "const xml = $input.first().json.data;\n\nconst items = [];\nconst itemRegex = /<item[^>]*>([\\s\\S]*?)<\\/item>/gi;\nconst titleRegex = /<title><!\\[CDATA\\[([\\s\\S]*?)\\]\\]><\\/title>|<title>([\\s\\S]*?)<\\/title>/i;\nconst linkRegex = /<link>([\\s\\S]*?)<\\/link>/i;\nconst descRegex = /<description><!\\[CDATA\\[([\\s\\S]*?)\\]\\]><\\/description>|<description>([\\s\\S]*?)<\\/description>/i;\nconst pubDateRegex = /<pubDate>([\\s\\S]*?)<\\/pubDate>/i;\nconst guidRegex = /<guid[^>]*>([\\s\\S]*?)<\\/guid>/i;\nconst categoryRegex = /<category><!\\[CDATA\\[([\\s\\S]*?)\\]\\]><\\/category>|<category>([\\s\\S]*?)<\\/category>/gi;\n\nlet match;\nwhile ((match = itemRegex.exec(xml)) !== null) {\n const itemXml = match[1];\n const titleMatch = titleRegex.exec(itemXml);\n const linkMatch = linkRegex.exec(itemXml);\n const descMatch = descRegex.exec(itemXml);\n const pubDateMatch = pubDateRegex.exec(itemXml);\n const guidMatch = guidRegex.exec(itemXml);\n const categories = [];\n let catMatch;\n while ((catMatch = categoryRegex.exec(itemXml)) !== null) {\n categories.push(catMatch[1] || catMatch[2] || '');\n }\n items.push({\n json: {\n title: (titleMatch && (titleMatch[1] || titleMatch[2]) || '').trim(),\n link: (linkMatch && linkMatch[1] || '').trim(),\n description: (descMatch && (descMatch[1] || descMatch[2]) || '').replace(/<[^>]+>/g, '').trim(),\n pubDate: (pubDateMatch && pubDateMatch[1] || '').trim(),\n guid: (guidMatch && guidMatch[1] || '').trim(),\n categories: categories,\n source: 'BBC Sport'\n }\n });\n}\n\nif (items.length === 0) {\n return [{ json: { error: 'No items parsed', rawSnippet: xml.substring(0, 500) } }];\n}\nreturn items;"
},
"typeVersion": 2
},
{
"id": "8c04763b-a87e-4bce-b483-a04a59e682a6",
"name": "Fetch \u2014 BBC Sport RSS",
"type": "n8n-nodes-base.httpRequest",
"position": [
1024,
928
],
"parameters": {
"url": "https://feeds.bbci.co.uk/sport/rss.xml",
"options": {
"response": {
"response": {
"responseFormat": "text"
}
}
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "User-Agent",
"value": "Mozilla/5.0 (compatible; n8n-bot/1.0)"
},
{
"name": "Accept",
"value": "application/rss+xml, application/xml, text/xml"
}
]
}
},
"typeVersion": 4.4
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "5004c787-115a-48ec-9710-21a0c1be2a3a",
"connections": {
"Schedule Trigger": {
"main": [
[
{
"node": "Config \u2014 Topic & Recipients",
"type": "main",
"index": 0
}
]
]
},
"Fetch \u2014 NCAA RSS": {
"main": [
[
{
"node": "Normalize \u2014 NCAA",
"type": "main",
"index": 0
}
]
]
},
"Normalize \u2014 NCAA": {
"main": [
[
{
"node": "Merge \u2014 All Sources",
"type": "main",
"index": 3
}
]
]
},
"LLM \u2014 GPT-4o-mini": {
"ai_languageModel": [
[
{
"node": "AI \u2014 Summarize Articles",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"WP \u2014 Upload Image": {
"main": [
[
{
"node": "Collect \u2014 Image URLs",
"type": "main",
"index": 0
}
]
]
},
"Normalize \u2014 Reddit": {
"main": [
[
{
"node": "Merge \u2014 All Sources",
"type": "main",
"index": 0
}
]
]
},
"Merge \u2014 All Sources": {
"main": [
[
{
"node": "Select \u2014 7 Articles",
"type": "main",
"index": 0
}
]
]
},
"Select \u2014 7 Articles": {
"main": [
[
{
"node": "AI \u2014 Summarize Articles",
"type": "main",
"index": 0
}
]
]
},
"Collect \u2014 Image URLs": {
"main": [
[
{
"node": "Attach \u2014 Images to Articles",
"type": "main",
"index": 0
}
]
]
},
"Fetch \u2014 Reddit Posts": {
"main": [
[
{
"node": "Normalize \u2014 Reddit",
"type": "main",
"index": 0
}
]
]
},
"Fetch \u2014 BBC Sport RSS": {
"main": [
[
{
"node": "Parse \u2014 BBC Sport XML",
"type": "main",
"index": 0
}
]
]
},
"Parse \u2014 BBC Sport XML": {
"main": [
[
{
"node": "Merge \u2014 All Sources",
"type": "main",
"index": 4
}
]
]
},
"AI \u2014 Summarize Articles": {
"main": [
[
{
"node": "Parse AI \u2014 Build Image Prompts",
"type": "main",
"index": 0
}
]
]
},
"Build \u2014 Newsletter HTML": {
"main": [
[
{
"node": "Outlook \u2014 Send Newsletter",
"type": "main",
"index": 0
}
]
]
},
"Fetch \u2014 Google News RSS": {
"main": [
[
{
"node": "Normalize \u2014 Google News",
"type": "main",
"index": 0
}
]
]
},
"Normalize \u2014 Google News": {
"main": [
[
{
"node": "Merge \u2014 All Sources",
"type": "main",
"index": 1
}
]
]
},
"Fetch \u2014 Yahoo Sports RSS": {
"main": [
[
{
"node": "Parse \u2014 Yahoo Sports XML",
"type": "main",
"index": 0
}
]
]
},
"Image Prompt \u2014 Article 1": {
"main": [
[
{
"node": "Gemini \u2014 Generate Image 1",
"type": "main",
"index": 0
}
]
]
},
"Image Prompt \u2014 Article 2": {
"main": [
[
{
"node": "Gemini \u2014 Generate Image 2",
"type": "main",
"index": 0
}
]
]
},
"Merge \u2014 Generated Images": {
"main": [
[
{
"node": "Prep \u2014 Images for Upload",
"type": "main",
"index": 0
}
]
]
},
"Parse \u2014 Yahoo Sports XML": {
"main": [
[
{
"node": "Merge \u2014 All Sources",
"type": "main",
"index": 2
}
]
]
},
"Prep \u2014 Images for Upload": {
"main": [
[
{
"node": "WP \u2014 Upload Image",
"type": "main",
"index": 0
}
]
]
},
"Gemini \u2014 Generate Image 1": {
"main": [
[
{
"node": "Merge \u2014 Generated Images",
"type": "main",
"index": 0
}
]
]
},
"Gemini \u2014 Generate Image 2": {
"main": [
[
{
"node": "Merge \u2014 Generated Images",
"type": "main",
"index": 1
}
]
]
},
"Attach \u2014 Images to Articles": {
"main": [
[
{
"node": "Build \u2014 Newsletter HTML",
"type": "main",
"index": 0
}
]
]
},
"Config \u2014 Topic & Recipients": {
"main": [
[
{
"node": "Fetch \u2014 Reddit Posts",
"type": "main",
"index": 0
},
{
"node": "Fetch \u2014 Google News RSS",
"type": "main",
"index": 0
},
{
"node": "Fetch \u2014 NCAA RSS",
"type": "main",
"index": 0
},
{
"node": "Fetch \u2014 Yahoo Sports RSS",
"type": "main",
"index": 0
},
{
"node": "Fetch \u2014 BBC Sport RSS",
"type": "main",
"index": 0
}
]
]
},
"Parse AI \u2014 Build Image Prompts": {
"main": [
[
{
"node": "Image Prompt \u2014 Article 1",
"type": "main",
"index": 0
},
{
"node": "Image Prompt \u2014 Article 2",
"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.
googlePalmApihttpBasicAuthmicrosoftOutlookOAuth2ApiopenAiApiredditOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Automatically aggregates sports news for a configurable topic (e.g., "University of Florida Football" or "Atlanta Falcons") from Reddit, Google News, Yahoo Sports, NCAA.com, and BBC Sport, then curates and delivers a branded HTML email newsletter. Schedule Trigger fires weekly…
Source: https://n8n.io/workflows/15136/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow automatically generates and publishes a full-length, SEO-optimized WordPress article on a weekly schedule. Topics are sourced from a SharePoint list. The AI agent writes the article body
We’ve released Version 4 of our AI Powered Blog Automation workflow. We heard your complains and made a complete redesign built for serious content creators.
Automate Microsoft Teams Meeting Analysis with GPT-4.1, Outlook & Mem.ai Watch the YouTube video to get started Follow along with the blog post
Most blogs publish words. This system publishes experiences.
kisisel asistan. Uses toolWorkflow, toolHttpRequest, toolCalculator, toolThink. Scheduled trigger; 43 nodes.