n8n
BUILD GUIDEDEVFEST PADERBORN14 NOV 2026

Build your personal daily brief

An AI agent in n8n that runs every morning on its own: weather, news on your topics, and a short brief written by Gemini.

SCHEDULEEvery morning at 7
EDIT FIELDSMy settings
HTTP REQUESTGet weather
IFRain over 50%?
RSS READGoogle News
AGENTAI Agent
GMAILSend brief
BEFORE YOU START

Setup

1

n8n Cloud trial

Start a free trial at n8n.io. 14 days, no credit card.

2

Gemini API key

At aistudio.google.com: Get API key → Create API key.

3

New workflow

In n8n, click Create workflow. That's it.

CORE BUILD

Build it in four stages

Watch each stage on the big screen, then build it. Try it yourself first and open a node only when you need the details.

STAGE 1

Settings and weather

Your workflow wakes up every morning, knows who you are and checks the weather.

SCHEDULEEvery morning at 7
EDIT FIELDSMy settings
HTTP REQUESTGet weather
EDIT FIELDSFormat weather
IFRain over 50%?
EDIT FIELDSUmbrella note
SCHEDULE TRIGGEREvery morning at 7Show me how

Trigger interval Days, hour 7am. You do not have to wait until 7: click Execute workflow to test.

EDIT FIELDSMy settingsShow me how

The one node you personalize. Everything else reads from here. Put in your own topics, city and email.

NameTypeValue
topicsstringAI agents, Gemini, n8n
citystringPaderborn
latitudenumber51.72
longitudenumber8.76
emailstringyou@example.com
HTTP REQUESTGet weatherShow me how

Method GET. Paste into URL. The curly braces read your coordinates from My settings.

https://api.open-meteo.com/v1/forecast?latitude={{ $('My settings').first().json.latitude }}&longitude={{ $('My settings').first().json.longitude }}&daily=temperature_2m_max,temperature_2m_min,precipitation_probability_max&timezone=auto&forecast_days=1
EDIT FIELDSFormat weatherShow me how

The API returns a lot of JSON. Keep only what the brief needs:

temp_min · number

{{ $json.daily.temperature_2m_min[0] }}

temp_max · number

{{ $json.daily.temperature_2m_max[0] }}

rain_chance · number

{{ $json.daily.precipitation_probability_max[0] }}

summary · string

{{ $json.daily.temperature_2m_min[0] }} to {{ $json.daily.temperature_2m_max[0] }} °C, {{ $json.daily.precipitation_probability_max[0] }}% chance of rain
IFRain over 50%?Show me how

Value 1 is this expression, operator Number → is greater than, value 2 is 50.

{{ $json.rain_chance }}
EDIT FIELDSUmbrella noteShow me how

On the true branch. One string field, umbrella_note:

☔ Take an umbrella today.

Behind or stuck? Copy the checkpoint and paste it into an empty canvas with Ctrl+V (Cmd+V on a Mac), then pick your credentials again.

Download JSON
STAGE 2

The agent

Gemini writes your first brief. For now it only knows the weather.

IFRain over 50%?
AGENTAI Agent
CHAT MODELGemini
AI AGENTAI AgentShow me how

Connect the false output of the If node and the output of Umbrella note to it. Source for prompt: Define below.

Write my morning brief for today, {{ $now.toFormat('cccc, d LLLL') }}.

Weather in {{ $('My settings').first().json.city }}: {{ $('Format weather').first().json.summary }}

Under Options → System message:

You are my personal morning briefing assistant.
My interests: {{ $('My settings').first().json.topics }}.

Write a short brief in plain text, no Markdown:
1. One line on today's weather.
2. The 3 to 5 most relevant stories for my interests, each with one sentence and its link.

Only use stories from the news list in the message or from your tools.
Never use your own knowledge for news. If there are no stories, write only the weather line.
Keep the brief under 200 words.

Important: in the node's Settings tab, switch on Execute once. Otherwise the agent writes one brief per news story in stage 3.

GOOGLE GEMINI CHAT MODELGeminiShow me how

Click the Chat Model plus under the agent. Create a credential with your Gemini API key and pick this model:

gemini-3.5-flash-lite

Behind or stuck? Copy the checkpoint and paste it into an empty canvas with Ctrl+V (Cmd+V on a Mac), then pick your credentials again.

Download JSON
STAGE 3

News

Real news on your topics, plus a tool the agent decides to use on its own.

