This workflow corresponds to n8n.io template #13696 — we link there as the canonical source.
This workflow follows the Emailsend → HTTP Request 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": "hAijyRcR9xXmda5H",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "AI Brand Reputation Crisis Detector",
"tags": [],
"nodes": [
{
"id": "930025e9-0425-48c5-9e61-489fe3c9d024",
"name": "Every 10 Minutes",
"type": "n8n-nodes-base.scheduleTrigger",
"notes": "Runs every 10 minutes for near real-time brand monitoring",
"position": [
96,
176
],
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 10
}
]
}
},
"notesInFlow": true,
"typeVersion": 1.2
},
{
"id": "d3467c72-9fac-47df-846e-471ac561367f",
"name": "Monitor Twitter/X",
"type": "n8n-nodes-base.twitter",
"notes": "Searches for brand mentions on Twitter/X",
"position": [
320,
-16
],
"parameters": {
"limit": 100,
"operation": "search",
"searchText": "={{ $json.brandName || 'YourBrandName' }} OR @{{ $json.brandHandle || 'yourbrand' }}",
"additionalFields": {}
},
"credentials": {
"twitterOAuth2Api": {
"name": "<your credential>"
}
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "1057b564-c8d2-4367-a0fe-9f7de0b14174",
"name": "Monitor Reddit",
"type": "n8n-nodes-base.reddit",
"notes": "Searches for brand mentions on Reddit",
"position": [
320,
176
],
"parameters": {
"limit": 50,
"keyword": "=",
"operation": "search",
"subreddit": "all",
"additionalFields": {}
},
"credentials": {
"redditOAuth2Api": {
"name": "<your credential>"
}
},
"notesInFlow": true,
"typeVersion": 1
},
{
"id": "60a65ed8-05b5-46a2-9944-3495d36fc8e1",
"name": "Monitor News & Blogs",
"type": "n8n-nodes-base.httpRequest",
"notes": "Searches news articles and blog posts via News API",
"position": [
320,
368
],
"parameters": {
"url": "=https://newsapi.org/v2/everything?q={{ $json.brandName || 'YourBrandName' }}&sortBy=publishedAt&pageSize=50&apiKey=YOUR_NEWS_API_KEY",
"options": {}
},
"notesInFlow": true,
"typeVersion": 4.2
},
{
"id": "97af41ff-80a7-406f-b483-e6b784bec436",
"name": "Normalize Data Format",
"type": "n8n-nodes-base.code",
"notes": "Converts all platform data to unified format",
"position": [
544,
176
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Normalize data from different platforms to unified format\nconst item = $input.item.json;\nlet normalized = {};\n\n// Detect platform and normalize\nif (item.tweet_id || item.id_str || item.text) {\n // Twitter/X format\n normalized = {\n platform: 'twitter',\n id: item.id_str || item.id,\n text: item.text || item.full_text || '',\n author: item.user?.screen_name || 'unknown',\n authorFollowers: item.user?.followers_count || 0,\n timestamp: item.created_at,\n url: `https://twitter.com/${item.user?.screen_name}/status/${item.id_str}`,\n engagement: (item.retweet_count || 0) + (item.favorite_count || 0),\n isVerified: item.user?.verified || false\n };\n} else if (item.subreddit || item.permalink) {\n // Reddit format\n normalized = {\n platform: 'reddit',\n id: item.id,\n text: `${item.title || ''} ${item.selftext || ''}`,\n author: item.author || 'unknown',\n authorFollowers: 0,\n timestamp: new Date(item.created_utc * 1000).toISOString(),\n url: `https://reddit.com${item.permalink}`,\n engagement: (item.score || 0) + (item.num_comments || 0),\n subreddit: item.subreddit,\n isVerified: false\n };\n} else if (item.title && item.source) {\n // News API format\n normalized = {\n platform: 'news',\n id: item.url,\n text: `${item.title || ''} ${item.description || ''}`,\n author: item.author || item.source?.name || 'unknown',\n authorFollowers: 0,\n timestamp: item.publishedAt,\n url: item.url,\n engagement: 0,\n source: item.source?.name,\n isVerified: true // News sources are considered verified\n };\n} else {\n // Unknown format - try to extract what we can\n normalized = {\n platform: 'unknown',\n id: item.id || Date.now().toString(),\n text: item.text || item.content || JSON.stringify(item),\n author: item.author || 'unknown',\n authorFollowers: 0,\n timestamp: item.timestamp || new Date().toISOString(),\n url: item.url || '',\n engagement: 0,\n isVerified: false\n };\n}\n\nnormalized.rawData = item;\n\nreturn { json: normalized };"
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "9931cce1-ef16-4334-bad5-55d5c445b0ff",
"name": "Remove Duplicates",
"type": "n8n-nodes-base.filter",
"notes": "Filters out already-analyzed mentions",
"position": [
768,
176
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": false,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "5e6f7g8h-9i0j-1k2l-3m4n-o5p6q7r8s9t0",
"operator": {
"type": "string",
"operation": "isNotEmpty"
},
"leftValue": "={{ $json.text }}"
},
{
"id": "6f7g8h9i-0j1k-2l3m-4n5o-p6q7r8s9t0u1",
"operator": {
"type": "string",
"operation": "isNotEmpty"
},
"leftValue": "={{ $json.id }}"
}
]
}
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "973eef39-a26c-48bc-8b16-c015021b3330",
"name": "Merge Platform Data",
"type": "n8n-nodes-base.merge",
"notes": "Combines mentions from all platforms",
"position": [
992,
176
],
"parameters": {},
"notesInFlow": true,
"typeVersion": 3
},
{
"id": "8562740d-60d9-44f3-80e0-81b41d134761",
"name": "Track Analyzed Mentions",
"type": "n8n-nodes-base.code",
"notes": "Prevents duplicate analysis using workflow static data",
"position": [
1216,
176
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Check if we've already analyzed this mention\nconst mentionId = $input.item.json.id;\nconst platform = $input.item.json.platform;\n\n// Create a unique key for this mention\nconst uniqueKey = `${platform}_${mentionId}`;\n\n// Use workflow static data to store analyzed mentions\nconst analyzedMentions = $getWorkflowStaticData('global').analyzedMentions || {};\n\nif (analyzedMentions[uniqueKey]) {\n // Already analyzed this mention\n return { json: { skip: true, reason: 'Already analyzed' } };\n}\n\n// Mark as analyzed\nanalyzedMentions[uniqueKey] = {\n timestamp: new Date().toISOString(),\n analyzed: true\n};\n\n// Clean up old entries (older than 7 days)\nconst sevenDaysAgo = Date.now() - (7 * 24 * 60 * 60 * 1000);\nfor (const key in analyzedMentions) {\n const entryTime = new Date(analyzedMentions[key].timestamp).getTime();\n if (entryTime < sevenDaysAgo) {\n delete analyzedMentions[key];\n }\n}\n\n// Save back to static data\n$getWorkflowStaticData('global').analyzedMentions = analyzedMentions;\n\nreturn { \n json: { \n skip: false, \n ...($input.item.json),\n uniqueKey,\n analysisTimestamp: new Date().toISOString()\n } \n};"
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "cdbd74a0-8619-46ec-a858-f00721acb823",
"name": "API Rate Limiter",
"type": "n8n-nodes-base.wait",
"notes": "Prevents API rate limit issues",
"position": [
1440,
176
],
"parameters": {
"amount": 1
},
"notesInFlow": true,
"typeVersion": 1.1
},
{
"id": "1b32e48c-8425-4ada-b243-327572294b70",
"name": "Only New Mentions",
"type": "n8n-nodes-base.filter",
"notes": "Filters to unanalyzed mentions only",
"position": [
1664,
176
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": false,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "9i0j1k2l-3m4n-5o6p-7q8r-s9t0u1v2w3x4",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.skip }}",
"rightValue": "false"
}
]
}
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "ef03f71b-7083-4e23-b0ba-7e306c3578aa",
"name": "AI Sentiment Analysis Engine",
"type": "n8n-nodes-base.code",
"notes": "Analyzes sentiment and detects negative trends",
"position": [
1888,
176
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// AI-powered sentiment analysis engine\nconst mention = $input.item.json;\nconst text = (mention.text || '').toLowerCase();\n\n// Sentiment keyword dictionaries\nconst negativePhrases = {\n critical: ['scam', 'fraud', 'lawsuit', 'illegal', 'criminal', 'investigate', 'sue', 'boycott', 'terrible', 'worst', 'horrible', 'awful', 'disaster', 'catastrophe', 'dangerous', 'unsafe', 'toxic', 'predatory'],\n severe: ['disappointed', 'unacceptable', 'disgraceful', 'shameful', 'pathetic', 'failure', 'failed', 'broken', 'useless', 'waste', 'regret', 'never again', 'avoid', 'warning', 'beware', 'don\\'t buy'],\n moderate: ['bad', 'poor', 'not good', 'mediocre', 'subpar', 'lacking', 'issues', 'problems', 'concerns', 'unhappy', 'dissatisfied', 'frustrated', 'annoyed'],\n mild: ['okay', 'fine', 'average', 'could be better', 'meh', 'not impressed']\n};\n\nconst positivePhrases = {\n strong: ['amazing', 'excellent', 'outstanding', 'phenomenal', 'incredible', 'fantastic', 'brilliant', 'perfect', 'love it', 'best ever', 'highly recommend', 'game changer'],\n moderate: ['good', 'great', 'nice', 'happy', 'satisfied', 'pleased', 'enjoy', 'like', 'recommend', 'quality'],\n mild: ['decent', 'acceptable', 'fine', 'works', 'not bad']\n};\n\n// Calculate sentiment scores\nlet sentimentScore = 0;\nlet sentimentLevel = 'NEUTRAL';\nlet detectedPhrases = [];\n\n// Check negative phrases\nfor (const [level, phrases] of Object.entries(negativePhrases)) {\n for (const phrase of phrases) {\n if (text.includes(phrase)) {\n const weight = level === 'critical' ? -10 : level === 'severe' ? -7 : level === 'moderate' ? -4 : -2;\n sentimentScore += weight;\n detectedPhrases.push({ phrase, sentiment: 'negative', level });\n }\n }\n}\n\n// Check positive phrases\nfor (const [level, phrases] of Object.entries(positivePhrases)) {\n for (const phrase of phrases) {\n if (text.includes(phrase)) {\n const weight = level === 'strong' ? 8 : level === 'moderate' ? 5 : 2;\n sentimentScore += weight;\n detectedPhrases.push({ phrase, sentiment: 'positive', level });\n }\n }\n}\n\n// Amplification factors\nlet amplificationScore = 0;\n\n// High engagement amplifies impact\nif (mention.engagement > 1000) amplificationScore += 30;\nelse if (mention.engagement > 500) amplificationScore += 20;\nelse if (mention.engagement > 100) amplificationScore += 10;\nelse if (mention.engagement > 10) amplificationScore += 5;\n\n// Verified/influential accounts amplify impact\nif (mention.isVerified) amplificationScore += 15;\nif (mention.authorFollowers > 100000) amplificationScore += 25;\nelse if (mention.authorFollowers > 10000) amplificationScore += 15;\nelse if (mention.authorFollowers > 1000) amplificationScore += 5;\n\n// News platforms have high credibility\nif (mention.platform === 'news') amplificationScore += 40;\n\n// Determine sentiment level\nif (sentimentScore <= -20) {\n sentimentLevel = 'VERY_NEGATIVE';\n} else if (sentimentScore <= -10) {\n sentimentLevel = 'NEGATIVE';\n} else if (sentimentScore <= -3) {\n sentimentLevel = 'SLIGHTLY_NEGATIVE';\n} else if (sentimentScore >= 15) {\n sentimentLevel = 'VERY_POSITIVE';\n} else if (sentimentScore >= 8) {\n sentimentLevel = 'POSITIVE';\n} else if (sentimentScore >= 3) {\n sentimentLevel = 'SLIGHTLY_POSITIVE';\n} else {\n sentimentLevel = 'NEUTRAL';\n}\n\n// Calculate total impact score (sentiment + amplification)\nconst impactScore = Math.abs(sentimentScore) + amplificationScore;\n\n// Determine if this is crisis-worthy\nlet crisisLevel = 'NONE';\nlet requiresCrisisResponse = false;\n\nif (sentimentScore <= -15 && impactScore >= 50) {\n crisisLevel = 'CRITICAL';\n requiresCrisisResponse = true;\n} else if (sentimentScore <= -10 && impactScore >= 35) {\n crisisLevel = 'HIGH';\n requiresCrisisResponse = true;\n} else if (sentimentScore <= -5 && impactScore >= 20) {\n crisisLevel = 'MEDIUM';\n requiresCrisisResponse = true;\n} else if (sentimentScore <= -3 && impactScore >= 10) {\n crisisLevel = 'LOW';\n requiresCrisisResponse = false;\n}\n\nreturn {\n json: {\n ...mention,\n sentimentScore,\n sentimentLevel,\n amplificationScore,\n impactScore,\n detectedPhrases,\n crisisLevel,\n requiresCrisisResponse,\n analysisComplete: true\n }\n};"
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "2984eae8-2455-48d5-9c81-f439007d7b1c",
"name": "Calculate Trend Baseline",
"type": "n8n-nodes-base.code",
"notes": "Compares current sentiment to historical baseline",
"position": [
2112,
176
],
"parameters": {
"jsCode": "// Calculate sentiment trends and detect sharp drops\nconst mentions = $input.all().map(item => item.json);\n\n// Get historical sentiment data\nconst historicalData = $getWorkflowStaticData('global').sentimentHistory || [];\n\n// Calculate current batch metrics\nconst negativeMentions = mentions.filter(m => m.sentimentScore < 0);\nconst positiveMentions = mentions.filter(m => m.sentimentScore > 0);\nconst neutralMentions = mentions.filter(m => m.sentimentScore === 0);\n\nconst avgSentiment = mentions.reduce((sum, m) => sum + m.sentimentScore, 0) / mentions.length || 0;\nconst avgImpact = mentions.reduce((sum, m) => sum + m.impactScore, 0) / mentions.length || 0;\nconst negativeRatio = negativeMentions.length / mentions.length || 0;\n\nconst currentMetrics = {\n timestamp: new Date().toISOString(),\n totalMentions: mentions.length,\n avgSentiment,\n avgImpact,\n negativeRatio,\n negativeMentions: negativeMentions.length,\n positiveMentions: positiveMentions.length,\n neutralMentions: neutralMentions.length\n};\n\n// Add to historical data\nhistoricalData.push(currentMetrics);\n\n// Keep only last 72 hours of data (assuming 10-min intervals = 6 per hour)\nconst maxDataPoints = 72 * 6; // 432 data points\nif (historicalData.length > maxDataPoints) {\n historicalData.shift();\n}\n\n// Calculate baseline (average of last 24 hours)\nconst last24Hours = historicalData.slice(-144); // 24 hours * 6 intervals\nconst baseline = {\n avgSentiment: last24Hours.reduce((sum, d) => sum + d.avgSentiment, 0) / last24Hours.length || 0,\n avgImpact: last24Hours.reduce((sum, d) => sum + d.avgImpact, 0) / last24Hours.length || 0,\n negativeRatio: last24Hours.reduce((sum, d) => sum + d.negativeRatio, 0) / last24Hours.length || 0\n};\n\n// Detect sharp drops\nconst sentimentDrop = baseline.avgSentiment - currentMetrics.avgSentiment;\nconst impactIncrease = currentMetrics.avgImpact - baseline.avgImpact;\nconst negativeRatioIncrease = currentMetrics.negativeRatio - baseline.negativeRatio;\n\nlet trendAlert = 'NORMAL';\nlet trendSeverity = 0;\nlet trendIndicators = [];\n\n// Critical trend: massive sentiment drop\nif (sentimentDrop > 15) {\n trendSeverity = 100;\n trendAlert = 'CRISIS';\n trendIndicators.push(`\ud83d\udd34 Sentiment dropped by ${sentimentDrop.toFixed(1)} points`);\n}\n// High alert: significant sentiment drop\nelse if (sentimentDrop > 10) {\n trendSeverity = 75;\n trendAlert = 'HIGH_ALERT';\n trendIndicators.push(`\ud83d\udfe0 Sentiment dropped by ${sentimentDrop.toFixed(1)} points`);\n}\n// Medium alert: moderate sentiment drop\nelse if (sentimentDrop > 5) {\n trendSeverity = 50;\n trendAlert = 'MEDIUM_ALERT';\n trendIndicators.push(`\ud83d\udfe1 Sentiment dropped by ${sentimentDrop.toFixed(1)} points`);\n}\n\n// Check negative ratio spike\nif (negativeRatioIncrease > 0.3) {\n trendSeverity = Math.max(trendSeverity, 75);\n trendAlert = trendAlert === 'NORMAL' ? 'HIGH_ALERT' : trendAlert;\n trendIndicators.push(`\ud83d\udfe0 Negative mentions spiked by ${(negativeRatioIncrease * 100).toFixed(1)}%`);\n} else if (negativeRatioIncrease > 0.15) {\n trendSeverity = Math.max(trendSeverity, 50);\n trendAlert = trendAlert === 'NORMAL' ? 'MEDIUM_ALERT' : trendAlert;\n trendIndicators.push(`\ud83d\udfe1 Negative mentions increased by ${(negativeRatioIncrease * 100).toFixed(1)}%`);\n}\n\n// Check impact surge\nif (impactIncrease > 20) {\n trendSeverity = Math.max(trendSeverity, 65);\n trendIndicators.push(`\u26a1 Impact score surged by ${impactIncrease.toFixed(1)} points`);\n}\n\n// Save updated historical data\n$getWorkflowStaticData('global').sentimentHistory = historicalData;\n\n// Return enriched mentions with trend context\nreturn mentions.map(mention => ({\n json: {\n ...mention,\n trendMetrics: currentMetrics,\n baseline,\n trendAlert,\n trendSeverity,\n trendIndicators,\n sentimentDrop,\n negativeRatioIncrease\n }\n}));"
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "34f454c5-a87a-4144-b4af-daee4f3af656",
"name": "Filter Crisis Triggers",
"type": "n8n-nodes-base.switch",
"notes": "Routes based on crisis severity",
"position": [
2336,
176
],
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "",
"rightValue": ""
}
]
}
}
]
},
"options": {}
},
"notesInFlow": true,
"typeVersion": 3.1
},
{
"id": "0b730149-0ecf-49d9-991d-20350abae57b",
"name": "Aggregate Crisis Data",
"type": "n8n-nodes-base.code",
"notes": "Combines multiple crisis mentions into single alert",
"position": [
2560,
176
],
"parameters": {
"jsCode": "// Aggregate all crisis-level mentions\nconst crisisMentions = $input.all().map(item => item.json);\n\n// Sort by impact score\ncrisisMentions.sort((a, b) => b.impactScore - a.impactScore);\n\n// Calculate aggregate metrics\nconst topMentions = crisisMentions.slice(0, 5); // Top 5 most impactful\nconst platforms = [...new Set(crisisMentions.map(m => m.platform))];\nconst totalEngagement = crisisMentions.reduce((sum, m) => sum + m.engagement, 0);\nconst avgSentiment = crisisMentions.reduce((sum, m) => sum + m.sentimentScore, 0) / crisisMentions.length;\nconst avgImpact = crisisMentions.reduce((sum, m) => sum + m.impactScore, 0) / crisisMentions.length;\n\n// Determine overall crisis severity\nlet overallSeverity = 'MEDIUM';\nif (avgImpact >= 60 || crisisMentions.length >= 10) {\n overallSeverity = 'CRITICAL';\n} else if (avgImpact >= 40 || crisisMentions.length >= 5) {\n overallSeverity = 'HIGH';\n}\n\n// Extract common themes\nconst allPhrases = crisisMentions.flatMap(m => m.detectedPhrases || []);\nconst phraseFrequency = {};\nallPhrases.forEach(p => {\n const key = p.phrase;\n phraseFrequency[key] = (phraseFrequency[key] || 0) + 1;\n});\n\nconst topThemes = Object.entries(phraseFrequency)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 5)\n .map(([phrase, count]) => ({ phrase, count }));\n\nconst crisisData = {\n crisisId: `CRISIS-${Date.now()}`,\n timestamp: new Date().toISOString(),\n severity: overallSeverity,\n totalMentions: crisisMentions.length,\n platforms,\n totalEngagement,\n avgSentiment: avgSentiment.toFixed(2),\n avgImpact: avgImpact.toFixed(2),\n topMentions: topMentions.map(m => ({\n platform: m.platform,\n text: m.text.substring(0, 150) + '...',\n author: m.author,\n url: m.url,\n sentiment: m.sentimentLevel,\n impact: m.impactScore\n })),\n topThemes,\n trendIndicators: crisisMentions[0]?.trendIndicators || [],\n sentimentDrop: crisisMentions[0]?.sentimentDrop || 0,\n allMentions: crisisMentions\n};\n\nreturn [{ json: crisisData }];"
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "ae7de7b6-8e00-45b5-a9be-8404f836981a",
"name": "Generate Crisis Brief",
"type": "n8n-nodes-base.code",
"notes": "Creates executive crisis brief and response plan",
"position": [
2784,
176
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Generate comprehensive crisis brief\nconst crisis = $input.item.json;\nconst timestamp = new Date(crisis.timestamp).toLocaleString();\n\nconst brief = `\ud83d\udea8 BRAND REPUTATION CRISIS ALERT \ud83d\udea8\n\nCrisis ID: ${crisis.crisisId}\nSeverity: ${crisis.severity}\nDetected: ${timestamp}\n\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udcca CRISIS METRICS\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\n\u2022 Total Mentions: ${crisis.totalMentions}\n\u2022 Affected Platforms: ${crisis.platforms.join(', ')}\n\u2022 Total Engagement: ${crisis.totalEngagement.toLocaleString()}\n\u2022 Avg Sentiment Score: ${crisis.avgSentiment} (baseline comparison)\n\u2022 Avg Impact Score: ${crisis.avgImpact}\n${crisis.sentimentDrop > 0 ? `\u2022 Sentiment Drop: -${crisis.sentimentDrop.toFixed(1)} points` : ''}\n\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\u26a0\ufe0f KEY ISSUES DETECTED\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\n${crisis.topThemes.map((theme, i) => `${i + 1}. \"${theme.phrase}\" - mentioned ${theme.count}x`).join('\\n')}\n\n${crisis.trendIndicators.length > 0 ? `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udcc8 TREND ALERTS\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\n${crisis.trendIndicators.map((ind, i) => `${i + 1}. ${ind}`).join('\\n')}\n` : ''}\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udd25 TOP IMPACTFUL MENTIONS\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\n${crisis.topMentions.map((m, i) => `${i + 1}. [${m.platform.toUpperCase()}] @${m.author} (Impact: ${m.impact})\n \"${m.text}\"\n \ud83d\udd17 ${m.url}\n`).join('\\n')}\n\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83c\udfaf RECOMMENDED IMMEDIATE ACTIONS\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\n${crisis.severity === 'CRITICAL' ? `\n\u26a1 CRITICAL RESPONSE REQUIRED:\n\n1. \ud83d\udea8 Convene crisis management team IMMEDIATELY\n2. \ud83d\udce2 Prepare holding statement within 1 hour\n3. \ud83d\udd0d Investigate root cause with urgency\n4. \ud83d\udcf1 Monitor all social channels continuously\n5. \ud83e\udd1d Reach out to top influencers/affected parties\n6. \ud83d\udce7 Notify executive leadership and PR team\n7. \ud83d\udee1\ufe0f Activate social media response protocols\n8. \ud83d\udcca Set up real-time sentiment monitoring dashboard\n` : crisis.severity === 'HIGH' ? `\n\u26a0\ufe0f HIGH PRIORITY RESPONSE:\n\n1. \ud83d\udd14 Alert crisis management team\n2. \ud83d\udd0d Investigate issue and gather facts\n3. \ud83d\udcdd Draft response statement (4-6 hour window)\n4. \ud83d\udcf1 Increase social monitoring frequency\n5. \ud83d\udc65 Identify and engage with key voices\n6. \ud83d\udce7 Brief PR and customer service teams\n7. \ud83d\udcca Track sentiment evolution hourly\n` : `\n\ud83d\udccb STANDARD RESPONSE:\n\n1. \ud83d\udcca Continue monitoring situation\n2. \ud83d\udd0d Investigate mentioned issues\n3. \ud83d\udcdd Prepare response if trend continues\n4. \ud83d\udc65 Engage with concerned customers\n5. \ud83d\udce7 Brief relevant teams\n`}\n\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udccb CRISIS RESPONSE CHECKLIST\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\n\u2610 Situation assessment completed\n\u2610 Crisis team assembled\n\u2610 Root cause identified\n\u2610 Response strategy approved\n\u2610 Statement drafted and reviewed\n\u2610 Stakeholders notified\n\u2610 Response published\n\u2610 Monitoring intensified\n\u2610 Follow-up plan created\n\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\nGenerated by AI Brand Reputation Crisis Detector\nThis is an automated alert - human review required`;\n\nconst slackMessage = {\n channel: '#crisis-management',\n text: `\ud83d\udea8 *${crisis.severity} BRAND REPUTATION CRISIS DETECTED* \ud83d\udea8`,\n attachments: [\n {\n color: crisis.severity === 'CRITICAL' ? 'danger' : 'warning',\n title: `Crisis ID: ${crisis.crisisId}`,\n text: `${crisis.totalMentions} negative mentions detected across ${crisis.platforms.join(', ')}`,\n fields: [\n {\n title: 'Severity',\n value: crisis.severity,\n short: true\n },\n {\n title: 'Total Engagement',\n value: crisis.totalEngagement.toLocaleString(),\n short: true\n },\n {\n title: 'Avg Sentiment',\n value: crisis.avgSentiment,\n short: true\n },\n {\n title: 'Avg Impact',\n value: crisis.avgImpact,\n short: true\n }\n ],\n actions: [\n {\n type: 'button',\n text: '\ud83d\udea8 Activate Crisis Response',\n url: 'https://your-crisis-dashboard.com'\n },\n {\n type: 'button',\n text: '\ud83d\udcca View Dashboard',\n url: 'https://your-analytics-dashboard.com'\n }\n ]\n }\n ]\n};\n\nreturn {\n json: {\n ...crisis,\n brief,\n slackMessage,\n emailSubject: `\ud83d\udea8 ${crisis.severity} BRAND CRISIS - Immediate Action Required`,\n emailBody: brief\n }\n};"
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "cd1985eb-468f-4701-94d1-20acaf59ae0c",
"name": "Alert Crisis Team - Slack",
"type": "n8n-nodes-base.slack",
"notes": "Immediately notifies crisis management team",
"position": [
3008,
176
],
"parameters": {
"resource": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"notesInFlow": true,
"typeVersion": 2.1
},
{
"id": "39c90e61-31f6-4243-ac61-b28170501145",
"name": "Alert Crisis Team - Email",
"type": "n8n-nodes-base.emailSend",
"notes": "Sends detailed crisis brief to leadership",
"position": [
3232,
176
],
"parameters": {
"options": {
"ccEmail": "user@example.com, user@example.com"
},
"subject": "={{ $json.emailSubject }}",
"toEmail": "user@example.com, user@example.com, user@example.com",
"fromEmail": "user@example.com"
},
"credentials": {
"smtp": {
"name": "<your credential>"
}
},
"notesInFlow": true,
"typeVersion": 2.1
},
{
"id": "de278e52-4482-4df8-a007-b1ecf4f9174a",
"name": "Create JIRA Crisis Ticket",
"type": "n8n-nodes-base.jira",
"notes": "Creates high-priority ticket for crisis tracking",
"position": [
3456,
176
],
"parameters": {
"project": {
"__rl": true,
"mode": "id",
"value": "=",
"__regex": "^([0-9]{2,})"
},
"summary": "={{ $json.emailSubject }}",
"issueType": "Bug",
"additionalFields": {
"labels": "crisis,reputation,urgent",
"priority": "Highest"
}
},
"credentials": {
"jiraSoftwareCloudApi": {
"name": "<your credential>"
}
},
"notesInFlow": true,
"typeVersion": 1
},
{
"id": "91ccc0aa-461c-4c87-acd8-a01a69d621ef",
"name": "Log Crisis Event",
"type": "n8n-nodes-base.code",
"notes": "Records crisis in database for analysis",
"position": [
3680,
176
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Log crisis event to workflow storage\nconst crisis = $input.item.json;\n\nconst crisisEvent = {\n crisisId: crisis.crisisId,\n timestamp: crisis.timestamp,\n severity: crisis.severity,\n totalMentions: crisis.totalMentions,\n platforms: crisis.platforms,\n totalEngagement: crisis.totalEngagement,\n avgSentiment: parseFloat(crisis.avgSentiment),\n avgImpact: parseFloat(crisis.avgImpact),\n topThemes: crisis.topThemes,\n responseInitiated: true,\n resolved: false\n};\n\nconsole.log('\ud83d\udea8 CRISIS EVENT LOGGED:');\nconsole.log(JSON.stringify(crisisEvent, null, 2));\n\n// Store in workflow static data\nconst crisisLog = $getWorkflowStaticData('global').crisisLog || [];\ncrisisLog.push(crisisEvent);\n\n// Keep only last 50 crises\nif (crisisLog.length > 50) {\n crisisLog.shift();\n}\n\n$getWorkflowStaticData('global').crisisLog = crisisLog;\n\nreturn {\n json: {\n success: true,\n crisisEvent,\n totalCrisesLogged: crisisLog.length,\n message: `Crisis ${crisisEvent.crisisId} logged and response initiated`\n }\n};"
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "e2fc634b-0864-4cee-8d61-d9834b5a8067",
"name": "Log Routine Monitoring",
"type": "n8n-nodes-base.code",
"notes": "Records non-crisis mentions for trend analysis",
"position": [
3904,
176
],
"parameters": {
"jsCode": "// Log routine monitoring data\nconst mentions = $input.all().map(item => item.json);\n\nconst summary = {\n timestamp: new Date().toISOString(),\n totalMentions: mentions.length,\n avgSentiment: mentions.reduce((sum, m) => sum + m.sentimentScore, 0) / mentions.length || 0,\n positive: mentions.filter(m => m.sentimentScore > 0).length,\n neutral: mentions.filter(m => m.sentimentScore === 0).length,\n negative: mentions.filter(m => m.sentimentScore < 0).length,\n platforms: [...new Set(mentions.map(m => m.platform))]\n};\n\nconsole.log('\ud83d\udcca ROUTINE MONITORING:');\nconsole.log(JSON.stringify(summary, null, 2));\n\n// Store summary\nconst monitoringLog = $getWorkflowStaticData('global').monitoringLog || [];\nmonitoringLog.push(summary);\n\n// Keep only last 1000 summaries\nif (monitoringLog.length > 1000) {\n monitoringLog.shift();\n}\n\n$getWorkflowStaticData('global').monitoringLog = monitoringLog;\n\nreturn [{\n json: {\n success: true,\n summary,\n message: 'Routine monitoring logged'\n }\n}];"
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "dbf1757c-4212-48c0-80bb-04924c30aae8",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-720,
-496
],
"parameters": {
"width": 636,
"height": 1288,
"content": "## AI Brand Reputation Crisis Detector\n\nThis workflow monitors brand mentions across multiple platforms (Twitter/X, Reddit, News) and automatically detects reputation crises based on sentiment analysis and trend detection.\n\n## How it works\n\n1. **Multi-platform monitoring**: Every 10 minutes, scans Twitter/X, Reddit, and news sites for brand mentions\n2. **Data normalization**: Converts all platform data into unified format\n3. **Smart filtering**: Removes duplicates and already-analyzed mentions\n4. **AI sentiment analysis**: Analyzes each mention for:\n - Sentiment score (positive/negative/neutral)\n - Amplification factors (engagement, verified accounts, news sources)\n - Crisis-level phrases and keywords\n5. **Trend detection**: Compares current sentiment to 24-hour baseline:\n - Detects sharp sentiment drops\n - Identifies negative mention spikes\n - Calculates impact surge\n6. **Crisis classification**: Assigns severity (CRITICAL/HIGH/MEDIUM/LOW)\n7. **Automated response**: For crises, triggers immediate alerts:\n - Executive crisis brief with action plan\n - Slack alerts to crisis team\n - Email to leadership and PR team\n - JIRA ticket creation\n - Crisis event logging\n\n## Setup steps\n\n1. **Connect platforms**:\n - Twitter/X: Add OAuth credentials to \"Monitor Twitter/X\" node\n - Reddit: Add OAuth credentials to \"Monitor Reddit\" node\n - News API: Get API key from newsapi.org and add to \"Monitor News & Blogs\" node\n\n2. **Configure brand monitoring**:\n - Update brand name and handles in search queries\n - Add additional platforms if needed (LinkedIn, Facebook, Instagram)\n\n3. **Set up alerting**:\n - Slack: Add credentials and update channel names\n - Email: Add SMTP settings and recipient lists\n - JIRA: Add credentials and project ID\n\n4. **Adjust sensitivity**:\n - Modify sentiment keyword dictionaries in \"AI Sentiment Analysis Engine\"\n - Adjust crisis threshold scores\n - Customize amplification factors\n\n5. **Test thoroughly**:\n - Run manual execution with test data\n - Verify alert routing and content\n - Test false positive handling\n\n6. **Activate**: Enable for continuous 24/7 monitoring\n\n"
},
"typeVersion": 1
},
{
"id": "7e3ee568-5e52-49e2-b819-8ffc42eadda3",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
48,
-128
],
"parameters": {
"color": 6,
"width": 640,
"height": 672,
"content": "## \ud83d\udce1 Multi-Platform Monitoring"
},
"typeVersion": 1
},
{
"id": "6f9bfe0e-9664-47b7-b30b-9eeed4c84492",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
720,
16
],
"parameters": {
"color": 4,
"width": 1088,
"height": 320,
"content": "## \ud83d\udd04 Data Normalization & Deduplication"
},
"typeVersion": 1
},
{
"id": "730e6388-d6a5-452f-8342-72de4d325953",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
1816,
0
],
"parameters": {
"color": 6,
"width": 688,
"height": 336,
"content": "## \ud83e\udd16 AI Sentiment Analysis & Trend Detection"
},
"typeVersion": 1
},
{
"id": "b614b8ce-f893-4a30-a739-a9c18798d793",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
2520,
-32
],
"parameters": {
"color": 4,
"width": 1296,
"height": 368,
"content": "## \ud83d\udea8 Crisis Response Activation"
},
"typeVersion": 1
},
{
"id": "11931922-1856-4481-8160-c2eda2d92e28",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
3840,
-16
],
"parameters": {
"color": 6,
"width": 304,
"height": 352,
"content": "## \ud83d\udcca Routine Logging"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "675eb895-dfbd-4574-82d3-529d8220d1c9",
"connections": {
"Monitor Reddit": {
"main": [
[
{
"node": "Normalize Data Format",
"type": "main",
"index": 0
}
]
]
},
"API Rate Limiter": {
"main": [
[
{
"node": "Only New Mentions",
"type": "main",
"index": 0
}
]
]
},
"Every 10 Minutes": {
"main": [
[
{
"node": "Monitor Twitter/X",
"type": "main",
"index": 0
},
{
"node": "Monitor Reddit",
"type": "main",
"index": 0
},
{
"node": "Monitor News & Blogs",
"type": "main",
"index": 0
}
]
]
},
"Log Crisis Event": {
"main": [
[
{
"node": "Log Routine Monitoring",
"type": "main",
"index": 0
}
]
]
},
"Monitor Twitter/X": {
"main": [
[
{
"node": "Normalize Data Format",
"type": "main",
"index": 0
}
]
]
},
"Only New Mentions": {
"main": [
[
{
"node": "AI Sentiment Analysis Engine",
"type": "main",
"index": 0
}
]
]
},
"Remove Duplicates": {
"main": [
[
{
"node": "Merge Platform Data",
"type": "main",
"index": 0
}
]
]
},
"Merge Platform Data": {
"main": [
[
{
"node": "Track Analyzed Mentions",
"type": "main",
"index": 0
}
]
]
},
"Monitor News & Blogs": {
"main": [
[
{
"node": "Normalize Data Format",
"type": "main",
"index": 0
}
]
]
},
"Aggregate Crisis Data": {
"main": [
[
{
"node": "Generate Crisis Brief",
"type": "main",
"index": 0
}
]
]
},
"Generate Crisis Brief": {
"main": [
[
{
"node": "Alert Crisis Team - Slack",
"type": "main",
"index": 0
}
]
]
},
"Normalize Data Format": {
"main": [
[
{
"node": "Remove Duplicates",
"type": "main",
"index": 0
}
]
]
},
"Filter Crisis Triggers": {
"main": [
[
{
"node": "Aggregate Crisis Data",
"type": "main",
"index": 0
}
]
]
},
"Track Analyzed Mentions": {
"main": [
[
{
"node": "API Rate Limiter",
"type": "main",
"index": 0
}
]
]
},
"Calculate Trend Baseline": {
"main": [
[
{
"node": "Filter Crisis Triggers",
"type": "main",
"index": 0
}
]
]
},
"Alert Crisis Team - Email": {
"main": [
[
{
"node": "Create JIRA Crisis Ticket",
"type": "main",
"index": 0
}
]
]
},
"Alert Crisis Team - Slack": {
"main": [
[
{
"node": "Alert Crisis Team - Email",
"type": "main",
"index": 0
}
]
]
},
"Create JIRA Crisis Ticket": {
"main": [
[
{
"node": "Log Crisis Event",
"type": "main",
"index": 0
}
]
]
},
"AI Sentiment Analysis Engine": {
"main": [
[
{
"node": "Calculate Trend Baseline",
"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.
jiraSoftwareCloudApiredditOAuth2ApislackApismtptwitterOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow monitors brand mentions across multiple platforms (Twitter/X, Reddit, News) and automatically detects reputation crises based on sentiment analysis and trend detection. Multi-platform monitoring: Every 10 minutes, scans Twitter/X, Reddit, and news sites for brand…
Source: https://n8n.io/workflows/13696/ — 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.
Automatically discovers trending topics in your niche and generates ready-to-use content ideas with AI. Twitter/X trending topics and hashtags Reddit hot posts from niche subreddits Google Trends dail
Social media managers, creators, and brand accounts that rely on retweets for reach but want an automated, hands-off cleanup after campaigns to keep profiles tidy and on-brand.
AI Social Media Post Creator. Uses googleSheets, openAi, twitter, httpRequest. Scheduled trigger; 7 nodes.
Marketing teams and social media managers in Japan who want to automate content creation while maintaining high quality standards and cultural appropriateness. Perfect for businesses that need consist
This template is ideal for sales teams, recruiters, business development professionals, and relationship managers who need to monitor changes in their network's LinkedIn profiles. Perfect for agencies