{
  "name": "Forex Factory \u2013 High-Impact Telegram Alerts",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "id": "node-schedule",
      "name": "Every 15 Minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        300
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "https://www.jblanked.com/news/api/calendar/",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Api-Key YOUR_FOREX_FACTORY_API_KEY_HERE"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "options": {
          "timeout": 10000,
          "response": {
            "response": {
              "fullResponse": false
            }
          }
        }
      },
      "id": "node-http",
      "name": "Fetch FF Calendar",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        220,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Split array response into individual n8n items\nconst events = $input.first().json;\n\nif (!Array.isArray(events)) {\n  throw new Error('Expected an array of events from Forex Factory API');\n}\n\n// Map each event to an n8n item\nreturn events.map(event => ({\n  json: {\n    id:         event.id        ?? '',\n    name:       event.name      ?? 'Unknown Event',\n    currency:   event.currency  ?? '',\n    impact:     event.impact    ?? '',\n    date:       event.date      ?? '',\n    time:       event.time      ?? '',\n    forecast:   event.forecast  ?? 'N/A',\n    previous:   event.previous  ?? 'N/A',\n    actual:     event.actual    ?? 'Pending',\n    outcome:    event.outcome   ?? '',\n    strength:   event.strength  ?? ''\n  }\n}));\n"
      },
      "id": "node-parse",
      "name": "Parse Events",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": false,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "cond-impact",
              "leftValue": "={{ $json.impact }}",
              "rightValue": "High",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "node-filter",
      "name": "Filter High Impact",
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2,
      "position": [
        660,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Deduplication using static in-memory store\n// For persistent dedup across runs, replace with a Redis or DB node\nif (!global.seenEventIds) {\n  global.seenEventIds = new Set();\n}\n\nconst results = [];\n\nfor (const item of $input.all()) {\n  const eventId = item.json.id + '_' + item.json.date;\n  if (!global.seenEventIds.has(eventId)) {\n    global.seenEventIds.add(eventId);\n    results.push(item);\n  }\n}\n\n// Limit set size to avoid memory bloat\nif (global.seenEventIds.size > 500) {\n  global.seenEventIds = new Set([...global.seenEventIds].slice(-200));\n}\n\nreturn results;\n"
      },
      "id": "node-dedup",
      "name": "Deduplicate Events",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        880,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Build a rich Telegram message for each high-impact event\nconst item = $input.first().json;\n\nconst flagMap = {\n  USD: '\ud83c\uddfa\ud83c\uddf8', EUR: '\ud83c\uddea\ud83c\uddfa', GBP: '\ud83c\uddec\ud83c\udde7', JPY: '\ud83c\uddef\ud83c\uddf5',\n  AUD: '\ud83c\udde6\ud83c\uddfa', CAD: '\ud83c\udde8\ud83c\udde6', CHF: '\ud83c\udde8\ud83c\udded', NZD: '\ud83c\uddf3\ud83c\uddff',\n  CNY: '\ud83c\udde8\ud83c\uddf3', INR: '\ud83c\uddee\ud83c\uddf3'\n};\n\nconst flag    = flagMap[item.currency] ?? '\ud83c\udf10';\nconst actual  = item.actual  && item.actual  !== 'Pending' ? `\\n\ud83d\udccc Actual:   ${item.actual}` : '';\nconst outcome = item.outcome ? `\\n\ud83d\udcca Outcome:  ${item.outcome}` : '';\nconst strength = item.strength ? `\\n\ud83d\udcaa Strength: ${item.strength}` : '';\n\nconst message =\n`\ud83d\udd34 *HIGH-IMPACT FOREX EVENT*\n\n${flag} *${item.currency}* \u2014 ${item.name}\n\n\ud83d\udd50 Time:      ${item.date} ${item.time}\n\ud83d\udcc8 Forecast:  ${item.forecast}\n\ud83d\udcc9 Previous:  ${item.previous}${actual}${outcome}${strength}\n\n\u26a1 _Stay alert \u2014 market volatility expected_`;\n\nreturn [{ json: { ...item, telegramMessage: message } }];\n"
      },
      "id": "node-format",
      "name": "Format Message",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1100,
        300
      ]
    },
    {
      "parameters": {
        "chatId": "YOUR_TELEGRAM_CHAT_ID_HERE",
        "text": "={{ $json.telegramMessage }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "disable_notification": false
        }
      },
      "id": "node-telegram",
      "name": "Send Telegram Alert",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1320,
        300
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "content": "## \ud83d\udfe2 Setup Checklist\n\n**1. Forex Factory API key**\n- Node: `Fetch FF Calendar`\n- Replace `YOUR_FOREX_FACTORY_API_KEY_HERE` in the header\n- Get free key at: https://www.jblanked.com\n\n**2. Telegram Bot**\n- Go to @BotFather on Telegram \u2192 `/newbot`\n- Copy the token \u2192 add to n8n Credentials as *Telegram API*\n- Node: `Send Telegram Alert` \u2192 link that credential\n\n**3. Telegram Chat ID**\n- Node: `Send Telegram Alert`\n- Replace `YOUR_TELEGRAM_CHAT_ID_HERE`\n- To find yours: message @userinfobot on Telegram\n- For a channel: use `-100XXXXXXXXXX` format\n\n**4. Schedule**\n- Currently set to every 15 minutes\n- Adjust in `Every 15 Minutes` node if needed\n\n**5. Deduplication**\n- In-memory only (resets on n8n restart)\n- For persistent dedup: add a Redis or Postgres node before `Deduplicate Events`",
        "height": 380,
        "width": 420,
        "color": 4
      },
      "id": "node-sticky",
      "name": "Setup Instructions",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -160
      ]
    },
    {
      "parameters": {
        "content": "## \ud83d\udcca Flow Overview\n\n**Every 15 min \u2192**\nFetch today's calendar from Forex Factory API\n\n**Parse \u2192**\nSplit array into individual event items\n\n**Filter \u2192**\nKeep only `impact === High`\n\n**Dedup \u2192**\nSkip already-alerted events\n\n**Format \u2192**\nBuild a Markdown message with flag emoji, time, forecast, previous, actual\n\n**Telegram \u2192**\nSend to your bot/channel",
        "height": 260,
        "width": 300,
        "color": 6
      },
      "id": "node-sticky2",
      "name": "Flow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        440,
        -160
      ]
    },
    {
      "parameters": {
        "content": "## \ud83d\udcac Example Telegram Message\n\n\ud83d\udd34 HIGH-IMPACT FOREX EVENT\n\n\ud83c\uddfa\ud83c\uddf8 USD \u2014 Non-Farm Payrolls\n\n\ud83d\udd50 Time: 2026-05-14 08:30\n\ud83d\udcc8 Forecast: 180K\n\ud83d\udcc9 Previous: 151K\n\ud83d\udccc Actual: 175K\n\ud83d\udcca Outcome: Worse than expected\n\ud83d\udcaa Strength: Medium\n\n\u26a1 Stay alert \u2014 market volatility expected",
        "height": 280,
        "width": 320,
        "color": 5
      },
      "id": "node-sticky3",
      "name": "Message Preview",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1100,
        -140
      ]
    }
  ],
  "connections": {
    "Every 15 Minutes": {
      "main": [
        [
          {
            "node": "Fetch FF Calendar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch FF Calendar": {
      "main": [
        [
          {
            "node": "Parse Events",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Events": {
      "main": [
        [
          {
            "node": "Filter High Impact",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter High Impact": {
      "main": [
        [
          {
            "node": "Deduplicate Events",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Deduplicate Events": {
      "main": [
        [
          {
            "node": "Format Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Message": {
      "main": [
        [
          {
            "node": "Send Telegram Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true,
    "callerPolicy": "workflowsFromSameOwner",
    "errorWorkflow": "",
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "all",
    "executionTimeout": 300,
    "timezone": "Asia/Kolkata"
  },
  "versionId": "1.0.0",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": [
    "forex",
    "telegram",
    "alerts",
    "forex-factory",
    "high-impact"
  ]
}