This workflow corresponds to n8n.io template #7997 — 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": "m1",
"name": "When clicking \u2018Execute workflow\u2019",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-520,
-160
],
"parameters": {},
"typeVersion": 1
},
{
"id": "note_overview",
"name": "Sticky Note \u2014 Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-540,
-360
],
"parameters": {
"color": 6,
"width": 860,
"height": 260,
"content": "## Crypto RSI Alert Bot (overview)\n- Runs on a schedule or manual trigger.\n- Iterates a **watchlist** (BTC/ETH/SOL).\n- Fetches **intraday 1h** OHLCV from **EODHD** for each symbol.\n- Code node computes **Wilder's RSI(14)** and detects **30/70** crossings.\n- On signal, sends a **Telegram** alert (HTML) + **View chart** button (TradingView BINANCE/USD).\n\nEnv vars required:\n- `EODHD_TOKEN`\n- `TELEGRAM_CHAT_ID`"
},
"typeVersion": 1
},
{
"id": "set1",
"name": "Edit Fields (watchlist)",
"type": "n8n-nodes-base.set",
"position": [
-300,
-160
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "sym_arr",
"name": "symbol",
"type": "array",
"value": "[\"BTC-USD.CC\",\"ETH-USD.CC\",\"SOL-USD.CC\"]"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "note_watchlist",
"name": "Sticky Note \u2014 Watchlist",
"type": "n8n-nodes-base.stickyNote",
"position": [
-320,
-300
],
"parameters": {
"color": 6,
"width": 560,
"height": 160,
"content": "### Edit Fields (watchlist)\n- Defines the **symbol array**.\n- Make sure the field type is **Array** (String[]), not a single String.\n- Example output: `{ symbol: [\"BTC-USD.CC\",\"ETH-USD.CC\",\"SOL-USD.CC\"] }`"
},
"typeVersion": 1
},
{
"id": "split_out",
"name": "Split Out",
"type": "n8n-nodes-base.splitOut",
"position": [
-80,
-160
],
"parameters": {
"options": {},
"fieldToSplitOut": "symbol"
},
"typeVersion": 1
},
{
"id": "note_split",
"name": "Sticky Note \u2014 Split Out",
"type": "n8n-nodes-base.stickyNote",
"position": [
-100,
-300
],
"parameters": {
"color": 6,
"width": 520,
"height": 150,
"content": "### Split Out\n- Explodes the array into **one item per symbol**.\n- Input: 1 item with array \u2192 Output: N items like `{ symbol: \"BTC-USD.CC\" }`."
},
"typeVersion": 1
},
{
"id": "loop",
"name": "Loop Over Items",
"type": "n8n-nodes-base.splitInBatches",
"position": [
140,
-160
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "note_loop",
"name": "Sticky Note \u2014 Loop",
"type": "n8n-nodes-base.stickyNote",
"position": [
120,
-300
],
"parameters": {
"color": 6,
"width": 560,
"height": 120,
"content": "### Loop Over Items\n- Processes **one symbol per pass** to avoid mixing BTC/ETH/SOL candles.\n- Wiring: **Loop \u2192 HTTP \u2192 Code \u2192 back to Loop**. **Done \u2192 IF**."
},
"typeVersion": 1
},
{
"id": "http",
"name": "HTTP Request (EODHD intraday 1h)",
"type": "n8n-nodes-base.httpRequest",
"position": [
360,
-260
],
"parameters": {
"url": "=https://eodhd.com/api/intraday/{{ $json.symbol }}",
"method": "GET",
"options": {
"redirect": {
"redirect": {}
},
"response": {
"response": {}
},
"splitIntoItems": true
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "interval",
"value": "1h"
},
{
"name": "fmt",
"value": "json"
},
{
"name": "api_token",
"value": "={{ $env.EODHD_TOKEN }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "note_http",
"name": "Sticky Note \u2014 HTTP",
"type": "n8n-nodes-base.stickyNote",
"position": [
340,
-420
],
"parameters": {
"color": 6,
"width": 560,
"height": 160,
"content": "### HTTP (EODHD)\n- Fetches **intraday 1h OHLCV** for current symbol.\n- Token via env var `EODHD_TOKEN` \u2192 no secret in JSON.\n- **Split Into Items** enabled: 1 candle = 1 item (~2\u20133k items)."
},
"typeVersion": 1
},
{
"id": "code",
"name": "Code (RSI + message)",
"type": "n8n-nodes-base.code",
"position": [
580,
-260
],
"parameters": {
"jsCode": "// n8n Code node \u2014 Run Once for All Items\\n// Computes RSI(14) (Wilder) on 1h candles and raises 30/70 cross alerts.\\n\\nconst PERIOD = 14;\\nconst OVERBOUGHT = 70;\\nconst OVERSOLD = 30;\\n\\n// Collect ALL candles from the HTTP node (1 item = 1 candle)\\nconst inputItems = $input.all();\\nlet candles = [];\\nfor (const it of inputItems) {\\n if (Array.isArray(it.json)) candles.push(...it.json);\\n else candles.push(it.json);\\n}\\n\\n// Normalize + sort by time (prefer numeric timestamp, fallback to datetime)\\ncandles = candles\\n .filter(r => r && r.close !== undefined)\\n .map(r => ({\\n t: (Number.isFinite(+r.timestamp) ? +r.timestamp\\n : (typeof r.datetime === 'number' ? r.datetime : Date.parse(r.datetime)/1000)),\\n close: +r.close\\n }))\\n .filter(r => Number.isFinite(r.t) && Number.isFinite(r.close))\\n .sort((a,b) => a.t - b.t);\\n\\nif (candles.length < PERIOD + 2) {\\n return [{ json: { error: 'Not enough candles for RSI', count: candles.length } }];\\n}\\n\\nconst closes = candles.map(c => c.close);\\n\\n// Wilder RSI (full series)\\nfunction rsiSeries(values, period = 14) {\\n const deltas = [];\\n for (let i = 1; i < values.length; i++) deltas.push(values[i] - values[i - 1]);\\n let gain = 0, loss = 0;\\n for (let i = 0; i < period; i++) { const d = deltas[i]; if (d >= 0) gain += d; else loss -= d; }\\n let avgGain = gain / period; let avgLoss = loss / period;\\n const rsis = new Array(values.length).fill(null);\\n rsis[period] = avgLoss === 0 ? 100 : 100 - (100 / (1 + (avgGain / avgLoss)));\\n for (let i = period + 1; i < values.length; i++) {\\n const d = deltas[i - 1];\\n const up = Math.max(d, 0);\\n const down = Math.max(-d, 0);\\n avgGain = ((avgGain * (period - 1)) + up) / period;\\n avgLoss = ((avgLoss * (period - 1)) + down) / period;\\n const rs = avgLoss === 0 ? Infinity : (avgGain / avgLoss);\\n rsis[i] = 100 - (100 / (1 + rs));\\n }\\n return rsis;\\n}\\n\\nconst rsis = rsiSeries(closes, PERIOD);\\nconst lastIdx = rsis.length - 1;\\nconst rsiNow = +rsis[lastIdx].toFixed(1);\\nconst rsiPrev = +rsis[lastIdx - 1].toFixed(1);\\nconst lastClose = +closes[lastIdx].toFixed(2);\\nconst lastTs = candles[lastIdx].t;\\n\\n// Signals\\nlet signal = null;\\nif (rsiPrev > OVERSOLD && rsiNow <= OVERSOLD) signal = 'enter_oversold';\\nelse if (rsiPrev < OVERBOUGHT && rsiNow >= OVERBOUGHT) signal = 'enter_overbought';\\nelse if (rsiPrev <= OVERSOLD && rsiNow > OVERSOLD) signal = 'exit_oversold';\\nelse if (rsiPrev >= OVERBOUGHT && rsiNow < OVERBOUGHT) signal = 'exit_overbought';\\n\\n// ======= TEST TOGGLE (set true only to test Telegram delivery) =======\\nconst FORCE_ALERT = false; // keep false in production\\nconst FORCE_SIGNAL = 'enter_overbought'; // 'enter_oversold' | 'exit_oversold' | 'exit_overbought'\\nif (FORCE_ALERT) signal = FORCE_SIGNAL;\\n// YOUR_AWS_SECRET_KEY_HERE=============================\\n\\n// Current symbol from the loop item (fallback to input $json)\\nconst symbol = $('Split Out')?.item?.json?.symbol || $json.symbol || 'UNKNOWN';\\n\\nconst TF = '1h';\\nconst fmt = (n, d=2) => Number(n).toLocaleString('en-US',{minimumFractionDigits:d, maximumFractionDigits:d});\\nconst tsUTC = (ts) => new Date(ts*1000).toISOString().replace('T',' ').slice(0,16) + ' UTC';\\n\\nlet emoji = '\ud83d\udd14', headline = '';\\nif (signal === 'enter_oversold') { emoji='\ud83d\udd3b'; headline = `enters <u>oversold</u> (RSI ${rsiNow} \u2264 30)`; }\\nelse if (signal === 'enter_overbought') { emoji='\ud83d\ude80'; headline = `enters <u>overbought</u> (RSI ${rsiNow} \u2265 70)`; }\\nelse if (signal === 'exit_oversold') { emoji='\u2705'; headline = `exits <u>oversold</u> (RSI ${rsiNow})`; }\\nelse if (signal === 'exit_overbought') { emoji='\u2705'; headline = `exits <u>overbought</u> (RSI ${rsiNow})`; }\\n\\nconst alertTextHtml = signal ? (\\n `${emoji} <b>${symbol}</b> ${headline}\\n` +\\n `Price: <b>$${fmt(lastClose)}</b> \u00b7 TF: <b>${TF}</b> \u00b7 ${tsUTC(lastTs)}\\n` +\\n `RSI: <b>${rsiPrev} \u2192 ${rsiNow}</b> (30/70)\\n` +\\n `\u2014 <i>RSI Heatwave</i>`\\n) : null;\\n\\nconst alertText = signal ? `${symbol} | ${headline.replace(/<[^>]*>/g,'')} | Price $${fmt(lastClose)} \u00b7 TF ${TF} \u00b7 ${tsUTC(lastTs)} | RSI ${rsiPrev}\u2192${rsiNow}` : null;\\n\\n// TradingView link (BINANCE + USD) from EODHD symbol (e.g., BTC-USD.CC \u2192 BTCUSD)\\nconst rawSymbol = $('Split Out')?.item?.json?.symbol ?? $json.symbol ?? symbol;\\nconst sym = Array.isArray(rawSymbol) ? rawSymbol[0] : rawSymbol; \\nconst base = String(sym).split('-')[0].toUpperCase();\\nconst tradingViewUrl = `https://www.tradingview.com/symbols/${base}USD/?exchange=BINANCE`;\\n\\nreturn [{ json: { symbol, rsi: rsiNow, rsiPrev, period: PERIOD, lastClose, signal, timestamp: lastTs, alertText, alertTextHtml, tradingViewUrl } }];"
},
"typeVersion": 2
},
{
"id": "note_code",
"name": "Sticky Note \u2014 Code",
"type": "n8n-nodes-base.stickyNote",
"position": [
560,
-420
],
"parameters": {
"color": 6,
"width": 560,
"height": 170,
"content": "### Code (RSI + message)\n- Sorts candles, computes **RSI(14)** (Wilder), detects 30/70 crossings.\n- Builds HTML message + TradingView URL (BINANCE/USD).\n- Testing: set `FORCE_ALERT = true`, then back to `false`."
},
"typeVersion": 1
},
{
"id": "if",
"name": "IF (has signal?)",
"type": "n8n-nodes-base.if",
"position": [
140,
40
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond1",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.signal }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "tg",
"name": "Send a text message",
"type": "n8n-nodes-base.telegram",
"position": [
360,
40
],
"parameters": {
"text": "={{ $json.alertTextHtml }}",
"chatId": "={{ $env.TELEGRAM_CHAT_ID }}",
"replyMarkup": "inlineKeyboard",
"inlineKeyboard": {
"rows": [
{
"row": {
"buttons": [
{
"text": "View chart",
"additionalFields": {
"url": "={{ $json.tradingViewUrl }}"
}
}
]
}
}
]
},
"additionalFields": {
"parse_mode": "HTML",
"disable_web_page_preview": true
}
},
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "note_tg",
"name": "Sticky Note \u2014 Telegram",
"type": "n8n-nodes-base.stickyNote",
"position": [
340,
180
],
"parameters": {
"color": 6,
"width": 560,
"height": 140,
"content": "### Telegram (delivery)\n- Parse Mode: **HTML**.\n- Text: `{{$json.alertTextHtml}}`.\n- Button: **View chart** \u2192 `{{$json.tradingViewUrl}}`.\n- Chat ID via env var `TELEGRAM_CHAT_ID`.\n- Bot token stays in Credentials."
},
"typeVersion": 1
}
],
"connections": {
"Split Out": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items": {
"main": [
[
{
"node": "IF (has signal?)",
"type": "main",
"index": 0
}
],
[
{
"node": "HTTP Request (EODHD intraday 1h)",
"type": "main",
"index": 0
}
]
]
},
"IF (has signal?)": {
"main": [
[
{
"node": "Send a text message",
"type": "main",
"index": 0
}
]
]
},
"Code (RSI + message)": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields (watchlist)": {
"main": [
[
{
"node": "Split Out",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request (EODHD intraday 1h)": {
"main": [
[
{
"node": "Code (RSI + message)",
"type": "main",
"index": 0
}
]
]
},
"When clicking \u2018Execute workflow\u2019": {
"main": [
[
{
"node": "Edit Fields (watchlist)",
"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
How it works
Source: https://n8n.io/workflows/7997/ — 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.
This n8n workflow automates the process of scraping Google Play Store reviews, analyzing app performance, and sending alerts for low-rated applications. It integrates with Bright Data for web scraping
⚠️ Heads up: this is satire. The "Hell Yeah!" workflow is a parody of "automate your whole life with AI agents" grindset content. The API endpoints are fictional and the function nodes are illustrativ
This n8n workflow provides automated monitoring of Public Key Infrastructure (PKI) components including CA certificates, Certificate Revocation Lists (CRLs), and associated web services. It extracts c
This workflow continuously monitors the Meta Ads Library for new creatives from a specific competitor pages, logs them into Google Sheets, and sends a concise Telegram notification with the number of
Track all n8n workflow failures with automatic error capture, severity classification, duplicate detection, Slack alerting, performance metrics, and log retention.