IFRain over 50%?
RSS READGoogle News
AGENTAI Agent
TOOLHacker News
RSS READGoogle NewsShow me how

Place it between the If node and the agent. It builds a Google News search from your topics, last 24 hours only:

https://news.google.com/rss/search?q={{ encodeURIComponent($('My settings').first().json.topics.split(',').map(t => '"' + t.trim() + '"').join(' OR ') + ' when:1d') }}&hl=en-US&gl=US&ceid=US:en
AI AGENTAI Agent: new promptShow me how

Replace the prompt with this version, which hands over the news:

Write my morning brief for today, {{ $now.toFormat('cccc, d LLLL') }}.

Weather in {{ $('My settings').first().json.city }}: {{ $('Format weather').first().json.summary }}

News on my topics from Google News:
{{ $('Google News').all().slice(0, 15).map(item => '- ' + item.json.title + ' (' + item.json.link + ')').join('\n') }}

Also check the Hacker News front page with your tool and include stories that fit my interests.
HTTP REQUEST TOOLHacker NewsShow me how

Click the Tool plus under the agent. Description:

Returns the current Hacker News front page stories (title, url, points) as JSON.

URL:

https://hn.algolia.com/api/v1/search?tags=front_page&hitsPerPage=10&attributesToRetrieve=title,url,points&attributesToHighlight=%5B%5D

Behind or stuck? Copy the checkpoint and paste it into an empty canvas with Ctrl+V (Cmd+V on a Mac), then pick your credentials again.

Download JSON
STAGE 4

Send and switch on

The brief lands in your inbox. Switch it on, and tomorrow at 7 it runs without you.

AGENTAI Agent
GMAILSend brief
GMAILSend briefShow me how

Operation Send a message, connect with Sign in with Google. Gmail is only the last step, not a tool of the agent, so Gemini cannot read your inbox. n8n itself does get mailbox access: use ntfy or Discord below if you prefer.

To

{{ $('My settings').first().json.email }}

Subject

Your daily brief, {{ $now.toFormat('ccc d LLL') }}

Email type Text, message

{{ $('Umbrella note').isExecuted ? $('Umbrella note').first().json.umbrella_note + '\n\n' : '' }}{{ $json.output }}

Under Options, switch off Append n8n attribution.

HTTP REQUESTOr: ntfy pushShow me how

No account needed. Subscribe to a hard-to-guess topic in the ntfy app or at ntfy.sh. Then method POST, body Raw, content type text/plain.

URL

https://ntfy.sh/your-secret-topic

Body

{{ $json.output }}
HTTP REQUESTOr: DiscordShow me how

In your own Discord server: Server settings → Integrations → Webhooks → Copy webhook URL. Method POST, URL = webhook, body JSON:

{{ JSON.stringify({ content: $json.output }) }}
ACTIVATESwitch it onShow me how

Run it once and check your inbox. Then set the workflow to Active in the top bar. Your first real brief arrives tomorrow at 7:00.

Behind or stuck? Copy the checkpoint and paste it into an empty canvas with Ctrl+V (Cmd+V on a Mac), then pick your credentials again.

Download JSON
MAKE IT YOURS

Remix cards

Pick a card, or build your own idea. Start with the hint, and open the full solution only if you get stuck.

Quick remixes

QUICK

Brief on demand

Type a topic into a form and get a brief right away, in the browser.

FORM TRIGGERIFFORM ENDING
Hint

A workflow can have more than one trigger. $('Node name').isExecuted tells you which one started the run.

Full solution

Add an n8n Form Trigger with one field, Topic, and connect it to My settings. Use this as the value of topics:

{{ $('Brief on demand').isExecuted ? $('Brief on demand').first().json.Topic : 'AI agents, Gemini, n8n' }}

After the agent, an If node checks {{ $('Brief on demand').isExecuted }}. True goes to an n8n Form node (operation Form ending) showing {{ $json.output }}, false goes to Gmail.

Download JSON
QUICK

Feed your brief

More sources: new releases of a repo you follow, and your Google Calendar.

RSS READGOOGLE CALENDAR TOOL
Hint

Every GitHub repo has a releases feed at /releases.atom. And every new source also belongs in the system prompt.

Full solution

Add github_repo to My settings. After Google News, add an RSS Read node (Settings: Execute once):

https://github.com/{{ $('My settings').first().json.github_repo }}/releases.atom

