This workflow corresponds to n8n.io template #15515 — we link there as the canonical source.
This workflow follows the Agent → Gmail 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 →
{
"id": "4Z7beGBJ3MSo6W4X",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Risk Alert Generator",
"tags": [],
"nodes": [
{
"id": "709245f2-e7a2-4627-accb-ab6e9408cf95",
"name": "Read Portfolio from Google Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
208,
0
],
"parameters": {
"range": "Portfolio!A:E",
"options": {},
"sheetId": "1KA65Fzm1uP4kP8UiCN7rlQxUmcCv5ESxhivfxfDui2s"
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "fd7fd453-1605-40eb-b211-bd33b1c0cb18",
"name": "Edit Fields \u2013 Config & Thresholds",
"type": "n8n-nodes-base.set",
"position": [
400,
0
],
"parameters": {
"values": {
"number": [
{
"name": "drop_threshold",
"value": -5
},
{
"name": "volatility_threshold",
"value": 3
}
],
"boolean": [
{
"name": "alert_enabled",
"value": true
}
]
},
"options": {}
},
"typeVersion": 2
},
{
"id": "bfd44777-cb5a-4b27-aea9-ce0cb544d2de",
"name": "Filter \u2013 Only Flagged Risks",
"type": "n8n-nodes-base.if",
"position": [
1200,
-208
],
"parameters": {
"conditions": {
"string": [
{
"value1": "={{$json.risks.length}}",
"value2": "0",
"operation": "notEqual"
}
]
}
},
"typeVersion": 1
},
{
"id": "4c93a63b-bfe3-474e-93c0-d9de5f17ccf1",
"name": "When clicking \u2018Execute workflow\u2019",
"type": "n8n-nodes-base.manualTrigger",
"position": [
0,
0
],
"parameters": {},
"typeVersion": 1
},
{
"id": "89ec7fd7-41a1-42e6-875d-41d3767b4124",
"name": "Send Risk Alert Notification",
"type": "n8n-nodes-base.gmail",
"position": [
1872,
-224
],
"parameters": {
"sendTo": "",
"message": "={{ $json.output }}",
"options": {},
"subject": "Daily Stock Risk Alert"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "ca20c72d-0721-41ef-8209-c88871391e81",
"name": "Wait 8 Seconds Before request",
"type": "n8n-nodes-base.wait",
"position": [
784,
176
],
"parameters": {
"amount": 8
},
"typeVersion": 1.1
},
{
"id": "d3915664-c776-4817-b42f-9980bd7bc1ce",
"name": "Compute Risk Logic",
"type": "n8n-nodes-base.function",
"position": [
992,
-208
],
"parameters": {
"functionCode": "// First, collect all input items from the previous node\nconst allItems = $items(); // $items() gives all input items as an array\n\n// Separate stocks (first half) and prices (second half)\nconst stocks = allItems.slice(0, 5).map(item => item.json);\nconst prices = allItems.slice(5, 10).map(item => parseFloat(item.json.price));\n\n// Combine stock with its corresponding price\nreturn stocks.map((stock, i) => {\n const currentPrice = prices[i];\n const avgPrice = parseFloat(stock.avg_price);\n const changePercent = ((currentPrice - avgPrice) / avgPrice) * 100;\n const volatility = Math.abs(changePercent);\n\n const risks = [];\n if (changePercent <= stock.drop_threshold) risks.push(\"Large Price Drop\");\n if (volatility >= stock.volatility_threshold) risks.push(\"High Volatility\");\n\n return {\n json: {\n ...stock,\n current_price: currentPrice,\n change_percent: changePercent.toFixed(2),\n volatility: volatility.toFixed(2),\n risks\n }\n };\n});"
},
"typeVersion": 1
},
{
"id": "874debab-4fbd-4685-9ad4-e5163c15b93f",
"name": "Generate Risk Summary",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
1584,
-224
],
"parameters": {
"text": "=You are a stock risk assistant. \n\nBelow is the list of flagged stocks, if any. Generate a **clear, concise plain-text summary** for an email alert. Use natural sentences, not just raw tables or code. Include only relevant info: symbol, current price, average price, change %, volatility %, quantity, sector, notes and risks.\n\n- If there are no flagged stocks, output: \"No stocks meet the risk criteria today. No email alert is necessary.\"\n- Otherwise, generate a **single, readable paragraph per stock**.\n- Do not use Markdown, symbols like ** or _ or HTML.\n\nFlagged Stocks:\n{{$json.output}}",
"options": {},
"promptType": "define"
},
"typeVersion": 3.1
},
{
"id": "16fec3d5-8fe4-49ff-aedb-2473ac806ee7",
"name": "Groq Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGroq",
"position": [
1584,
-32
],
"parameters": {
"model": "openai/gpt-oss-120b",
"options": {}
},
"credentials": {
"groqApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "2e78d5d2-fe9d-43dd-aa2a-b3a1c5446f2d",
"name": "Collect Flagged Stocks",
"type": "n8n-nodes-base.code",
"position": [
1392,
-224
],
"parameters": {
"jsCode": "// Collect all input items\nconst allItems = $items(); // gives array of all input items\n\n// Map over items to format each stock\nconst flaggedStocksText = allItems.map(item => {\n const stock = item.json;\n return `Symbol: ${stock.symbol}\nCurrent Price: ${stock.current_price}\nAverage Price: ${stock.avg_price}\nChange Percent: ${stock.change_percent}%\nVolatility: ${stock.volatility}%\nQuantity: ${stock.quantity}\nSector: ${stock.sector}\nNotes: ${stock.notes}\nRisks: ${stock.risks.join(\", \")}`;\n}).join(\"\\n\\n\"); // double newline for readability\n\n// Return as single output\nreturn [{ json: { output: flaggedStocksText } }];"
},
"typeVersion": 2
},
{
"id": "8a3c1984-5e31-4c24-bb56-2de9a1394901",
"name": "Loop: Fetch Current Prices",
"type": "n8n-nodes-base.splitInBatches",
"position": [
608,
0
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "ffe6e92a-4b02-4b51-bbbf-9d5afb9ff726",
"name": "Fetch Stock Prices",
"type": "n8n-nodes-base.httpRequest",
"position": [
992,
176
],
"parameters": {
"url": "=https://api.twelvedata.com/price?symbol={{$json.symbol}}",
"options": {}
},
"typeVersion": 4.4
},
{
"id": "79f13e81-092d-4be1-9018-b388a45847a8",
"name": "Merge Portfolio & Prices",
"type": "n8n-nodes-base.merge",
"position": [
784,
-208
],
"parameters": {},
"typeVersion": 3.2
},
{
"id": "f63ddc9e-8294-404a-ab1f-1e6515e6a782",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-304,
-720
],
"parameters": {
"width": 368,
"height": 416,
"content": "## Risk Alert Generator\n\n### How It Works:\nThis workflow reads your stock portfolio from Google Sheets, fetches current prices via API, calculates risk metrics (price drops & volatility), identifies flagged stocks and generates a concise AI-written summary. Only flagged stocks trigger an email alert, keeping notifications relevant.\n\n### Setup Steps:\n1. Connect your Google Sheets account and specify the portfolio sheet and range.\n2. Connect your Gmail account to send alerts.\n3. Configure alert thresholds in the Set Config & Thresholds node.\n4. Provide a stock price API key in the HTTP Request node.\n5. Test by executing the workflow manually or schedule it as needed."
},
"typeVersion": 1
},
{
"id": "fb047815-2223-4df3-ae6f-56c4de0ba3f7",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
144,
-416
],
"parameters": {
"color": 7,
"width": 384,
"height": 816,
"content": "## Portfolio Input & Thresholds\nReads portfolio data from Google Sheets, adds configuration parameters like drop thresholds and volatility limits and enables/disables alerts. Prepares a structured dataset for the subsequent risk computation steps. Ensures that only relevant fields are passed downstream."
},
"typeVersion": 1
},
{
"id": "89d519d7-3f80-4c4e-a6b2-049d289c4552",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
544,
-416
],
"parameters": {
"color": 7,
"width": 608,
"height": 816,
"content": "## Fetch Prices & Compute Risks\nLoops through each stock, retrieves live prices via API with a small delay to respect rate limits, merges original portfolio data and calculates risk metrics (percentage change, volatility). Flags stocks exceeding thresholds, producing a clean dataset for flagged risk analysis."
},
"typeVersion": 1
},
{
"id": "ceb2a706-b5f1-448e-9556-7b048be5af2e",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1168,
-416
],
"parameters": {
"color": 7,
"width": 848,
"height": 816,
"content": "## Summarize & Notify\nFilters only stocks with flagged risks, formats them into a readable summary and passes the data to the AI node for generating plain-text email content. Sends email notifications only when risks exist, ensuring alerts are meaningful and avoiding unnecessary emails."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "21c59cee-37ad-4273-bf3e-2854448a59d0",
"connections": {
"Groq Chat Model": {
"ai_languageModel": [
[
{
"node": "Generate Risk Summary",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Compute Risk Logic": {
"main": [
[
{
"node": "Filter \u2013 Only Flagged Risks",
"type": "main",
"index": 0
}
]
]
},
"Fetch Stock Prices": {
"main": [
[
{
"node": "Loop: Fetch Current Prices",
"type": "main",
"index": 0
}
]
]
},
"Generate Risk Summary": {
"main": [
[
{
"node": "Send Risk Alert Notification",
"type": "main",
"index": 0
}
]
]
},
"Collect Flagged Stocks": {
"main": [
[
{
"node": "Generate Risk Summary",
"type": "main",
"index": 0
}
]
]
},
"Merge Portfolio & Prices": {
"main": [
[
{
"node": "Compute Risk Logic",
"type": "main",
"index": 0
}
]
]
},
"Loop: Fetch Current Prices": {
"main": [
[
{
"node": "Merge Portfolio & Prices",
"type": "main",
"index": 1
}
],
[
{
"node": "Wait 8 Seconds Before request",
"type": "main",
"index": 0
}
]
]
},
"Filter \u2013 Only Flagged Risks": {
"main": [
[
{
"node": "Collect Flagged Stocks",
"type": "main",
"index": 0
}
]
]
},
"Wait 8 Seconds Before request": {
"main": [
[
{
"node": "Fetch Stock Prices",
"type": "main",
"index": 0
}
]
]
},
"Read Portfolio from Google Sheets": {
"main": [
[
{
"node": "Edit Fields \u2013 Config & Thresholds",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields \u2013 Config & Thresholds": {
"main": [
[
{
"node": "Loop: Fetch Current Prices",
"type": "main",
"index": 0
},
{
"node": "Merge Portfolio & Prices",
"type": "main",
"index": 0
}
]
]
},
"When clicking \u2018Execute workflow\u2019": {
"main": [
[
{
"node": "Read Portfolio from Google Sheets",
"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.
gmailOAuth2googleSheetsOAuth2ApigroqApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
> Smart Stock Risk Alerts in Minutes
Source: https://n8n.io/workflows/15515/ — 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 your entire B2B outreach pipeline from lead discovery to personalized cold email delivery. Submit a form, let Apollo find and enrich your leads, review AI-generated emails
This workflow automates the tracking of stock market sector rotation. It fetches a list of active stocks from Google Sheets, pulls their last 5 days of market data from Yahoo Finance and calculates mo
This workflow is designed for marketers, content creators, agencies, and solo founders who want to publish long‑form posts with visuals on autopilot using n8n and AI agents.
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.
Want to skip the manual work and instantly generate SWOT analyses for your business plans, investor decks, or strategy docs? 🚀 This workflow lets you automate the entire SWOT (Strengths, Weaknesses, O