AutomationFlowsAI & RAG › Real-time Oil Price Crisis Detection with Qwq-32b AI and Multi-channel Alerts

Real-time Oil Price Crisis Detection with Qwq-32b AI and Multi-channel Alerts

ByCheng Siong Chin @cschin on n8n.io

Scheduled runs collect data from oil markets, global shipping movements, news sources, and official reports. The system performs statistical checks to detect anomalies and volatility shifts. An AI-driven geopolitical model evaluates emerging risks and assigns a crisis score.…

Cron / scheduled trigger★★★★★ complexityAI-powered34 nodesHTTP RequestAgentGmailSlackPostgresOpenRouter Chat
AI & RAG Trigger: Cron / scheduled Nodes: 34 Complexity: ★★★★★ AI nodes: yes Added:
Real-time Oil Price Crisis Detection with Qwq-32b AI and Multi-channel Alerts — n8n workflow card showing HTTP Request, Agent, Gmail integration

This workflow corresponds to n8n.io template #10763 — 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 →

Download .json
{
  "id": "GtOVwfzfA4dk0exU",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Real-Time Oil Price Crisis Detection and Smart Alert System via Qwq-32b",
  "tags": [],
  "nodes": [
    {
      "id": "e63bbdb7-d063-4f97-8eb8-1050bdb82157",
      "name": "Schedule Every 5 Minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -2176,
        480
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "0605042b-b18d-491d-9418-89af3a229c1d",
      "name": "Workflow Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        -1920,
        480
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "oilPriceApiUrl",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Oil Price API endpoint URL__>"
            },
            {
              "id": "id-2",
              "name": "opecReportsUrl",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__OPEC Reports API endpoint__>"
            },
            {
              "id": "id-3",
              "name": "shippingDataUrl",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Shipping Data API endpoint__>"
            },
            {
              "id": "id-4",
              "name": "newsApiUrl",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__News API endpoint__>"
            },
            {
              "id": "id-5",
              "name": "dashboardApiUrl",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Dashboard API endpoint__>"
            },
            {
              "id": "id-6",
              "name": "alertEmailRecipients",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Email addresses for alerts (comma-separated)__>"
            },
            {
              "id": "id-7",
              "name": "slackChannel",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Slack channel ID for alerts__>"
            },
            {
              "id": "id-8",
              "name": "infoThreshold",
              "type": "number",
              "value": 30
            },
            {
              "id": "id-9",
              "name": "warningThreshold",
              "type": "number",
              "value": 60
            },
            {
              "id": "id-10",
              "name": "criticalThreshold",
              "type": "number",
              "value": 85
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "3d1f09cb-e959-493e-9ed1-a792a543783e",
      "name": "Fetch Oil Price Data (API)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1696,
        304
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration').first().json.oilPriceApiUrl }}",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "1b0559d6-7853-436c-91ff-5401ca6fbdbf",
      "name": "Fetch OPEC Reports",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1696,
        464
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration').first().json.opecReportsUrl }}",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "db390c16-d4b1-4607-9188-350e700f4595",
      "name": "Fetch Shipping Data",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1696,
        592
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration').first().json.shippingDataUrl }}",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "5765af2b-7800-4655-9110-74a4c5018403",
      "name": "Fetch News Feed",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1696,
        736
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration').first().json.newsApiUrl }}",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "c629c377-1587-4df6-94b5-c64bf974b256",
      "name": "Merge All Data Sources",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1408,
        432
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3.2
    },
    {
      "id": "d8ebe013-070d-4ea8-9410-5a05aff03dbc",
      "name": "Data Cleaning & Preprocessing",
      "type": "n8n-nodes-base.code",
      "position": [
        -1200,
        432
      ],
      "parameters": {
        "jsCode": "// Data Cleaning & Preprocessing for Oil Price Crisis Detection\n// Handles data from multiple sources: API, OPEC, Shipping, News\n\nconst cleanedData = [];\n\n// Process all input items\nfor (const item of $input.all()) {\n  try {\n    const source = item.json.source || 'unknown';\n    const timestamp = item.json.timestamp || new Date().toISOString();\n    \n    // Standardize timestamp to ISO format\n    let standardizedTimestamp;\n    try {\n      standardizedTimestamp = new Date(timestamp).toISOString();\n    } catch (e) {\n      standardizedTimestamp = new Date().toISOString();\n    }\n    \n    // Initialize cleaned data object\n    const cleanedItem = {\n      timestamp: standardizedTimestamp,\n      source: source,\n      rawData: item.json\n    };\n    \n    // Extract oil price data\n    if (item.json.price !== undefined && item.json.price !== null) {\n      cleanedItem.oilPrice = parseFloat(item.json.price) || 0;\n    } else if (item.json.brent_price !== undefined) {\n      cleanedItem.oilPrice = parseFloat(item.json.brent_price) || 0;\n    } else if (item.json.wti_price !== undefined) {\n      cleanedItem.oilPrice = parseFloat(item.json.wti_price) || 0;\n    }\n    \n    // Extract volume data\n    if (item.json.volume !== undefined && item.json.volume !== null) {\n      cleanedItem.volume = parseFloat(item.json.volume) || 0;\n    } else if (item.json.trading_volume !== undefined) {\n      cleanedItem.volume = parseFloat(item.json.trading_volume) || 0;\n    }\n    \n    // Extract shipping/supply data\n    if (item.json.shipping_volume !== undefined) {\n      cleanedItem.shippingVolume = parseFloat(item.json.shipping_volume) || 0;\n    }\n    if (item.json.supply_disruption !== undefined) {\n      cleanedItem.supplyDisruption = item.json.supply_disruption;\n    }\n    \n    // Extract and analyze news sentiment\n    if (item.json.title || item.json.headline || item.json.text) {\n      const newsText = (item.json.title || item.json.headline || item.json.text || '').toLowerCase();\n      \n      // Simple sentiment analysis based on keywords\n      const negativeKeywords = ['crisis', 'war', 'conflict', 'shortage', 'disruption', 'sanction', 'embargo', 'attack', 'threat'];\n      const positiveKeywords = ['stable', 'agreement', 'peace', 'increase', 'surplus', 'cooperation'];\n      \n      let sentimentScore = 0;\n      negativeKeywords.forEach(keyword => {\n        if (newsText.includes(keyword)) sentimentScore -= 1;\n      });\n      positiveKeywords.forEach(keyword => {\n        if (newsText.includes(keyword)) sentimentScore += 1;\n      });\n      \n      cleanedItem.newsSentiment = sentimentScore;\n      cleanedItem.newsText = item.json.title || item.json.headline || item.json.text;\n    }\n    \n    // Extract geopolitical indicators\n    if (item.json.region !== undefined) {\n      cleanedItem.region = item.json.region;\n    }\n    if (item.json.country !== undefined) {\n      cleanedItem.country = item.json.country;\n    }\n    \n    // Handle missing values - set defaults\n    if (cleanedItem.oilPrice === undefined) {\n      cleanedItem.oilPrice = null;\n    }\n    if (cleanedItem.volume === undefined) {\n      cleanedItem.volume = null;\n    }\n    if (cleanedItem.newsSentiment === undefined) {\n      cleanedItem.newsSentiment = 0;\n    }\n    \n    // Add data quality flag\n    cleanedItem.dataQuality = {\n      hasPriceData: cleanedItem.oilPrice !== null,\n      hasVolumeData: cleanedItem.volume !== null,\n      hasNewsData: cleanedItem.newsText !== undefined,\n      completeness: 0\n    };\n    \n    // Calculate completeness score\n    let completenessCount = 0;\n    if (cleanedItem.oilPrice !== null) completenessCount++;\n    if (cleanedItem.volume !== null) completenessCount++;\n    if (cleanedItem.newsText !== undefined) completenessCount++;\n    cleanedItem.dataQuality.completeness = completenessCount / 3;\n    \n    cleanedData.push(cleanedItem);\n    \n  } catch (error) {\n    console.log('Error processing item:', error.message);\n    // Continue processing other items\n  }\n}\n\n// Return structured data array\nreturn cleanedData.map(item => ({ json: item }));"
      },
      "typeVersion": 2
    },
    {
      "id": "d381a9a2-f656-493c-893f-75a761935888",
      "name": "Statistical Anomaly Detection",
      "type": "n8n-nodes-base.code",
      "position": [
        -896,
        272
      ],
      "parameters": {
        "jsCode": "// Statistical Anomaly Detection for Oil Price Crisis Detection\n// Implements Z-score, moving average, volatility, and spike detection\n\nconst items = $input.all();\n\n// Helper function to calculate mean\nfunction calculateMean(values) {\n  return values.reduce((sum, val) => sum + val, 0) / values.length;\n}\n\n// Helper function to calculate standard deviation\nfunction calculateStdDev(values, mean) {\n  const squaredDiffs = values.map(val => Math.pow(val - mean, 2));\n  const variance = squaredDiffs.reduce((sum, val) => sum + val, 0) / values.length;\n  return Math.sqrt(variance);\n}\n\n// Helper function to calculate Z-score\nfunction calculateZScore(value, mean, stdDev) {\n  if (stdDev === 0) return 0;\n  return (value - mean) / stdDev;\n}\n\n// Helper function to calculate moving average\nfunction calculateMovingAverage(values, window = 5) {\n  if (values.length < window) return calculateMean(values);\n  const recentValues = values.slice(-window);\n  return calculateMean(recentValues);\n}\n\n// Extract price data from items\nconst priceData = items.map(item => ({\n  price: parseFloat(item.json.price || item.json.oilPrice || 0),\n  volume: parseFloat(item.json.volume || item.json.tradingVolume || 0),\n  timestamp: item.json.timestamp || new Date().toISOString()\n}));\n\n// Calculate statistics\nconst prices = priceData.map(d => d.price);\nconst volumes = priceData.map(d => d.volume);\n\nconst priceMean = calculateMean(prices);\nconst priceStdDev = calculateStdDev(prices, priceMean);\nconst volumeMean = calculateMean(volumes);\nconst volumeStdDev = calculateStdDev(volumes, volumeMean);\n\n// Calculate moving average (5-period)\nconst movingAverage = calculateMovingAverage(prices, 5);\n\n// Get current (latest) values\nconst currentPrice = prices[prices.length - 1];\nconst previousPrice = prices.length > 1 ? prices[prices.length - 2] : currentPrice;\nconst currentVolume = volumes[volumes.length - 1];\n\n// Calculate price change percentage\nconst priceChangePercent = previousPrice !== 0 \n  ? ((currentPrice - previousPrice) / previousPrice) * 100 \n  : 0;\n\n// Calculate Z-scores\nconst priceZScore = calculateZScore(currentPrice, priceMean, priceStdDev);\nconst volumeZScore = calculateZScore(currentVolume, volumeMean, volumeStdDev);\n\n// Calculate price volatility (coefficient of variation)\nconst priceVolatility = priceMean !== 0 ? (priceStdDev / priceMean) * 100 : 0;\n\n// Detect anomalies\nconst suddenPriceChange = Math.abs(priceChangePercent) > 5;\nconst volumeSpike = Math.abs(volumeZScore) > 2;\nconst priceAnomaly = Math.abs(priceZScore) > 2;\nconst highVolatility = priceVolatility > 10;\n\n// Calculate composite anomaly score (0-100)\nlet anomalyScore = 0;\n\n// Price Z-score contribution (0-30 points)\nanomalyScore += Math.min(Math.abs(priceZScore) * 10, 30);\n\n// Sudden price change contribution (0-25 points)\nif (suddenPriceChange) {\n  anomalyScore += Math.min(Math.abs(priceChangePercent) * 2, 25);\n}\n\n// Volume spike contribution (0-25 points)\nif (volumeSpike) {\n  anomalyScore += Math.min(Math.abs(volumeZScore) * 8, 25);\n}\n\n// Volatility contribution (0-20 points)\nanomalyScore += Math.min(priceVolatility * 2, 20);\n\n// Cap at 100\nanomalyScore = Math.min(anomalyScore, 100);\n\n// Determine overall anomaly flag\nconst isAnomaly = anomalyScore > 50 || suddenPriceChange || (priceAnomaly && volumeSpike);\n\n// Return enriched data with anomaly detection results\nreturn [{\n  json: {\n    // Original data\n    ...items[items.length - 1].json,\n    \n    // Statistical metrics\n    statistics: {\n      priceMean: priceMean.toFixed(2),\n      priceStdDev: priceStdDev.toFixed(2),\n      movingAverage: movingAverage.toFixed(2),\n      priceVolatility: priceVolatility.toFixed(2),\n      volumeMean: volumeMean.toFixed(2),\n      volumeStdDev: volumeStdDev.toFixed(2)\n    },\n    \n    // Current values\n    currentMetrics: {\n      price: currentPrice,\n      previousPrice: previousPrice,\n      priceChangePercent: priceChangePercent.toFixed(2),\n      volume: currentVolume,\n      priceZScore: priceZScore.toFixed(2),\n      volumeZScore: volumeZScore.toFixed(2)\n    },\n    \n    // Anomaly detection results\n    anomalyDetection: {\n      anomalyScore: Math.round(anomalyScore),\n      isAnomaly: isAnomaly,\n      flags: {\n        suddenPriceChange: suddenPriceChange,\n        volumeSpike: volumeSpike,\n        priceAnomaly: priceAnomaly,\n        highVolatility: highVolatility\n      },\n      details: {\n        priceChangeDetected: suddenPriceChange ? `${priceChangePercent.toFixed(2)}% change detected` : 'Normal',\n        volumeStatus: volumeSpike ? 'Volume spike detected' : 'Normal',\n        priceStatus: priceAnomaly ? 'Price anomaly detected' : 'Normal',\n        volatilityStatus: highVolatility ? 'High volatility' : 'Normal'\n      }\n    },\n    \n    // Timestamp\n    analysisTimestamp: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "44984f6f-6894-485f-8dfa-95f6b8710028",
      "name": "AI Trend & Geopolitical Analyzer",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -928,
        656
      ],
      "parameters": {
        "text": "=Analyze the following oil market data for geopolitical risks, supply disruptions, and crisis indicators. Consider: 1) News sentiment and geopolitical events, 2) OPEC production changes, 3) Shipping disruptions, 4) Historical patterns. Provide a risk assessment score (0-100) and key risk factors. Data: {{ JSON.stringify($json) }}",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "5a9fbd66-5014-4b87-a82e-33eb701282a6",
      "name": "Calculate Crisis Risk Score",
      "type": "n8n-nodes-base.code",
      "position": [
        -592,
        400
      ],
      "parameters": {
        "jsCode": "// Calculate Comprehensive Crisis Risk Score\n// Combines multiple data sources with weighted scoring\n\nconst items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n  const data = item.json;\n  \n  // Extract scores from previous nodes\n  const statisticalAnomalyScore = data.anomalyScore || 0;\n  const aiGeopoliticalScore = data.geopoliticalRiskScore || 0;\n  const priceVolatility = data.priceVolatility || 0;\n  const volumeChange = data.volumeChange || 0;\n  \n  // Normalize all scores to 0-100 scale if needed\n  const normalizedStatistical = Math.min(Math.max(statisticalAnomalyScore, 0), 100);\n  const normalizedGeopolitical = Math.min(Math.max(aiGeopoliticalScore, 0), 100);\n  const normalizedVolatility = Math.min(Math.max(priceVolatility, 0), 100);\n  const normalizedVolume = Math.min(Math.max(volumeChange, 0), 100);\n  \n  // Calculate weighted crisis risk score\n  const crisisRiskScore = (\n    (normalizedStatistical * 0.40) +\n    (normalizedGeopolitical * 0.40) +\n    (normalizedVolatility * 0.10) +\n    (normalizedVolume * 0.10)\n  );\n  \n  // Round to 2 decimal places\n  const finalScore = Math.round(crisisRiskScore * 100) / 100;\n  \n  // Determine alert level based on score\n  let alertLevel;\n  if (finalScore >= 70) {\n    alertLevel = 'critical';\n  } else if (finalScore >= 40) {\n    alertLevel = 'warning';\n  } else {\n    alertLevel = 'info';\n  }\n  \n  // Create detailed breakdown\n  const breakdown = {\n    statisticalAnomaly: {\n      score: normalizedStatistical,\n      weight: '40%',\n      contribution: Math.round(normalizedStatistical * 0.40 * 100) / 100\n    },\n    geopoliticalRisk: {\n      score: normalizedGeopolitical,\n      weight: '40%',\n      contribution: Math.round(normalizedGeopolitical * 0.40 * 100) / 100\n    },\n    priceVolatility: {\n      score: normalizedVolatility,\n      weight: '10%',\n      contribution: Math.round(normalizedVolatility * 0.10 * 100) / 100\n    },\n    volumeChanges: {\n      score: normalizedVolume,\n      weight: '10%',\n      contribution: Math.round(normalizedVolume * 0.10 * 100) / 100\n    }\n  };\n  \n  // Return comprehensive result\n  results.push({\n    json: {\n      ...data,\n      crisisRiskScore: finalScore,\n      alertLevel: alertLevel,\n      scoreBreakdown: breakdown,\n      timestamp: new Date().toISOString(),\n      scoringMethod: 'weighted_composite'\n    }\n  });\n}\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "f2612ed5-0943-4b8e-b480-47a9d85ebb66",
      "name": "Route by Alert Level",
      "type": "n8n-nodes-base.switch",
      "position": [
        -352,
        496
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Info",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "id-1",
                    "operator": {
                      "type": "number",
                      "operation": "lt"
                    },
                    "leftValue": "={{ $json.riskScore }}",
                    "rightValue": "={{ $('Workflow Configuration').first().json.warningThreshold }}"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Warning",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "id-2",
                    "operator": {
                      "type": "number",
                      "operation": "gte"
                    },
                    "leftValue": "={{ $json.riskScore }}",
                    "rightValue": "={{ $('Workflow Configuration').first().json.warningThreshold }}"
                  },
                  {
                    "id": "id-3",
                    "operator": {
                      "type": "number",
                      "operation": "lt"
                    },
                    "leftValue": "={{ $json.riskScore }}",
                    "rightValue": "={{ $('Workflow Configuration').first().json.criticalThreshold }}"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Critical",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "id-4",
                    "operator": {
                      "type": "number",
                      "operation": "gte"
                    },
                    "leftValue": "={{ $json.riskScore }}",
                    "rightValue": "={{ $('Workflow Configuration').first().json.criticalThreshold }}"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.3
    },
    {
      "id": "e8a59011-bdd3-4ba8-bfd4-3e1d586112e2",
      "name": "Format Info Alert",
      "type": "n8n-nodes-base.set",
      "position": [
        -128,
        336
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "alertLevel",
              "type": "string",
              "value": "INFO"
            },
            {
              "id": "id-2",
              "name": "alertTitle",
              "type": "string",
              "value": "Oil Market Update"
            },
            {
              "id": "id-3",
              "name": "alertMessage",
              "type": "string",
              "value": "=Risk Score: {{ $json.riskScore }} - {{ $json.summary }}"
            },
            {
              "id": "id-4",
              "name": "timestamp",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            },
            {
              "id": "id-5",
              "name": "color",
              "type": "string",
              "value": "#17a2b8"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8833b06e-b711-49da-a213-d6d59547d785",
      "name": "Format Warning Alert",
      "type": "n8n-nodes-base.set",
      "position": [
        -128,
        480
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "alertLevel",
              "type": "string",
              "value": "WARNING"
            },
            {
              "id": "id-2",
              "name": "alertTitle",
              "type": "string",
              "value": "\u26a0\ufe0f Oil Market Warning Alert"
            },
            {
              "id": "id-3",
              "name": "alertMessage",
              "type": "string",
              "value": "=Risk Score: {{ $json.riskScore }} - {{ $json.summary }}. Factors: {{ $json.riskFactors }}"
            },
            {
              "id": "id-4",
              "name": "timestamp",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            },
            {
              "id": "id-5",
              "name": "color",
              "type": "string",
              "value": "#ffc107"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "b912a259-b218-4ea7-9fc9-2cfb800ff5f6",
      "name": "Format Critical Alert",
      "type": "n8n-nodes-base.set",
      "position": [
        -128,
        640
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "alertLevel",
              "type": "string",
              "value": "CRITICAL"
            },
            {
              "id": "id-2",
              "name": "alertTitle",
              "type": "string",
              "value": "\ud83d\udea8 CRITICAL Oil Market Crisis Alert"
            },
            {
              "id": "id-3",
              "name": "alertMessage",
              "type": "string",
              "value": "=URGENT - Risk Score: {{ $json.riskScore }} - {{ $json.summary }}. Immediate action required. Factors: {{ $json.riskFactors }}"
            },
            {
              "id": "id-4",
              "name": "timestamp",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            },
            {
              "id": "id-5",
              "name": "color",
              "type": "string",
              "value": "#dc3545"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "59216b80-04b3-401a-806c-1a8b994c9734",
      "name": "Send Email Alert",
      "type": "n8n-nodes-base.gmail",
      "position": [
        176,
        400
      ],
      "parameters": {
        "sendTo": "={{ $('Workflow Configuration').first().json.alertEmailRecipients }}",
        "message": "=<h2>{{ $json.alertTitle }}</h2><p>{{ $json.alertMessage }}</p><p><strong>Timestamp:</strong> {{ $json.timestamp }}</p><p><strong>Details:</strong> {{ JSON.stringify($json, null, 2) }}</p>",
        "options": {},
        "subject": "={{ $json.alertTitle }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "6bba8b1d-6bdc-4a53-9910-49e795c5a23b",
      "name": "Send Slack Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        192,
        576
      ],
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Workflow Configuration').first().json.slackChannel }}"
        },
        "attachments": [
          {
            "text": "={{ $json.alertTitle }}\n{{ $json.alertMessage }}\nTimestamp: {{ $json.timestamp }}",
            "color": "={{ $json.color }}",
            "fallback": "Alert Notification"
          }
        ],
        "messageType": "attachment",
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "360d44b6-be4b-4d9e-99a7-29b0cad84364",
      "name": "Store Alert History",
      "type": "n8n-nodes-base.postgres",
      "position": [
        240,
        64
      ],
      "parameters": {
        "table": {
          "__rl": true,
          "mode": "name",
          "value": "alert_history"
        },
        "schema": {
          "__rl": true,
          "mode": "list",
          "value": "public"
        },
        "columns": {
          "value": {},
          "mappingMode": "autoMapInputData"
        },
        "options": {}
      },
      "typeVersion": 2.6
    },
    {
      "id": "1d8ffdde-0611-478b-bac4-8d861234528a",
      "name": "Send to Dashboard API",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        64,
        64
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration').first().json.dashboardApiUrl }}",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ $json }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "0fad6e92-4d61-4b28-9951-714e23dffd6b",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2224,
        -160
      ],
      "parameters": {
        "width": 336,
        "height": 336,
        "content": "## How It Works\nScheduled runs collect data from oil markets, global shipping movements, news sources, and official reports. The system performs statistical checks to detect anomalies and volatility shifts. An AI-driven geopolitical model evaluates emerging risks and assigns a crisis score. Based on severity thresholds, results are routed to the appropriate alert channels for rapid response."
      },
      "typeVersion": 1
    },
    {
      "id": "9019b097-ef4c-4c90-a96c-1d02bd941659",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1872,
        -160
      ],
      "parameters": {
        "width": 464,
        "height": 272,
        "content": "## Setup Steps\n1. **Data Sources:** Connect the oil price API, OPEC report feeds, shipping databases, and news sources.\n2. **AI Model:** Configure the OpenRouter ChatGPT model for geopolitical and risk analysis.\n3. **Alerts:** Define severity rules and route alerts to Email, Slack, or Dashboard APIs.\n4. **Storage:** Configure a database for historical records, audit logging, and trend tracking."
      },
      "typeVersion": 1
    },
    {
      "id": "1125bbc0-11b7-464b-b475-252b593719ec",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1376,
        -144
      ],
      "parameters": {
        "color": 6,
        "width": 672,
        "height": 208,
        "content": "\n## Prerequisites\nOil market API credentials; news feed access; OPEC data source; OpenRouter API key; Slack/email/dashboard integrations\n\n## Use Cases\nSupply chain risk monitoring; energy market crisis detection; geopolitical threat assessment; trader decision support; operational risk management"
      },
      "typeVersion": 1
    },
    {
      "id": "d724f033-8c42-4d3e-b95b-1104e6a68985",
      "name": "OpenRouter Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        -928,
        800
      ],
      "parameters": {
        "model": "qwen/qwq-32b",
        "options": {}
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "5af012b5-81f3-4624-825d-e32befc29e70",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -672,
        -144
      ],
      "parameters": {
        "color": 3,
        "width": 624,
        "height": 192,
        "content": "## Customization\nAdjust risk thresholds; add market data sources; modify alert routing rules \n\n## Benefits\nReduces crisis detection lag 90%; consolidates fragmented data; enables proactive response "
      },
      "typeVersion": 1
    },
    {
      "id": "968857a5-92e6-4b8b-9b23-d02acbf84685",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2224,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 496,
        "content": "## Schedule Data Collection\nTriggers workflow every 5 minutes to fetch oil markets, OPEC reports, shipping data, news feeds\nWhy: 5-minute cadence catches early warnings before markets react"
      },
      "typeVersion": 1
    },
    {
      "id": "096bb892-a30a-42f1-ab45-5c131e5256a9",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1488,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 416,
        "content": "## Aggregate Shipping Data\nCollects vessel tracking, route changes, port delays, freight costs\nWhy: Shipping delays signal supply disruptions or geopolitical tensions before announcements"
      },
      "typeVersion": 1
    },
    {
      "id": "9c572d55-c40a-411e-ae34-09c6828baf87",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1968,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 464,
        "height": 688,
        "content": "## Fetch Oil Price Data\nRetrieves spot prices, futures, contract rates from multiple exchanges\nWhy: Oil prices move first in crises; multiple sources catch geographic and contract divergence"
      },
      "typeVersion": 1
    },
    {
      "id": "cc635d3b-3751-443c-9e89-4690584e4a77",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1216,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 464,
        "content": "##  Clean Multi-Source Data\nStandardizes oil prices, shipping metrics, dates, timestamps into unified schema\nWhy: Fragmented data prevents analysis"
      },
      "typeVersion": 1
    },
    {
      "id": "55123cd3-ccb6-49ad-abf1-0a92c6526eba",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -976,
        80
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 336,
        "content": "## Statistical Anomaly Detection\nIdentifies unusual shifts: price volatility spikes, shipping jumps, OPEC deviations, news volume surges\nWhy: Quantitative anomalies flag problems objectively \n"
      },
      "typeVersion": 1
    },
    {
      "id": "b19afa6f-008c-4e56-8fb8-75901269fec3",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -976,
        432
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 512,
        "content": "## AI Crisis Risk Model\nUses Claude to analyze geopolitical tensions, sanctions risk\nWhy: AI connects disparate signals into coherent risk narrative; combines quantitative anomalies with context"
      },
      "typeVersion": 1
    },
    {
      "id": "90af94d4-ca13-4d6a-84fe-fadf4b03c6f4",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -640,
        144
      ],
      "parameters": {
        "color": 7,
        "height": 448,
        "content": "\n## Score Emerging Threats\nAssigns crisis risk rating: threat probability, impact, time horizon\nWhy: Scoring prioritizes analyst time on highest-threat scenarios"
      },
      "typeVersion": 1
    },
    {
      "id": "db334106-5b75-452c-b5de-1d084ef08267",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -384,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 688,
        "content": "## Route Critical Alerts to Traders\nSends high-risk signals (8-10 severity) immediately via Slack and dashboard\nWhy: Traders need sub-minute warnings to adjust positions; speed determines profit/loss"
      },
      "typeVersion": 1
    },
    {
      "id": "0643b2b5-13f4-4437-badc-61878b0aec03",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        16,
        -112
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 320,
        "content": "## Alerts to Dashboard & Storage\nPosts lower-severity signals (1-4) to dashboard for shift review\nWhy: Lower-risk items inform strategy without urgent action; creates searchable record"
      },
      "typeVersion": 1
    },
    {
      "id": "52f35260-0719-48dc-aa3d-877a90f1eb35",
      "name": "Sticky Note13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        32,
        224
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 528,
        "content": "\n## Alert to Email & Slack\nLogs alerts with timestamps, reasoning, sources, routing decisions\nWhy: Regulators require monitoring evidence; audit trail proves due diligence"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "b07a4dc6-2c1b-4584-ab5f-414cc35d0484",
  "connections": {
    "Format Info Alert": {
      "main": [
        [
          {
            "node": "Send to Dashboard API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch OPEC Reports": {
      "main": [
        [
          {
            "node": "Merge All Data Sources",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Format Warning Alert": {
      "main": [
        [
          {
            "node": "Send Email Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Slack Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send to Dashboard API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Alert Level": {
      "main": [
        [
          {
            "node": "Format Info Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Format Warning Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Format Critical Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Critical Alert": {
      "main": [
        [
          {
            "node": "Send Email Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Slack Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send to Dashboard API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Trend & Geopolitical Analyzer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Send to Dashboard API": {
      "main": [
        [
          {
            "node": "Store Alert History",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge All Data Sources": {
      "main": [
        [
          {
            "node": "Data Cleaning & Preprocessing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Workflow Configuration": {
      "main": [
        [
          {
            "node": "Fetch Oil Price Data (API)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch OPEC Reports",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Shipping Data",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch News Feed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Every 5 Minutes": {
      "main": [
        [
          {
            "node": "Workflow Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Oil Price Data (API)": {
      "main": [
        [
          {
            "node": "Merge All Data Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Crisis Risk Score": {
      "main": [
        [
          {
            "node": "Route by Alert Level",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data Cleaning & Preprocessing": {
      "main": [
        [
          {
            "node": "Statistical Anomaly Detection",
            "type": "main",
            "index": 0
          },
          {
            "node": "AI Trend & Geopolitical Analyzer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Statistical Anomaly Detection": {
      "main": [
        [
          {
            "node": "Calculate Crisis Risk Score",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Trend & Geopolitical Analyzer": {
      "main": [
        [
          {
            "node": "Calculate Crisis Risk Score",
            "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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Scheduled runs collect data from oil markets, global shipping movements, news sources, and official reports. The system performs statistical checks to detect anomalies and volatility shifts. An AI-driven geopolitical model evaluates emerging risks and assigns a crisis score.…

Source: https://n8n.io/workflows/10763/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Automates sales data analysis and strategic insight generation for sales managers and strategists needing actionable intelligence. Fetches multi-source data from sales, marketing, and financial system

HTTP Request, Agent, OpenAI Chat +6
AI & RAG

This workflow automates financial transaction surveillance by monitoring multiple payment systems, analyzing transaction patterns with AI, and triggering instant fraud alerts. Designed for finance tea

HTTP Request, Agent, OpenAI Chat +4
AI & RAG

Automates financial risk evaluation by intelligently consolidating information from five critical sources: financial, operational, legal, insurance, and regulatory systems. Hourly triggers enable cont

HTTP Request, Agent, Output Parser Structured +3
AI & RAG

This workflow automates competitive real estate pricing analysis by combining multiple MLS data sources with AI-powered market intelligence. Designed for real estate professionals, property managers,

HTTP Request, Agent, Output Parser Structured +4
AI & RAG

Complete PostgreSQL-backed system: Keyword scoring → AI research → Multi-part content generation → fal.ai Nano Banana image generation → WordPress publishing

WordPress, OpenAI, Perplexity +8