Add a Google Calendar tool to the agent: Get many events, from today to tomorrow. Then tell the agent about its new sources:

You are my personal morning briefing assistant.
My interests: {{ $('My settings').first().json.topics }}.

Your sources:
- Today's weather and Google News stories, in the message
- New releases of {{ $('My settings').first().json.github_repo }}, in the message
- My Google Calendar, through your calendar tool
- The Hacker News front page, through your Hacker News tool

Write a short brief in plain text, no Markdown, in this order:
1. One line on today's weather.
2. Today's calendar events with their start times. Leave this part out if there are none.
3. The 3 to 5 most relevant stories for my interests, each with one sentence and its link.
4. New releases of {{ $('My settings').first().json.github_repo }}, one line each. Leave this part out if there are none.

Only use information from these sources. Never use your own knowledge for news, events or releases.
Keep the brief under 250 words.
Download JSON
QUICK

Channel router

Email on weekdays, a push via ntfy at the weekend.

SWITCHHTTP REQUEST
Hint

{{ $now.weekday }} is 1 on Monday and 7 on Sunday.

Full solution

Put a Switch node between the agent and Gmail with two rules on {{ $now.weekday }}: is less than or equal to 5 goes to Gmail, is greater than 5 goes to ntfy:

https://ntfy.sh/{{ $('My settings').first().json.ntfy_topic }}

Add ntfy_topic to My settings and subscribe to it in the ntfy app.

Download JSON
QUICK

Rain or shine

Your own weather rules in JavaScript, with a tip on top of every brief.

CODE
Hint

A Code node reads the previous node with $input.first().json and returns new items.

Full solution

Add a Code node after Format weather. Change the thresholds and messages as you like:

const w = $input.first().json;
let tip;
if (w.rain_chance > 70) tip = '🚆 Heavy rain likely: take the bus or train.';
else if (w.temp_max < 5) tip = '🧣 Cold day: wrap up warm.';
else if (w.temp_max > 25) tip = '🕶️ Warm day: bring water and sunscreen.';
else tip = '🚲 Good day to bike.';
return [{ json: { ...w, tip } }];

Start the Gmail message with {{ $('Weather tip').first().json.tip }}.

Download JSON
QUICK

Make it pretty

Structured data from the agent, and a newsletter-style HTML email.

STRUCTURED OUTPUT PARSERHTML
Hint

In the agent, switch on Require specific output format.

Full solution

Connect a Structured Output Parser to the agent with this example:

{
  "weather": "8 to 12 \u00b0C, 40% chance of rain",
  "stories": [
    {
      "title": "Story title",
      "summary": "One sentence on why it matters.",
      "link": "https://example.com"
    }
  ]
}

Add an HTML node after the agent with this template:

<div style="font-family: Arial, sans-serif; max-width: 560px; margin: 0 auto; color: #191919;">
  <h1 style="font-size: 24px; margin-bottom: 4px;">Your daily brief</h1>
  <p style="color: #676767; margin-top: 0;">{{ $now.toFormat('cccc, d LLLL') }}</p>
  {{ $('Umbrella note').isExecuted ? '<p style="background: #FFE8EC; padding: 12px 16px; border-radius: 8px;">☔ Take an umbrella today.</p>' : '' }}
  <p style="font-size: 16px;"><strong>Weather:</strong> {{ $json.output.weather }}</p>
  <h2 style="font-size: 18px; margin-top: 24px;">Stories</h2>
  {{ $json.output.stories.map(s => `<div style="margin-bottom: 16px;"><a href="${s.link}" style="font-size: 16px; font-weight: bold; color: #191919;">${s.title}</a><p style="margin: 4px 0 0;">${s.summary}</p></div>`).join('') }}
</div>

In Gmail, set email type HTML and message {{ $json.html }}.

Download JSON

Deep remixes

DEEP

No repeats

A Google Sheet remembers every story already seen, so nothing appears twice.

GOOGLE SHEETSFILTERCODE
Hint

Read the sheet before Google News, then compare links in a Filter node.

Full solution

Create a sheet with the columns date, title, link. Read it before Google News (Settings: Execute once and Always output data). After Google News, a Filter keeps a story if this is true:

{{ !$('Already sent').all().map(row => row.json.link).includes($json.link) }}

After Gmail, a Code node turns the new stories into rows, and Google Sheets appends them:

const today = $now.toISODate();
return $('Only new stories').all().filter(item => item.json.title).slice(0, 15).map(item => ({
  json: { date: today, title: item.json.title, link: item.json.link }
}));
Download JSON
DEEP

