AutomationFlowsGeneral › Send Localized 9am RSS Coffee Newsletters with Rrule.net and Brevo

Send Localized 9am RSS Coffee Newsletters with Rrule.net and Brevo

Byrrule.net @rrulenet on n8n.io

Fetch an RSS feed, build a localized morning digest, and send it with Brevo at 09:00 local time in Paris, New York, Tokyo, and Perth from one target-aware rrule.net schedule. One rrule.net Schedule Trigger emits an execution at 09:00 in each configured target timezone. The…

Cron / scheduled trigger★★★★☆ complexity13 nodes@Rrulenet/N8N Nodes RrulenetRSS Feed ReadSendinblue
General Trigger: Cron / scheduled Nodes: 13 Complexity: ★★★★☆ Added:

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

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": "6c34e8d4-725c-4a74-909f-e2a1c3fe3421",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "name": "Send a coffee newsletter at 09:00 local time in every region",
  "tags": [],
  "nodes": [
    {
      "id": "2abf1afd-e8b8-4b84-93cd-68c4d5ea61e6",
      "name": "At 09:00 in each target timezone",
      "type": "@rrulenet/n8n-nodes-rrulenet.rruleNetScheduleTrigger",
      "position": [
        -920,
        -80
      ],
      "parameters": {
        "name": "Morning coffee newsletter",
        "input": "Send the morning coffee newsletter every day at 9am local time in Paris, New York, Tokyo, and Perth",
        "timezone": "UTC",
        "onDeactivate": "pause"
      },
      "typeVersion": 1
    },
    {
      "id": "d56ca913-fa14-4d76-a7ba-fbe797861d89",
      "name": "When clicking 'Execute workflow'",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -920,
        240
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "d4d03c2b-f38d-4c8d-8f35-d8ef05a0f4da",
      "name": "Use a sample Paris execution",
      "type": "n8n-nodes-base.set",
      "position": [
        -680,
        240
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "sample-schedule-id",
              "name": "schedule_id",
              "type": "string",
              "value": "manual-preview"
            },
            {
              "id": "sample-schedule-name",
              "name": "schedule_name",
              "type": "string",
              "value": "Morning coffee newsletter preview"
            },
            {
              "id": "sample-execution-id",
              "name": "execution_id",
              "type": "string",
              "value": "manual-preview-paris"
            },
            {
              "id": "sample-scheduled-for",
              "name": "scheduled_for",
              "type": "string",
              "value": "={{ $now.setZone('Europe/Paris').toISO() }}"
            },
            {
              "id": "sample-timezone",
              "name": "timezone",
              "type": "string",
              "value": "Europe/Paris"
            },
            {
              "id": "sample-target-id",
              "name": "target_id",
              "type": "string",
              "value": "paris"
            },
            {
              "id": "sample-target-label",
              "name": "target_label",
              "type": "string",
              "value": "Paris"
            },
            {
              "id": "sample-target-timezone",
              "name": "target_timezone",
              "type": "string",
              "value": "Europe/Paris"
            },
            {
              "id": "sample-target-metadata",
              "name": "target_metadata",
              "type": "object",
              "value": "={{ { city: 'Paris', timezone: 'Europe/Paris', preview: true } }}"
            },
            {
              "id": "sample-input-type",
              "name": "input_type",
              "type": "string",
              "value": "manual-preview"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "e9b540fb-e2aa-47a8-b87e-0cde5259dbb8",
      "name": "Configure regional audiences",
      "type": "n8n-nodes-base.code",
      "position": [
        -440,
        -80
      ],
      "parameters": {
        "jsCode": "// Replace every example.com address before publishing the workflow.\nconst shared = {\n  sender: 'Coffee Newsletter <user@example.com>',\n  feed_url: 'https://news.ycombinator.com/rss',\n  max_articles: 5,\n};\n\nconst audiences = {\n  paris: {\n    timezone: 'Europe/Paris',\n    locale: 'fr-FR',\n    recipient: 'user@example.com',\n    greeting: 'Bonjour Paris',\n    subject_prefix: 'Votre pause caf\u00e9',\n    intro: 'Voici les articles \u00e0 d\u00e9couvrir avec votre caf\u00e9 ce matin.',\n    footer: 'Envoy\u00e9 \u00e0 09:00 heure locale gr\u00e2ce \u00e0 rrule.net et n8n.',\n  },\n  new_york: {\n    timezone: 'America/New_York',\n    locale: 'en-US',\n    recipient: 'user@example.com',\n    greeting: 'Good morning New York',\n    subject_prefix: 'Your morning coffee',\n    intro: 'Here are a few stories to explore with your coffee this morning.',\n    footer: 'Delivered at 09:00 local time with rrule.net and n8n.',\n  },\n  tokyo: {\n    timezone: 'Asia/Tokyo',\n    locale: 'ja-JP',\n    recipient: 'user@example.com',\n    greeting: '\u304a\u306f\u3088\u3046\u3054\u3056\u3044\u307e\u3059\u3001\u6771\u4eac',\n    subject_prefix: '\u671d\u306e\u30b3\u30fc\u30d2\u30fc\u30cb\u30e5\u30fc\u30b9',\n    intro: '\u4eca\u671d\u306e\u30b3\u30fc\u30d2\u30fc\u3068\u4e00\u7dd2\u306b\u8aad\u307f\u305f\u3044\u8a18\u4e8b\u3092\u304a\u5c4a\u3051\u3057\u307e\u3059\u3002',\n    footer: 'rrule.net \u3068 n8n \u306b\u3088\u308a\u73fe\u5730\u6642\u959309:00\u306b\u914d\u4fe1\u3055\u308c\u307e\u3057\u305f\u3002',\n  },\n  perth: {\n    timezone: 'Australia/Perth',\n    locale: 'en-AU',\n    recipient: 'user@example.com',\n    greeting: 'Good morning Perth',\n    subject_prefix: 'Your morning coffee',\n    intro: 'Here are a few stories to explore with your coffee this morning.',\n    footer: 'Delivered at 09:00 local time with rrule.net and n8n.',\n  },\n};\n\nreturn $input.all().map((item, itemIndex) => {\n  const targetId = String(item.json.target_id ?? '');\n  const audience = audiences[targetId];\n\n  if (!audience) {\n    throw new Error(`No audience configuration exists for target_id ${targetId}. Update the audiences map before publishing.`);\n  }\n\n  if (item.json.target_timezone && item.json.target_timezone !== audience.timezone) {\n    throw new Error(`Unexpected timezone ${item.json.target_timezone} for target_id ${targetId}. Expected ${audience.timezone}.`);\n  }\n\n  return {\n    json: {\n      ...item.json,\n      ...shared,\n      ...audience,\n    },\n    pairedItem: { item: itemIndex },\n  };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "18d2e9cc-ca89-4ecf-990a-2a1b08b093b8",
      "name": "Read the morning RSS feed",
      "type": "n8n-nodes-base.rssFeedRead",
      "maxTries": 3,
      "position": [
        -180,
        -80
      ],
      "parameters": {
        "url": "={{ $json.feed_url }}",
        "options": {}
      },
      "retryOnFail": true,
      "typeVersion": 1.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "636484f7-bd90-4615-b6b3-f54e54d64227",
      "name": "Build localized newsletter",
      "type": "n8n-nodes-base.code",
      "position": [
        80,
        -80
      ],
      "parameters": {
        "jsCode": "const config = $('Configure regional audiences').first().json;\n\nconst escapeHtml = (value) => String(value ?? '')\n  .replaceAll('&', '&amp;')\n  .replaceAll('<', '&lt;')\n  .replaceAll('>', '&gt;')\n  .replaceAll('\\\"', '&quot;')\n  .replaceAll(\"'\", '&#039;');\n\nconst safeUrl = (value) => {\n  const url = String(value ?? '').trim();\n  return /^https?:\\/\\/[^\\s<>\"']+$/i.test(url) ? url : '#';\n};\n\nconst articleLimit = Math.max(1, Math.min(10, Number(config.max_articles) || 5));\nconst articles = $input.all()\n  .map((item) => item.json)\n  .filter((article) => typeof article.title === 'string' && typeof article.link === 'string')\n  .slice(0, articleLimit);\n\nif (articles.length === 0) {\n  throw new Error('The RSS feed returned no usable articles. Check feed_url before publishing.');\n}\n\nconst scheduledDate = new Date(String(config.scheduled_for || new Date().toISOString()));\nconst displayDate = new Intl.DateTimeFormat(String(config.locale), {\n  dateStyle: 'full',\n  timeZone: String(config.timezone),\n}).format(scheduledDate);\n\nconst articleHtml = articles.map((article) => {\n  const title = escapeHtml(article.title);\n  const link = escapeHtml(safeUrl(article.link));\n  const rawSnippet = String(article.contentSnippet ?? '').trim();\n  const snippet = /^Article URL:/i.test(rawSnippet) ? '' : escapeHtml(rawSnippet.slice(0, 240));\n  const description = snippet ? `<p style=\\\"margin:8px 0 0;color:#5f5149;font-size:14px;font-weight:400;line-height:1.5;\\\">${snippet}</p>` : '';\n  return `<li style=\\\"margin:0 0 22px;padding-left:4px;color:#6f3f21;font-size:17px;font-weight:700;line-height:1.4;\\\"><a href=\\\"${link}\\\" style=\\\"color:#6f3f21;font-size:17px;font-weight:700;line-height:1.4;text-decoration:none;\\\">${title}</a>${description}</li>`;\n}).join('');\n\nconst html = `<!doctype html>\n<html lang=\\\"${escapeHtml(String(config.locale).split('-')[0] || 'en')}\\\">\n  <head>\n    <meta charset=\\\"utf-8\\\">\n    <meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1\\\">\n  </head>\n  <body style=\\\"margin:0;background:#f7f1eb;font-family:Arial,sans-serif;color:#2d241f;\\\">\n    <div style=\\\"display:none;max-height:0;overflow:hidden;opacity:0;color:transparent;\\\">${escapeHtml(config.intro)}</div>\n    <table role=\\\"presentation\\\" width=\\\"100%\\\" cellspacing=\\\"0\\\" cellpadding=\\\"0\\\" style=\\\"background:#f7f1eb;padding:24px 12px;\\\">\n      <tr><td align=\\\"center\\\">\n        <table role=\\\"presentation\\\" width=\\\"100%\\\" cellspacing=\\\"0\\\" cellpadding=\\\"0\\\" style=\\\"max-width:640px;background:#ffffff;border-radius:14px;overflow:hidden;\\\">\n          <tr><td style=\\\"background:#5b3825;padding:28px 32px;color:#ffffff;\\\">\n            <div style=\\\"font-size:13px;letter-spacing:.08em;text-transform:uppercase;opacity:.8;\\\">${escapeHtml(displayDate)}</div>\n            <h1 style=\\\"margin:10px 0 0;font-size:28px;\\\">\u2615 ${escapeHtml(config.greeting)}</h1>\n          </td></tr>\n          <tr><td style=\\\"padding:30px 32px;\\\">\n            <p style=\\\"margin:0 0 24px;font-size:16px;line-height:1.6;\\\">${escapeHtml(config.intro)}</p>\n            <ol style=\\\"margin:0;padding-left:24px;\\\">${articleHtml}</ol>\n          </td></tr>\n          <tr><td style=\\\"padding:20px 32px;background:#efe3d7;color:#6b5a50;font-size:12px;line-height:1.5;\\\">\n            ${escapeHtml(config.footer)}<br>Target: ${escapeHtml(config.target_id)} \u00b7 ${escapeHtml(config.target_timezone)}\n          </td></tr>\n        </table>\n      </td></tr>\n    </table>\n  </body>\n</html>`;\n\nreturn [{\n  json: {\n    execution_id: config.execution_id,\n    scheduled_for: config.scheduled_for,\n    target_id: config.target_id,\n    target_timezone: config.target_timezone,\n    recipient: config.recipient,\n    sender: config.sender,\n    subject: `${config.subject_prefix} \u2014 ${displayDate}`,\n    html,\n    article_count: articles.length,\n  },\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "33bf7781-5b92-4105-aaed-78de5b3863e0",
      "name": "Send with Brevo",
      "type": "n8n-nodes-base.sendInBlue",
      "position": [
        340,
        -80
      ],
      "parameters": {
        "sender": "={{ $json.sender }}",
        "subject": "={{ $json.subject }}",
        "resource": "email",
        "sendHTML": true,
        "operation": "send",
        "htmlContent": "={{ $json.html }}",
        "receipients": "={{ $json.recipient }}",
        "additionalFields": {
          "emailTags": {
            "tags": {
              "tag": "rrulenet,coffee-newsletter"
            }
          }
        }
      },
      "typeVersion": 1
    },
    {
      "id": "19ad56f4-4a2f-411e-90e5-96703e00234c",
      "name": "Return delivery context",
      "type": "n8n-nodes-base.set",
      "position": [
        600,
        -80
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "result-message-id",
              "name": "message_id",
              "type": "string",
              "value": "={{ $json.messageId }}"
            },
            {
              "id": "result-execution-id",
              "name": "execution_id",
              "type": "string",
              "value": "={{ $('Build localized newsletter').first().json.execution_id }}"
            },
            {
              "id": "result-scheduled-for",
              "name": "scheduled_for",
              "type": "string",
              "value": "={{ $('Build localized newsletter').first().json.scheduled_for }}"
            },
            {
              "id": "result-target-id",
              "name": "target_id",
              "type": "string",
              "value": "={{ $('Build localized newsletter').first().json.target_id }}"
            },
            {
              "id": "result-target-timezone",
              "name": "target_timezone",
              "type": "string",
              "value": "={{ $('Build localized newsletter').first().json.target_timezone }}"
            },
            {
              "id": "result-recipient",
              "name": "recipient",
              "type": "string",
              "value": "={{ $('Build localized newsletter').first().json.recipient }}"
            },
            {
              "id": "result-article-count",
              "name": "article_count",
              "type": "number",
              "value": "={{ $('Build localized newsletter').first().json.article_count }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "34284232-2ab6-457d-a5b9-38d5adc34be0",
      "name": "Workflow guide",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1560,
        -400
      ],
      "parameters": {
        "width": 480,
        "height": 880,
        "content": "## Coffee newsletters at 09:00 local time\n\nThis workflow uses one target-aware rrule.net schedule to send a localized morning digest in Paris, New York, Tokyo, and Perth.\n\n### Before activation\n\n1. Install `@rrulenet/n8n-nodes-rrulenet` version 0.1.4 or later.\n2. Add rrule.net and Brevo credentials.\n3. In **Configure regional audiences**, replace the example sender and recipients.\n4. Optionally change the RSS feed and article limit in the same node.\n5. Run **When clicking 'Execute workflow'** to test the Paris preview.\n6. Check the email and links, then activate the workflow.\n\n### Customize\n\nAdd targets and matching audience entries, replace the RSS source, adapt the localized copy and branding, or swap Brevo for another email provider."
      },
      "typeVersion": 1
    },
    {
      "id": "3ade7f99-6a40-4a0c-8868-506081861f66",
      "name": "Target-aware schedule",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1040,
        -400
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 440,
        "content": "## One schedule, four local times\n\n**At 09:00 in each target timezone** creates one logical rrule.net schedule with four targets. Paris and New York stay at 09:00 through DST changes; Tokyo and Perth keep their non-DST offsets. Each execution carries `target_id` and `target_timezone` for deterministic routing."
      },
      "typeVersion": 1
    },
    {
      "id": "d902b6ee-9e0b-456e-bcda-a2a888e3668e",
      "name": "Manual preview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1040,
        40
      ],
      "parameters": {
        "color": 7,
        "width": 560,
        "height": 440,
        "content": "## Safe manual preview\n\n**When clicking 'Execute workflow'** injects a sample Paris execution without changing the remote rrule.net schedule. After you configure credentials and real addresses, the preview sends a real email through Brevo."
      },
      "typeVersion": 1
    },
    {
      "id": "34ffd0a9-a614-4d0d-92d7-c4485895a8d1",
      "name": "Localized content",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -480,
        -400
      ],
      "parameters": {
        "color": 7,
        "width": 720,
        "height": 440,
        "content": "## Select, fetch, and localize\n\n**Configure regional audiences** matches `target_id` to its locale, timezone, sender, recipient, and localized copy. **Read the morning RSS feed** loads the configured source. **Build localized newsletter** keeps usable articles, escapes feed text, accepts only HTTP(S) links, and creates the responsive HTML email."
      },
      "typeVersion": 1
    },
    {
      "id": "e90b268d-2f13-4fea-bf3b-4243bb55c334",
      "name": "Delivery context",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        288,
        -400
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 440,
        "content": "## Send and return context\n\n**Send with Brevo** sends one digest for the current target. **Return delivery context** exposes the message ID, execution ID, scheduled time, target, recipient, and article count. Add the required consent, unsubscribe, and list-management behavior before public marketing use."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "a9d67525-22b0-46e6-bd3f-41e031ebeb14",
  "connections": {
    "Send with Brevo": {
      "main": [
        [
          {
            "node": "Return delivery context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read the morning RSS feed": {
      "main": [
        [
          {
            "node": "Build localized newsletter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build localized newsletter": {
      "main": [
        [
          {
            "node": "Send with Brevo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configure regional audiences": {
      "main": [
        [
          {
            "node": "Read the morning RSS feed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Use a sample Paris execution": {
      "main": [
        [
          {
            "node": "Configure regional audiences",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "At 09:00 in each target timezone": {
      "main": [
        [
          {
            "node": "Configure regional audiences",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking 'Execute workflow'": {
      "main": [
        [
          {
            "node": "Use a sample Paris execution",
            "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

Fetch an RSS feed, build a localized morning digest, and send it with Brevo at 09:00 local time in Paris, New York, Tokyo, and Perth from one target-aware rrule.net schedule. One rrule.net Schedule Trigger emits an execution at 09:00 in each configured target timezone. The…

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

More General workflows → · Browse all categories →

Related workflows

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

General

Send a coffee newsletter at 09:00 local time in every region. Uses @rrulenet/n8n-nodes-rrulenet, rssFeedRead, sendInBlue. Scheduled trigger; 11 nodes.

@Rrulenet/N8N Nodes Rrulenet, RSS Feed Read, Sendinblue
General

This workflow runs hourly to read multiple RSS/Atom feeds, applies rule-based topic matching, relevance scoring, sentiment and entity tagging with deduplication, and then sends scored HTML email diges

RSS Feed Read, Email Send
General

World News Collector (10m). Uses rssFeedRead, dataTable. Scheduled trigger; 11 nodes.

RSS Feed Read, Data Table
General

Job Seeking. Uses scheduleTrigger, rssFeedRead, googleSheets, httpRequest. Scheduled trigger; 12 nodes.

RSS Feed Read, Google Sheets, HTTP Request +1
General

Finance Alert. Uses rssFeedRead, openAi, httpRequest. Scheduled trigger; 11 nodes.

RSS Feed Read, OpenAI, HTTP Request