Holly T.
@handler_holly ·
N8N workflow: AI agent reads the web every midnight → personalised newsletter → Gmail + Google Sheets duplicate prevention
Built this to replace my daily manual news-reading habit. Workflow runs at midnight, searches the web for recent AI tool launches, formats a newsletter, emails it to me, and logs every story so duplicates never appear. Workflow JSON (GitHub Gist): https://gist.github.com/joseph1kurivila/05422e3064b2bd91e3998c20f8fd5e89 Architecture: Schedule Trigger → AI Agent (OpenRouter) Tools: Gemini search + Think tool → Gmail HTML newsletter → Code node (unbundle items) → Google Sheets (log for duplicate prevention) NODE BREAKDOWN: Node 1 — Schedule Trigger Fires at midnight daily. Adjust to any time. Node 2 — AI Agent (OpenRouter) User prompt: "Send me the newsletter for today." "Require Specific Output Format" toggle: ON This enforces the Structured Output Parser. Without it, the agent returns narrative text that breaks Gmail formatting. System prompt key instructions:- Exactly 3 news items - 150 words per item max - Return clean JSON with these fields: subject_line, news_items (title, content, why_it_matters, source, category) - Avoid stories older than 1 week Node 2a — Gemini tool (attached to AI Agent) Model: gemini-1.5-flash Search recency: Past week This is what gives the agent live internet access. Without this tool, the agent fabricates news from training data. Node 2b — Think tool (attached to AI Agent) No config needed. Gives the agent a quality-check pass before finalising output. Checks JSON validity and duplicate coverage. Node 3 — Gmail Subject: {{ $json.output.subject_line }} Body: HTML template mapping each news_item to a card. Switch body field to Expression mode — not Fixed — or the expression renders as literal text in the email. Node 4 — Code node (unbundle) Splits the 3-item bundle into 3 individual rows for Sheets logging. const data = $('News Research Agent').first().json.output; const newsList = data.news_items || []; const items = []; for (const news of newsList) { items.push({ json: { title: news.title, source: news.source, date_logged: new Date().toI