Talk back

Your brief arrives on Telegram. Reply to it and ask follow-up questions.

TELEGRAM TRIGGERAI AGENTSIMPLE MEMORY
Hint

When you reply to a Telegram message, the original text comes along in message.reply_to_message.

Full solution
  1. In Telegram, message @BotFather, send /newbot, and save the token as a Telegram credential.
  2. Message your bot, run a Telegram Trigger once and copy message.chat.id into My settings as telegram_chat_id.
  3. Send the brief with a Telegram node instead of Gmail.
  4. A second agent with Simple Memory (session key: the chat id) answers replies with this prompt:
{{ $json.message.reply_to_message ? 'This is the brief I am replying to:\n' + $json.message.reply_to_message.text + '\n\n' : '' }}My message: {{ $json.message.text }}

Activate the workflow so the bot keeps listening.

Download JSON
DEEP

Morning podcast

Gemini reads your brief aloud, and the audio arrives as an attachment.

HTTP REQUESTIMPORT CURLCODE
Hint

There is no Gemini speech node. Use any API the developer way: Import cURL in the HTTP Request node.

Full solution

Add an HTTP Request node after the agent, click Import cURL and paste:

curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash-lite-tts:generateContent" \
  -H "x-goog-api-key: YOUR_GEMINI_KEY" \
  -H "Content-Type: application/json" \
  -X POST \
  -d '{"contents":[{"parts":[{"text":"Good morning. This is your daily brief."}]}],"generationConfig":{"responseModalities":["AUDIO"],"speechConfig":{"voiceConfig":{"prebuiltVoiceConfig":{"voiceName":"Kore"}}}}}'

Remove the x-goog-api-key header, set Authentication to Predefined credential type → Google Gemini (PaLM) API, and use this JSON body:

{{ JSON.stringify({ contents: [{ parts: [{ text: $json.output.replace(/\(?https?:\/\/\S+\)?/g, '') }] }], generationConfig: { responseModalities: ['AUDIO'], speechConfig: { voiceConfig: { prebuiltVoiceConfig: { voiceName: $('My settings').first().json.tts_voice } } } } }) }}

Gemini returns raw audio. This Code node turns it into a WAV file:

// Gemini returns raw audio (PCM, 24 kHz, 16 bit, mono). Add a WAV header so any player can open it.
const b64 = $input.first().json.candidates[0].content.parts[0].inlineData.data;
const pcm = Buffer.from(b64, 'base64');
const rate = 24000, channels = 1, bits = 16;
const h = Buffer.alloc(44);
h.write('RIFF', 0); h.writeUInt32LE(36 + pcm.length, 4); h.write('WAVE', 8);
h.write('fmt ', 12); h.writeUInt32LE(16, 16); h.writeUInt16LE(1, 20); h.writeUInt16LE(channels, 22);
h.writeUInt32LE(rate, 24); h.writeUInt32LE(rate * channels * bits / 8, 28);
h.writeUInt16LE(channels * bits / 8, 32); h.writeUInt16LE(bits, 34);
h.write('data', 36); h.writeUInt32LE(pcm.length, 40);
const wav = Buffer.concat([h, pcm]);
return [{ json: {}, binary: { data: { data: wav.toString('base64'), mimeType: 'audio/wav', fileName: 'daily-brief.wav' } } }];

In Gmail, add an attachment with the field name data. Add tts_voice (for example Kore) to My settings.

Download JSON
AFTER THE SESSION

What's next

Keep your workflow

The trial ends after 14 days. Export your workflow before that: workflow menu → Download. n8n is also free to self-host.

Join the community

n8n Paderborn: a relaxed group of people who build with n8n. Join when you like, come to a meetup when you feel like it.

Join the Discord

Tell us how it went

Five quick questions, under a minute.

Give feedback
HELP

When something does not work

The agent writes several briefs

AI Agent → Settings tab → switch on Execute once. Google News sends one item per story.

Gemini returns 429 or "quota"

The free tier allows only a few requests per minute. Wait a minute and run it again.

No news, or old news

Your topics may be too narrow for the last 24 hours. Add broader topics in My settings.

An expression shows in red or as plain text

Switch the field from Fixed to Expression, or start typing with {{.

A pasted workflow shows warnings

Credentials are not copied. Open the Gemini and Gmail nodes and pick your own.