AutomationFlowsAI & RAG › Nola Alert Triage

Nola Alert Triage

NOLA Alert Triage. Uses agent, lmChatAnthropic, toolCode. Webhook trigger; 8 nodes.

Webhook trigger★★★★☆ complexityAI-powered8 nodesAgentAnthropic ChatTool Code
AI & RAG Trigger: Webhook Nodes: 8 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow follows the Agent → Anthropic Chat 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
{
  "name": "NOLA Alert Triage",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "grafana-alert",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-node",
      "name": "Grafana Alert Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "respondWith": "text",
        "responseBody": "ok",
        "options": {}
      },
      "id": "respond-node",
      "name": "Respond to Grafana",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        460,
        160
      ]
    },
    {
      "parameters": {
        "agent": "conversationalAgent",
        "promptType": "define",
        "text": "={{ $json.triagePrompt }}",
        "options": {
          "systemMessage": "You are NOLA (Network Operations & Lab Assistant), an AI monitoring assistant for a self-hosted homelab called Galaxy Lab.\n\nYour job is to triage Grafana/Loki alerts and provide concise, actionable analysis. You have access to query_loki to pull relevant log context.\n\nWhen triaging an alert:\n1. Use query_loki to pull the relevant logs around the alert (use the time window and host from the alert context)\n2. Analyze what's actually happening \u2014 is this noise, a real issue, or something in between?\n3. Identify any patterns (same IP, same process, recurring issue)\n4. Give a clear verdict: Noise / Watch / Action Required\n5. Suggest what (if anything) to do\n\nKeep your response under 400 words. Be direct and technical \u2014 Chance is a 20-year network/security engineer who wants signal, not fluff.\n\nFormat your Discord message like this:\n\ud83d\udd14 **[AlertName]** \u2014 [Verdict: Noise/Watch/Action Required]\n**Host:** [hostname]\n**What happened:** [1-2 sentences]\n**Evidence:** [key log lines or patterns]\n**Recommendation:** [what to do, or \"No action needed\"]\n\nDo NOT include the full raw alert JSON in your message. Be concise."
        }
      },
      "id": "nola-agent-node",
      "name": "NOLA Triage Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2,
      "position": [
        460,
        380
      ]
    },
    {
      "parameters": {
        "model": "={{ $env.NOLA_MODEL }}",
        "options": {
          "temperature": 0.2
        }
      },
      "id": "claude-node",
      "name": "Claude",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.3,
      "position": [
        340,
        560
      ],
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "name": "query_loki",
        "description": "Query Loki for logs using LogQL. Use this to get log context around an alert. Label examples: {job=\"syslog\", host=\"stop\"}, {job=\"syslog\", host=\"halt.universe\"}. For firewall blocks: {job=\"syslog\"} |= \"filterlog\" |= \",block,\". For SSH: {job=\"syslog\"} |= \"sshd\". For WireGuard: {job=\"syslog\"} |= \"wireguard\".",
        "parametersUi": {
          "parameter": [
            {
              "name": "query",
              "description": "LogQL query string"
            },
            {
              "name": "since",
              "description": "Lookback window: 15m, 1h, 6h (default 30m)"
            },
            {
              "name": "limit",
              "description": "Max log lines to return (default 50)"
            }
          ]
        },
        "jsCode": "const LOKI_URL = process.env.LOKI_URL || 'http://loki.galaxy:3100';\nconst query = $parameter.query;\nconst since = $parameter.since || '30m';\nconst limit = $parameter.limit || 50;\n\nconst params = new URLSearchParams({\n  query,\n  since,\n  limit: String(limit),\n  direction: 'backward'\n});\n\nconst response = await $helpers.httpRequest({\n  method: 'GET',\n  url: `${LOKI_URL}/loki/api/v1/query_range?${params}`,\n  headers: { 'Content-Type': 'application/json' }\n});\n\nconst streams = response?.data?.result || [];\nif (streams.length === 0) return 'No logs found for query: ' + query;\n\nconst lines = streams.flatMap(s => s.values.map(v => v[1])).slice(0, limit);\nreturn `Found ${lines.length} log lines:\\n` + lines.join('\\n');"
      },
      "id": "loki-tool-node",
      "name": "query_loki",
      "type": "@n8n/n8n-nodes-langchain.toolCode",
      "typeVersion": 1.1,
      "position": [
        580,
        560
      ]
    },
    {
      "parameters": {
        "jsCode": "const body = $input.first().json.body || $input.first().json;\n\n// Handle both Grafana unified alerting and legacy formats\nconst alerts = body.alerts || [];\nconst status = body.status || 'firing';\n\nif (alerts.length === 0) {\n  return [{ json: { skip: true, reason: 'No alerts in payload' } }];\n}\n\nconst results = [];\nfor (const alert of alerts) {\n  const alertName = alert.labels?.alertname || 'Unknown Alert';\n  const severity = alert.labels?.severity || 'unknown';\n  const host = (alert.labels?.instance || alert.labels?.host || 'unknown').replace(/:\\d+$/, '');\n  const summary = alert.annotations?.summary || alert.annotations?.description || '';\n  const state = alert.status || status;\n  const startsAt = alert.startsAt || new Date().toISOString();\n\n  // Build a clear prompt for NOLA\n  const triagePrompt = `A Grafana alert has fired. Please triage it.\n\n**Alert:** ${alertName}\n**Status:** ${state}\n**Severity:** ${severity}\n**Host:** ${host}\n**Summary:** ${summary}\n**Fired at:** ${startsAt}\n\nPlease query Loki for relevant context (use a 30m window around the alert time on the relevant host), analyze what's happening, and post your triage verdict to Discord using post_to_discord.`;\n\n  results.push({\n    json: {\n      alertName,\n      severity,\n      host,\n      summary,\n      state,\n      startsAt,\n      triagePrompt,\n      skip: false\n    }\n  });\n}\n\nreturn results;"
      },
      "id": "parse-node",
      "name": "Parse Alert Payload",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true
          },
          "conditions": [
            {
              "leftValue": "={{ $json.skip }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ]
        }
      },
      "id": "skip-check-node",
      "name": "Skip if empty",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "name": "post_to_discord",
        "description": "Post NOLA's triage analysis to the Discord alert channel.",
        "jsCode": "const WEBHOOK_URL = process.env.DISCORD_WEBHOOK_URL;\nif (!WEBHOOK_URL) return 'Discord webhook URL not configured';\n\nconst message = $parameter.message;\nconst response = await $helpers.httpRequest({\n  method: 'POST',\n  url: WEBHOOK_URL,\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ content: message })\n});\nreturn `Posted to Discord (status: ${response.statusCode || 'ok'})`;"
      },
      "id": "discord-tool-node",
      "name": "post_to_discord",
      "type": "@n8n/n8n-nodes-langchain.toolCode",
      "typeVersion": 1.1,
      "position": [
        820,
        560
      ]
    }
  ],
  "connections": {
    "Grafana Alert Webhook": {
      "main": [
        [
          {
            "node": "Respond to Grafana",
            "type": "main",
            "index": 0
          },
          {
            "node": "Parse Alert Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Alert Payload": {
      "main": [
        [
          {
            "node": "Skip if empty",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip if empty": {
      "main": [
        [],
        [
          {
            "node": "NOLA Triage Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude": {
      "ai_languageModel": [
        [
          {
            "node": "NOLA Triage Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "query_loki": {
      "ai_tool": [
        [
          {
            "node": "NOLA Triage Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "post_to_discord": {
      "ai_tool": [
        [
          {
            "node": "NOLA Triage Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [
    {
      "name": "nola"
    },
    {
      "name": "alerts"
    }
  ]
}

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

NOLA Alert Triage. Uses agent, lmChatAnthropic, toolCode. Webhook trigger; 8 nodes.

Source: https://github.com/iamgadgetman/nola/blob/main/workflows/alert-triage.json — 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

Lead Pipeline v3.0. Uses httpRequest, agent, lmChatAnthropic, toolThink. Webhook trigger; 77 nodes.

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

Tired of grinding out YouTube content? This n8n workflow turns AI into your personal video factory—creating engaging, faceless shorts on autopilot. Perfect for creators, marketers, or side-hustlers lo

HTTP Request, Google Drive, Google Sheets +6
AI & RAG

Faceless YouTube Generator. Uses httpRequest, limit, googleDrive, googleSheets. Webhook trigger; 49 nodes.

HTTP Request, Google Drive, Google Sheets +7
AI & RAG

This workflow automates end-to-end marketing campaign management for digital marketing teams and agencies executing multi-channel strategies. It solves the complex challenge of coordinating personaliz

Agent, Anthropic Chat, Output Parser Structured +3
AI & RAG

🤖 DMO Claw. Uses executeWorkflowTrigger, postgres, agent, lmChatAnthropic. Webhook trigger; 37 nodes.

Execute Workflow Trigger, Postgres, Agent +3