This workflow corresponds to n8n.io template #4608 — we link there as the canonical source.
This workflow follows the HTTP Request → Telegram recipe pattern — see all workflows that pair these two integrations.
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 →
{
"meta": {
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "ae4935b5-9d82-45c6-849e-e4b5d99d9f98",
"name": "Daily Morning Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"notes": "Triggers every day at 7:50 AM to send weather forecast before work hours",
"position": [
-200,
-300
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 7,
"triggerAtMinute": 50
}
]
}
},
"typeVersion": 1.2
},
{
"id": "fd99b9db-969a-4ce2-8823-343c0b847c84",
"name": "OpenWeather API Request",
"type": "n8n-nodes-base.httpRequest",
"notes": "Fetches 5-day forecast data with 3-hour intervals for Strassen, Luxembourg",
"maxTries": 3,
"position": [
220,
-300
],
"parameters": {
"url": "https://api.openweathermap.org/data/2.5/forecast?q=Strassen&appid=API_KEY&units=metric",
"options": {
"timeout": 30000
}
},
"notesInFlow": true,
"retryOnFail": true,
"typeVersion": 4.2,
"waitBetweenTries": 5000
},
{
"id": "0703447c-83b0-4261-a2a6-4063a48e64d0",
"name": "Weather Data Processor",
"type": "n8n-nodes-base.code",
"notes": "Enhanced processor with comprehensive weather analysis, recommendations, and formatting",
"position": [
620,
-300
],
"parameters": {
"jsCode": "// Enhanced Weather Forecast Processor\n// Processes OpenWeatherMap API data and formats for Telegram\n\nfunction getWeatherEmoji(weatherMain, cloudiness = 0, isNight = false) {\n const emojiMap = {\n 'Clear': isNight ? '\ud83c\udf19' : '\u2600\ufe0f',\n 'Clouds': cloudiness > 75 ? '\u2601\ufe0f' : cloudiness > 25 ? '\u26c5' : '\ud83c\udf24\ufe0f',\n 'Rain': '\ud83c\udf27\ufe0f',\n 'Drizzle': '\ud83c\udf26\ufe0f',\n 'Thunderstorm': '\u26c8\ufe0f',\n 'Snow': '\u2744\ufe0f',\n 'Mist': '\ud83c\udf2b\ufe0f',\n 'Fog': '\ud83c\udf2b\ufe0f',\n 'Haze': '\ud83c\udf2b\ufe0f',\n 'Smoke': '\ud83c\udf2b\ufe0f'\n };\n return emojiMap[weatherMain] || '\ud83c\udf24\ufe0f';\n}\n\nfunction getTemperatureEmoji(temp) {\n if (temp >= 30) return '\ud83d\udd25';\n if (temp >= 25) return '\ud83d\ude0e';\n if (temp >= 20) return '\ud83d\ude0a';\n if (temp >= 15) return '\ud83d\ude10';\n if (temp >= 10) return '\ud83e\udde5';\n if (temp >= 5) return '\ud83e\udd76';\n return '\ud83e\uddca';\n}\n\nfunction getWindInfo(windSpeed, windDir = null) {\n let windEmoji = '\ud83d\udca8';\n let windText = '';\n \n if (windSpeed < 2) windText = 'Calm';\n else if (windSpeed < 6) windText = 'Light breeze';\n else if (windSpeed < 12) windText = 'Moderate breeze';\n else if (windSpeed < 20) windText = 'Strong breeze';\n else windText = 'Very windy';\n \n const windKmh = (windSpeed * 3.6).toFixed(1);\n return `${windEmoji} ${windText} (${windKmh} km/h)`;\n}\n\nfunction getHumidityLevel(humidity) {\n if (humidity >= 80) return '\ud83d\udca7 Very humid';\n if (humidity >= 60) return '\ud83d\udca7 Humid';\n if (humidity >= 40) return '\ud83d\udca7 Comfortable';\n return '\ud83c\udfdc\ufe0f Dry';\n}\n\nfunction getUVAdvice(hour) {\n // Estimate UV based on time (simplified)\n if (hour >= 10 && hour <= 16) return '\u2600\ufe0f Consider sun protection';\n return '';\n}\n\nfunction formatDateTime(dtText) {\n const date = new Date(dtText);\n const timeStr = date.toLocaleTimeString('en-GB', { \n hour: '2-digit', \n minute: '2-digit',\n timeZone: 'Europe/Luxembourg'\n });\n return timeStr;\n}\n\n// Main processing\nconst forecast = items[0].json.list;\nconst cityName = items[0].json.city?.name || 'Strassen';\nconst country = items[0].json.city?.country || 'LU';\nconst timezone = items[0].json.city?.timezone || 0;\n\nconst now = new Date();\nconst today = now.toISOString().split('T')[0];\nconst desiredHours = [\"09:00\", \"12:00\", \"15:00\", \"18:00\", \"21:00\"];\n\nlet summary = '';\nlet headerIcon = '\ud83c\udf24\ufe0f';\nlet dayHighTemp = -999;\nlet dayLowTemp = 999;\nlet totalRain = 0;\nlet avgHumidity = 0;\nlet avgWindSpeed = 0;\nlet weatherCount = 0;\n\n// Process today's forecast\nlet firstMatch = true;\nforecast.forEach(item => {\n const dt = item.dt_txt;\n const [date, time] = dt.split(' ');\n const hour = time.slice(0, 5);\n const hourNum = parseInt(time.slice(0, 2));\n\n if (date === today && desiredHours.includes(hour)) {\n const temp = Math.round(item.main.temp);\n const feelsLike = Math.round(item.main.feels_like);\n const humidity = item.main.humidity;\n const windSpeed = item.wind?.speed || 0;\n const rain = item.rain?.['3h'] || 0;\n const clouds = item.clouds?.all || 0;\n const weatherMain = item.weather?.[0]?.main || '';\n const weatherDesc = item.weather?.[0]?.description || '';\n const isNight = hourNum < 6 || hourNum > 20;\n\n // Track daily stats\n dayHighTemp = Math.max(dayHighTemp, temp);\n dayLowTemp = Math.min(dayLowTemp, temp);\n totalRain += rain;\n avgHumidity += humidity;\n avgWindSpeed += windSpeed;\n weatherCount++;\n\n // Set header icon based on first matching hour\n if (firstMatch && weatherMain) {\n headerIcon = getWeatherEmoji(weatherMain, clouds, isNight);\n firstMatch = false;\n }\n\n const tempEmoji = getTemperatureEmoji(temp);\n const weatherEmoji = getWeatherEmoji(weatherMain, clouds, isNight);\n const feelsLikeText = Math.abs(temp - feelsLike) > 2 ? ` (feels ${feelsLike}\u00b0C)` : '';\n const rainText = rain > 0 ? `, \ud83c\udf27\ufe0f ${rain}mm` : '';\n const windText = windSpeed > 3 ? `, ${getWindInfo(windSpeed).split(' ')[0]} ${(windSpeed * 3.6).toFixed(0)} km/h` : '';\n const humidityText = humidity > 80 || humidity < 30 ? `, \ud83d\udca7 ${humidity}%` : '';\n const uvText = getUVAdvice(hourNum);\n\n summary += `\ud83d\udd52 <b>${hour}</b> ${weatherEmoji} ${temp}\u00b0C${feelsLikeText}${rainText}${windText}${humidityText}\\n`;\n if (uvText) summary += ` ${uvText}\\n`;\n }\n});\n\n// Calculate averages\nif (weatherCount > 0) {\n avgHumidity = Math.round(avgHumidity / weatherCount);\n avgWindSpeed = avgWindSpeed / weatherCount;\n}\n\n// Build comprehensive message\nlet message = `${headerIcon} <b>Weather Forecast for ${cityName}, ${country}</b>\\n`;\nmessage += `\ud83d\udcc5 ${now.toLocaleDateString('en-GB', { \n weekday: 'long', \n year: 'numeric', \n month: 'long', \n day: 'numeric',\n timeZone: 'Europe/Luxembourg'\n})}\\n\\n`;\n\n// Daily overview\nmessage += `<b>\ud83d\udcca Daily Overview</b>\\n`;\nif (dayHighTemp > -999 && dayLowTemp < 999) {\n message += `\ud83c\udf21\ufe0f Range: ${dayLowTemp}\u00b0C - ${dayHighTemp}\u00b0C\\n`;\n}\nif (totalRain > 0) {\n message += `\ud83c\udf27\ufe0f Expected rain: ${totalRain.toFixed(1)}mm\\n`;\n}\nif (avgWindSpeed > 2) {\n message += `${getWindInfo(avgWindSpeed)}\\n`;\n}\nmessage += `${getHumidityLevel(avgHumidity)} (${avgHumidity}%)\\n\\n`;\n\n// Hourly forecast\nmessage += `<b>\u23f0 Hourly Forecast</b>\\n`;\nmessage += summary;\n\n// Smart recommendations - only show section if there are recommendations\nlet recommendations = '';\nif (totalRain > 0) {\n recommendations += `\u2602\ufe0f Bring an umbrella or raincoat\\n`;\n}\nif (dayLowTemp < 10) {\n recommendations += `\ud83e\udde5 Dress warmly (below 10\u00b0C expected)\\n`;\n}\nif (dayHighTemp > 25) {\n recommendations += `\ud83d\udca7 Stay hydrated and consider sun protection\\n`;\n}\nif (avgWindSpeed > 15) {\n recommendations += `\ud83d\udca8 Windy conditions - secure loose items\\n`;\n}\nif (avgHumidity > 80) {\n recommendations += `\ud83d\udca6 High humidity - may feel warmer than actual temperature\\n`;\n}\n\n// Only add recommendations section if there are any recommendations\nif (recommendations) {\n message += `\\n<b>\ud83d\udca1 Recommendations</b>\\n`;\n message += recommendations;\n}\n\n// Footer with data source and time\nconst updateTime = now.toLocaleTimeString('en-GB', {\n hour: '2-digit',\n minute: '2-digit',\n timeZone: 'Europe/Luxembourg'\n});\nmessage += `\\n<i>\ud83d\udce1 Data from OpenWeatherMap | Updated: ${updateTime} CET</i>`;\n\nreturn [{ json: { message: message } }];"
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "c620d227-2228-4473-a83f-7de21b02ba8c",
"name": "Send Weather Update",
"type": "n8n-nodes-base.telegram",
"notes": "Sends formatted weather forecast to Telegram with HTML formatting",
"position": [
1000,
-300
],
"parameters": {
"text": "={{$json[\"message\"]}}",
"chatId": "123456789",
"additionalFields": {
"parse_mode": "HTML",
"disable_web_page_preview": true
}
},
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "fad24450-beef-4da4-9f56-e6659e06d06d",
"name": "Schedule Setup",
"type": "n8n-nodes-base.stickyNote",
"position": [
-240,
-120
],
"parameters": {
"width": 300,
"height": 520,
"content": "### \u23f0 Schedule Configuration\n\n**Current Settings:**\n- **Time**: 7:50 AM daily\n- **Timezone**: System timezone (usually UTC in n8n cloud)\n\n**\ud83d\udca1 Tips:**\n- For Luxembourg time, consider UTC offset\n- Winter: UTC+1 (CET)\n- Summer: UTC+2 (CEST)\n- Adjust trigger time accordingly\n\n**\ud83d\udd27 To Change:**\n1. Click on Schedule Trigger node\n2. Modify `triggerAtHour` and `triggerAtMinute`\n3. Add multiple schedules if needed"
},
"typeVersion": 1
},
{
"id": "153eed2e-091b-4045-80c9-4c12fe11beaf",
"name": "API Configuration",
"type": "n8n-nodes-base.stickyNote",
"position": [
120,
-120
],
"parameters": {
"color": 3,
"width": 340,
"height": 520,
"content": "### \ud83c\udf10 OpenWeatherMap API\n\n**\ud83d\udd11 API Key Setup:**\n1. Sign up at [OpenWeatherMap](https://openweathermap.org/api)\n2. Get your free API key\n3. Replace the key in the URL parameter\n\n**\ud83d\udccd Location Settings:**\n- Current: `q=Strassen` (Luxembourg)\n- Format: `q=CityName,CountryCode`\n- Examples: `q=Paris,FR` or `q=London,GB`\n\n**\ud83d\udcca API Features:**\n- 5-day forecast with 3-hour intervals\n- Metric units (Celsius, km/h)\n- Includes: temp, humidity, wind, rain, clouds\n\n**\u26a0\ufe0f Rate Limits:**\n- Free tier: 60 calls/minute, 1000 calls/day\n- This workflow uses 1 call per day"
},
"typeVersion": 1
},
{
"id": "764cbb6e-db67-4128-8f55-936403f4c07f",
"name": "Processing Features",
"type": "n8n-nodes-base.stickyNote",
"position": [
500,
-120
],
"parameters": {
"color": 5,
"width": 340,
"height": 520,
"content": "### \ud83d\udcbb Code Node Features\n\n**\ud83d\udd27 Enhanced Processing:**\n- **Smart Emojis**: Weather + temperature-based icons\n- **Comprehensive Data**: Feels-like temp, wind, humidity\n- **Daily Stats**: High/low temps, total rainfall\n- **Time-Aware**: Night/day emoji variations\n- **Recommendations**: Context-aware advice\n\n**\ud83d\udcca Calculated Metrics:**\n- Temperature ranges and averages\n- Total expected rainfall\n- Wind speed in km/h\n- Humidity comfort levels\n- UV protection recommendations\n\n**\ud83c\udfa8 Formatting:**\n- HTML bold/italic formatting\n- Structured sections with headers\n- Emoji-based visual indicators\n- Timezone-aware timestamps"
},
"typeVersion": 1
},
{
"id": "56f717ee-2231-4ca7-b596-a32e9defd1f6",
"name": "Telegram Setup",
"type": "n8n-nodes-base.stickyNote",
"position": [
900,
-120
],
"parameters": {
"width": 340,
"height": 520,
"content": "### \ud83d\udcf1 Telegram Setup\n\n**\ud83e\udd16 Bot Creation:**\n1. Message [@BotFather](https://t.me/BotFather) on Telegram\n2. Send `/newbot` command\n3. Choose bot name and username\n4. Copy the API token\n\n**\ud83c\udd94 Chat ID Setup:**\n1. Add bot to your group/channel OR\n2. Start private chat with bot\n3. Send a message to the bot\n4. Visit: `https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates`\n5. Find your chat ID in the response\n6. Update the `chatId` parameter\n\n**\u2699\ufe0f Message Settings:**\n- **HTML parsing** enabled for formatting\n- **Web preview** disabled to keep messages clean\n- Supports emojis and rich text"
},
"typeVersion": 1
},
{
"id": "ae5a111b-8be6-4260-abb4-66093c757e35",
"name": "Setup Checklist",
"type": "n8n-nodes-base.stickyNote",
"position": [
1280,
-560
],
"parameters": {
"color": 7,
"width": 400,
"height": 640,
"content": "### \ud83d\ude80 Quick Setup Checklist\n\n**\u2705 Essential Steps:**\n1. **OpenWeatherMap API Key**\n - Sign up and get free API key\n - Replace in HTTP Request URL\n\n2. **Telegram Bot**\n - Create bot via @BotFather\n - Add bot token to credentials\n - Update chatId in Telegram node\n\n3. **Test the Workflow**\n - Execute manually first\n - Check Telegram for message\n - Activate for daily runs\n\n**\ud83d\udd27 Optional Customizations:**\n- Change city in HTTP URL\n- Adjust schedule timing\n- Modify forecast hours in code\n- Add more weather parameters\n\n**\ud83d\udccb Monitoring:**\n- Check execution history\n- Monitor API usage\n- Verify daily messages"
},
"typeVersion": 1
},
{
"id": "28c828bc-5b66-4e59-81f7-d941088cbbc6",
"name": "Weather Bot Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-260,
-900
],
"parameters": {
"color": 6,
"width": 900,
"height": 500,
"content": "## \ud83c\udf24\ufe0f Daily Weather Forecast Bot\n\n### \ud83d\udccb What It Does\nFetches detailed weather forecast from OpenWeatherMap API for **Strassen, Luxembourg** and sends a comprehensive daily weather summary to Telegram every morning at **7:50 AM**.\n\n---\n### \ud83d\udd0d Features\n- **\ud83d\udcca Daily Overview**: Temperature range, total rainfall, wind conditions\n- **\u23f0 Hourly Forecast**: Weather at key times (9AM, 12PM, 3PM, 6PM, 9PM)\n- **\ud83c\udf21\ufe0f Smart Emojis**: Weather-based icons and temperature indicators\n- **\ud83d\udca1 Recommendations**: Contextual advice (umbrella, warm clothes, sun protection)\n- **\ud83c\udf2a\ufe0f Enhanced Details**: Feels-like temperature, humidity, wind speed, UV warnings\n- **\ud83d\udcf1 Rich Formatting**: HTML-formatted messages for better readability\n\n---\n### \ud83d\udee0\ufe0f Technical Details\n- **API**: OpenWeatherMap 5-day forecast (3-hour intervals)\n- **Location**: Strassen, Luxembourg (configurable in HTTP node)\n- **Timezone**: Europe/Luxembourg (CET/CEST)\n- **Format**: HTML with emojis and formatting\n- **Error Handling**: 3 retry attempts with 5-second delays\n\n---\n### \u2699\ufe0f Configuration\n1. **API Key**: Replace `dsqdsqdsqdsqsdq` with your OpenWeatherMap API key\n2. **Location**: Change `q=Strassen` in the HTTP request URL for different cities\n3. **Schedule**: Modify trigger time in Schedule node\n4. **Telegram**: Update `chatId` with your chat/group ID\n\n---\n\u2705 **Ready to use!** Just update the API key and chat ID."
},
"typeVersion": 1
}
],
"connections": {
"Daily Morning Trigger": {
"main": [
[
{
"node": "OpenWeather API Request",
"type": "main",
"index": 0
}
]
]
},
"Weather Data Processor": {
"main": [
[
{
"node": "Send Weather Update",
"type": "main",
"index": 0
}
]
]
},
"OpenWeather API Request": {
"main": [
[
{
"node": "Weather Data Processor",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
telegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
A comprehensive n8n workflow that fetches detailed weather forecasts from OpenWeatherMap and sends beautifully formatted daily summaries to Telegram. 📊 Daily Overview: Complete temperature range, rainfall totals, and wind conditions ⏰ Hourly Forecast: Weather predictions at key…
Source: https://n8n.io/workflows/4608/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
GNCA AI News Pipeline. Uses rssFeedRead, httpRequest, telegram, errorTrigger. Scheduled trigger; 29 nodes.
This workflow automates plant care reminders and records using Google Sheets, Telegram, and OpenWeather API.
Apollo Data Enrichment Using Company Id to automatically finds contacts for companies listed in your Google Sheet, enriches each person with emails and phone numbers via Apollo’s API, and writes verif
MindFrame Psychology - FREE Complete Workflow. Uses httpRequest, googleDrive, telegram. Scheduled trigger; 25 nodes.
++Download the google sheet here++ and replace this with the googles sheet node: Google sheet , upload to google sheets and replace in the google sheets node. Scheduled trigger: Runs once a day at 8 A