{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "30a41334-edc9-46fc-aaba-95712e52bf3e",
      "name": "Set topics and language",
      "type": "n8n-nodes-base.set",
      "position": [
        -880,
        -368
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "2e1993fe-ae4d-46e0-be9f-6b02a18cc610",
              "name": "topics",
              "type": "string",
              "value": "AI, n8n"
            },
            {
              "id": "2c6e2dad-d54a-4387-9bf5-4751037f99d9",
              "name": "language",
              "type": "string",
              "value": "English"
            },
            {
              "id": "d1b520a6-2be1-4ca4-ac56-bd202cd91db1",
              "name": "minDaysBetween",
              "type": "number",
              "value": 2
            },
            {
              "id": "f77efe55-ab66-465d-aa4e-34e63e8fab17",
              "name": "maxDaysBetween",
              "type": "number",
              "value": 5
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "54136f01-80b7-4075-859b-54883fd13915",
      "name": "Build topic queries",
      "type": "n8n-nodes-base.code",
      "position": [
        -656,
        -368
      ],
      "parameters": {
        "jsCode": "return $input.first().json.topics.split(',').map(topic => ({\n  json: { topic: topic.trim() }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "5437a25b-4c4b-43b6-8dd3-422da7fc6856",
      "name": "Fetch news from SerpAPI (DuckDuckGo News)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -432,
        -368
      ],
      "parameters": {
        "url": "https://serpapi.com/search.html",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "engine",
              "value": "duckduckgo_news"
            },
            {
              "name": "q",
              "value": "={{ $json.topic }}"
            },
            {
              "name": "df",
              "value": "d"
            }
          ]
        }
      },
      "credentials": {
        "httpQueryAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "96501c79-9e18-4ae1-92df-504a4939ffa2",
      "name": "Split SerpAPI results into articles",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -208,
        -368
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "results"
      },
      "typeVersion": 1
    },
    {
      "id": "a118cc21-1454-436a-8f0e-e5104e28d1a9",
      "name": "Upsert articles into News table",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        16,
        -368
      ],
      "parameters": {
        "columns": {
          "value": {
            "url": "={{ $json.url }}",
            "date": "={{ DateTime.fromSeconds(Number($json.date), {zone: 'utc'}).toISO() }}",
            "title": "={{ $json.title }}",
            "source": "={{ $json.source }}",
            "excerpt": "={{ $json.excerpt }}"
          },
          "schema": [
            {
              "id": "title",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "title",
              "defaultMatch": false
            },
            {
              "id": "source",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "source",
              "defaultMatch": false
            },
            {
              "id": "url",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "url",
              "defaultMatch": false
            },
            {
              "id": "excerpt",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "excerpt",
              "defaultMatch": false
            },
            {
              "id": "date",
              "type": "dateTime",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "date",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "filters": {
          "conditions": [
            {
              "keyName": "title",
              "keyValue": "={{ $json.title }}"
            },
            {
              "keyName": "url",
              "keyValue": "={{ $json.url }}"
            }
          ]
        },
        "options": {
          "dryRun": false
        },
        "operation": "upsert",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "gGl7dr2dlh3davuE",
          "cachedResultUrl": "/projects/qtrMjn4i49GhWSSe/datatables/gGl7dr2dlh3davuE",
          "cachedResultName": "News"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "29f24b95-cd08-4141-943d-da16e54a18be",
      "name": "Get previous newsletters",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -1104,
        32
      ],
      "parameters": {
        "operation": "get",
        "returnAll": true,
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "70dNVBaVmekyxH50",
          "cachedResultUrl": "/projects/qtrMjn4i49GhWSSe/datatables/70dNVBaVmekyxH50",
          "cachedResultName": "Newsletters"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "2f5f51c0-4b1e-4148-91cb-fb0ae820bd07",
      "name": "Sort newsletters by newest first",
      "type": "n8n-nodes-base.sort",
      "onError": "continueRegularOutput",
      "position": [
        -912,
        32
      ],
      "parameters": {
        "options": {},
        "sortFieldsUi": {
          "sortField": [
            {
              "order": "descending",
              "fieldName": "createdAt"
            }
          ]
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "312b873b-822a-454d-a65a-ac326e887891",
      "name": "Get most recent newsletter",
      "type": "n8n-nodes-base.limit",
      "position": [
        -720,
        32
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "4c8a0574-d7a5-40cc-ae0a-7abcfd6ea96f",
      "name": "Limit previous newsletters to last 5",
      "type": "n8n-nodes-base.limit",
      "position": [
        -1104,
        480
      ],
      "parameters": {
        "maxItems": 5
      },
      "typeVersion": 1
    },
    {
      "id": "2acf19c2-f0d1-4435-8f83-4b53266a4362",
      "name": "Aggregate previous newsletters into list",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        -896,
        480
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "newsletters"
      },
      "typeVersion": 1
    },
    {
      "id": "eb5e8dc6-4553-45e2-859c-4eccd3464c1f",
      "name": "Combine articles with last newsletter metadata",
      "type": "n8n-nodes-base.merge",
      "position": [
        -496,
        48
      ],
      "parameters": {
        "mode": "combine",
        "options": {
          "clashHandling": {
            "values": {
              "resolveClash": "addSuffix"
            }
          }
        },
        "combineBy": "combineAll"
      },
      "typeVersion": 3.2
    },
    {
      "id": "a605ccda-aa6e-44b6-9e06-a3ce7f04e4bb",
      "name": "Filter articles newer than last newsletter",
      "type": "n8n-nodes-base.filter",
      "position": [
        -272,
        48
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "daf65a43-e643-4f64-a4ac-bc562418f4c8",
              "operator": {
                "type": "dateTime",
                "operation": "after"
              },
              "leftValue": "={{ $json.date_2 }}",
              "rightValue": "={{ $json.createdAt_1 ? $json.createdAt_1 : DateTime.fromISO('2024-01-01T00:00:00.000Z') }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "fdf0ec1f-c9b2-44ff-96b6-7ef80f2838d3",
      "name": "Stop if last newsletter is too recent",
      "type": "n8n-nodes-base.if",
      "position": [
        -64,
        48
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "4efef626-77be-4e29-8aa5-f9520cfa3127",
              "operator": {
                "type": "dateTime",
                "operation": "before"
              },
              "leftValue": "={{ $('Get most recent newsletter').item.json.createdAt }}",
              "rightValue": "={{ $now.minus({ days: $('Set topics and language').item.json.minDaysBetween }) }}"
            },
            {
              "id": "6c5410f0-2e2e-4212-9779-bf3a3a4fb009",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "772bbbf3-2d67-41b0-ad47-13aff0407cde",
      "name": "Aggregate candidate articles for AI",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        -1104,
        640
      ],
      "parameters": {
        "include": "specifiedFields",
        "options": {},
        "aggregate": "aggregateAllItemData",
        "fieldsToInclude": "title_2, excerpt_2, source_2, url_2",
        "destinationFieldName": "results"
      },
      "typeVersion": 1
    },
    {
      "id": "8f7881fd-9243-449a-81f3-1ec49b8c8582",
      "name": "Combine candidate articles with past newsletters",
      "type": "n8n-nodes-base.merge",
      "position": [
        -608,
        560
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3.2
    },
    {
      "id": "b3d1150a-0f2a-4be2-9db6-1e1733700105",
      "name": "If AI decided to send newsletter",
      "type": "n8n-nodes-base.if",
      "position": [
        -96,
        560
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "717c70de-dfc0-4ed5-86d6-3d1a612d0694",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.message.content.decision }}",
              "rightValue": "YES"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "7026acbc-aad2-491e-bdad-996ce1ffefe8",
      "name": "Split selected articles for enrichment",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -1104,
        992
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "message.content.articles"
      },
      "typeVersion": 1
    },
    {
      "id": "08067a28-c5a6-4907-80f4-f125bf091b44",
      "name": "GPT-5.1",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -1024,
        1216
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-5.1",
          "cachedResultName": "gpt-5.1"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "afee7f71-646b-489f-b17d-d01d86365fab",
      "name": "Tavily web search tool",
      "type": "@tavily/n8n-nodes-tavily.tavilyTool",
      "position": [
        -896,
        1216
      ],
      "parameters": {
        "query": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Query', ``, 'string') }}",
        "options": {}
      },
      "credentials": {
        "tavilyApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "efeac211-e0d4-4778-b0c3-60f0e03de8b9",
      "name": "Parse enriched article JSON",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -752,
        1216
      ],
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"title\": {\n\t\t\t\"type\": \"string\"\n\t\t},\n\t\t\"content\": {\n\t\t\t\"type\": \"string\"\n\t\t},\n        \"source\": {\n\t\t\t\"type\": \"string\"\n\t\t},\n        \"url\": {\n\t\t\t\"type\": \"string\"\n\t\t}\n\t}\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "9b5fb614-4e91-4e9c-88a3-7e36529fd1c5",
      "name": "AI: enrich & write article",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -960,
        992
      ],
      "parameters": {
        "text": "=You are a research writer that updates short news summaries into concise, factual articles.\n\n**Input:**\nTitle: {{ $json[\"title\"] }}\nSummary: {{ $json[\"summary\"] }}\nSource: {{ $json[\"source\"] }}\nOriginal URL: {{ $json[\"url\"] }}\nLanguage: {{ $('Set topics and language').item.json.language }}\n\n**Instructions:**\n\n1. Use **Tavily Search** to gather 2\u20133 reliable, recent, and relevant sources on this topic.\n2. Update the **title** if a more accurate or engaging one exists.\n3. Write **1\u20132 sentences** summarizing the topic, combining the original summary and information from the new sources.\n4. Return the original source name and url as well.\n\n**Output (JSON):**\n\n```json\n{\n  \"title\": \"final article title\",\n  \"content\": \"concise 1\u20133 sentence article content\",\n  \"source\": \"the name of the original source\",\n  \"url\": \"the url of the original source\"\n}\n```\n\n**Rules:**\n\n* Ensure the topic is relevant, informative, and timely.\n* Translate the article if necessary to comply with the desired language {{ $('Set topics and language').item.json.language }}.",
        "options": {},
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "2e8c420d-ac78-4eea-9866-3220f123f880",
      "name": "AI: decide send + select articles",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -416,
        560
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-5.1",
          "cachedResultName": "GPT-5.1"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "=You are an **AI Newsletter Editor**. Your job is to decide whether today\u2019s newsletter edition should be sent, and to select the best articles.\n\nYou will receive a list of articles with:\n'title', 'excerpt', `source`, `url`.\n\nYou will also receive content of **previously sent newsletters** (markdown).\n\n# Your Tasks\n\n## 1. Decide whether to send the newsletter\n\nOutput \"YES\" only if all of the following are satisfied **OR** the fallback rule applies:\n\n### **Base Criteria**\n\n1. There are **at least 3 meaningful articles**.\n   *Meaningful = not trivial, not purely promotional, not clickbait, contains actual informational value.*\n\n2. Articles must be **non-duplicate and non-overlapping**:\n\n   * Not the same topic/headline rephrased\n   * Not reporting identical events with minor variations\n   * Not the same news covered by multiple sources without distinct insights\n\n3. Articles must be **relevant to the user's topics**:\n   **{{ $('Set topics and language').item.json.topics }}**\n\n4. Articles must be **novel** relative to the **topics in previous newsletters**:\n\n   * Compare against all previous newsletters below\n   * Exclude articles that discuss topics already substantially covered\n\n5. Articles must offer **clear value**:\n\n   * New information\n   * Impact that matters to the user\n   * Insight, analysis, or meaningful expansion\n\n### **Fallback rule: Newsletter frequency requirement**\n\nIf **at least 1 relevant article exists** *and*\nthe last newsletter was sent **more than {{ $('Set topics and language').item.json.maxDaysBetween }} days ago**, then you **MUST** return \"YES\" as a decision even if the other criteria are not completely met.\n\nLast newsletter was sent {{ $('Get most recent newsletter').item.json.createdAt ? Math.floor($now.diff(DateTime.fromISO($('Get most recent newsletter').item.json.createdAt), 'days').days) : 999 }} days ago.\n\n### Otherwise \u2192 \"NO\"\n\n## **2. If \"YES\": Select Articles**\n\nSelect the **top 3\u20135** articles that best fulfill the criteria above.\n\nFor each selected article, output:\n\n* **title** (rewrite for clarity, conciseness, and impact)\n* **summary** (1\u20132 sentences; written in the output language)\n* **source**\n* **url**\n\nAll summaries **must** be written in:\n**{{ $('Set topics and language').item.json.language }}**\n\n---\n\n# **Output Format (JSON)**\n\n```json\n{\n  \"decision\": \"YES or NO\",\n  \"articles\": [\n    {\n      \"title\": \"...\",\n      \"summary\": \"...\",\n      \"source\": \"...\",\n      \"url\": \"...\"\n    }\n  ]\n}\n```\n\nWhen \"decision\": \"NO\", return an empty array for \"articles\".\n\n# **Article Input**\n\nUse these articles:\n\n{{\n$json.results.map(\narticle =>\n`Title: ${article.title_2}\nExcerpt: ${article.excerpt_2}\nSource: ${article.source_2}\nURL: ${article.url_2}`\n).join('\\n---\\n')\n}}\n\nYou must also consider the topics already covered in previous newsletters to avoid repetition:\n\n{{ $json.newsletters.map(x => `Newsletter: ${x.content}`).join('\\n---\\n') }}\n\n"
            }
          ]
        },
        "jsonOutput": true
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "f77c6e6f-966a-4448-a18a-2fbc76a1abdc",
      "name": "Aggregate enriched articles",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        -672,
        992
      ],
      "parameters": {
        "options": {},
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "fieldToAggregate": "output"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "72d2c603-6e58-456d-9567-cb2c3aef1387",
      "name": "Insert newsletter content into Newsletters table",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -448,
        992
      ],
      "parameters": {
        "columns": {
          "value": {
            "content": "={{    $json.output.map(article => {     const title = JSON.stringify(article.title).slice(1, -1);     const content = JSON.stringify(article.content).slice(1, -1);     const source = JSON.stringify(article.source).slice(1, -1);     const url = JSON.stringify(article.url).slice(1, -1);      return `*${title}*\\n${content}\\nSource: [${source}](${url})`;   }).join('\\n\\n') }}"
          },
          "schema": [
            {
              "id": "content",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "content",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "content"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "70dNVBaVmekyxH50",
          "cachedResultUrl": "/projects/qtrMjn4i49GhWSSe/datatables/70dNVBaVmekyxH50",
          "cachedResultName": "Newsletters"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "b69987aa-c78d-47d9-9cd6-54810108c9c1",
      "name": "Send newsletter to Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -256,
        992
      ],
      "parameters": {
        "text": "={{ \n  $('Aggregate enriched articles').item.json.output.map(article => {\n    const title = JSON.stringify(article.title).slice(1, -1);\n    const content = JSON.stringify(article.content).slice(1, -1);\n    const source = JSON.stringify(article.source).slice(1, -1);\n    const url = JSON.stringify(article.url).slice(1, -1);\n\n    return `*${title}*\\n${content}\\nSource: [${source}](${url})`;\n  }).join('\\n\\n')\n}}",
        "chatId": "89066090",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "23353202-c911-44b1-b8f0-0d506b8d72b0",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1152,
        -560
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 400,
        "content": "## Schedule & Configuration\n\nTriggers the workflow and sets global parameters.\n\n\u2022 Schedule Trigger: Runs at 9am and 5pm daily per default\n\u2022 Set topics and language: Configure your topics (comma-separated), output language, and frequency guardrails"
      },
      "typeVersion": 1
    },
    {
      "id": "3d3eda3a-1309-4e8b-a852-e009116d3b7a",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -704,
        -560
      ],
      "parameters": {
        "color": 7,
        "width": 896,
        "height": 400,
        "content": "## Fetch News\n\n\u2022 Build topic queries: Splits comma-separated topics into individual search queries\n\u2022 Fetch news from [SerpAPI](https://serpapi.com): Queries `DuckDuckGo News` via SerpAPI for each topic (last 24h)\n\u2022 Split results: Extracts individual articles from API response\n\u2022 Upsert into News table: Stores articles with deduplication by title+URL"
      },
      "typeVersion": 1
    },
    {
      "id": "8e14c373-3165-4015-98ab-3304427dc9b7",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1152,
        -144
      ],
      "parameters": {
        "color": 7,
        "width": 1344,
        "height": 384,
        "content": "## News Filtering & Frequency Guardrails\n\n\u2022 Get previous newsletters: Retrieves all past editions from the `Newsletters` table\n\u2022 Filter by date: Keeps only articles newer than the last newsletter\n\u2022 Frequency check: Stops execution if last newsletter was sent within `minDaysBetween`\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "4fbf49f0-5b04-4f23-94d0-310a8e813b61",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1152,
        256
      ],
      "parameters": {
        "color": 7,
        "width": 1344,
        "height": 544,
        "content": "## Newsletter Selection & Editorial AI\n\n- Aggregate articles: Bundles candidate articles with last 5 newsletters for context\n- AI: decide send + select articles: GPT-5.1 evaluates novelty, relevance, and value\n  - Returns `YES/NO` decision\n  - Selects top 3-5 articles when `YES`\n- Fallback rule: Forces `YES` if 1+ relevant article exists and `maxDaysBetween` exceeded"
      },
      "typeVersion": 1
    },
    {
      "id": "0d5908e4-fe0b-4dae-95d7-88c3705009e4",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1152,
        816
      ],
      "parameters": {
        "color": 7,
        "width": 1344,
        "height": 560,
        "content": "## Newsletter Writing & Delivery\n\n- Split selected articles: Processes each article individually\n- AI: enrich & write article: GPT-5.1 + Tavily research enhance each article (1-3 sentences)\n- Insert into `Newsletters` table: Archives the edition\n- Send to Telegram: Delivers formatted newsletter with bold titles and source links"
      },
      "typeVersion": 1
    },
    {
      "id": "a1405405-407b-4caf-9d7c-d50d2b41e6f4",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1104,
        -368
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 0 9,17 * * *"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "02f82f94-3543-45bf-890a-0fa78f72aa5d",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1744,
        -560
      ],
      "parameters": {
        "width": 576,
        "height": 752,
        "content": "# AI News Agent\n\nAutomated AI-powered newsletter that curates, filters, and delivers personalized news to Telegram.\n\n## How it works\n1. Schedule Trigger runs twice daily (9am & 5pm) and fetches news via SerpAPI's DuckDuckGo News engine for your configured topics.\n2. Articles are deduplicated and stored in an n8n Data Table.\n3. AI Editor (GPT-5.1) evaluates articles against past newsletters, checking for novelty, relevance, and value.\n4. Selected articles are enriched with Tavily web search and rewritten into concise summaries.\n5. Final newsletter is saved to the Newsletters table and sent to Telegram.\n\n## Setup\n- Add SerpAPI credentials (httpQueryAuth)\n- Add OpenAI API key\n- Add Tavily API key\n- Add Telegram Bot credentials\n- Set your Telegram Chat ID in the \"Send newsletter to Telegram\" node\n\n## Customize\n- Topics: Change the topics in \"Set topics and language\" node (comma-separated)\n- Language: Set output language\n- Frequency guardrails :Adjust minDaysBetween/maxDaysBetween to control send cadence\n- Timings: Modify cron expression for different run times"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "GPT-5.1": {
      "ai_languageModel": [
        [
          {
            "node": "AI: enrich & write article",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Set topics and language",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build topic queries": {
      "main": [
        [
          {
            "node": "Fetch news from SerpAPI (DuckDuckGo News)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tavily web search tool": {
      "ai_tool": [
        [
          {
            "node": "AI: enrich & write article",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Set topics and language": {
      "main": [
        [
          {
            "node": "Build topic queries",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get previous newsletters",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get previous newsletters": {
      "main": [
        [
          {
            "node": "Sort newsletters by newest first",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI: enrich & write article": {
      "main": [
        [
          {
            "node": "Aggregate enriched articles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get most recent newsletter": {
      "main": [
        [
          {
            "node": "Combine articles with last newsletter metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate enriched articles": {
      "main": [
        [
          {
            "node": "Insert newsletter content into Newsletters table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse enriched article JSON": {
      "ai_outputParser": [
        [
          {
            "node": "AI: enrich & write article",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Send newsletter to Telegram": {
      "main": [
        []
      ]
    },
    "Upsert articles into News table": {
      "main": [
        [
          {
            "node": "Combine articles with last newsletter metadata",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "If AI decided to send newsletter": {
      "main": [
        [
          {
            "node": "Split selected articles for enrichment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sort newsletters by newest first": {
      "main": [
        [
          {
            "node": "Get most recent newsletter",
            "type": "main",
            "index": 0
          },
          {
            "node": "Limit previous newsletters to last 5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI: decide send + select articles": {
      "main": [
        [
          {
            "node": "If AI decided to send newsletter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate candidate articles for AI": {
      "main": [
        [
          {
            "node": "Combine candidate articles with past newsletters",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Split SerpAPI results into articles": {
      "main": [
        [
          {
            "node": "Upsert articles into News table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Limit previous newsletters to last 5": {
      "main": [
        [
          {
            "node": "Aggregate previous newsletters into list",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stop if last newsletter is too recent": {
      "main": [
        [
          {
            "node": "Aggregate candidate articles for AI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split selected articles for enrichment": {
      "main": [
        [
          {
            "node": "AI: enrich & write article",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate previous newsletters into list": {
      "main": [
        [
          {
            "node": "Combine candidate articles with past newsletters",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch news from SerpAPI (DuckDuckGo News)": {
      "main": [
        [
          {
            "node": "Split SerpAPI results into articles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter articles newer than last newsletter": {
      "main": [
        [
          {
            "node": "Stop if last newsletter is too recent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine articles with last newsletter metadata": {
      "main": [
        [
          {
            "node": "Filter articles newer than last newsletter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine candidate articles with past newsletters": {
      "main": [
        [
          {
            "node": "AI: decide send + select articles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert newsletter content into Newsletters table": {
      "main": [
        [
          {
            "node": "Send newsletter to Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}