{
  "name": "uptime kuma alert",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "REPLACE_WITH_WEBHOOK_PATH",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -2320,
        192
      ],
      "id": "af61957f-a7bc-48b6-9a75-ed6a9a9e3469",
      "name": "Webhook"
    },
    {
      "parameters": {
        "url": "={{$json.body.monitor.url}}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "User-Agent",
              "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127 Safari/537.36"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -1040,
        96
      ],
      "id": "32b53fbc-cd1c-4af1-9631-38dbe438f3c0",
      "name": "HTTP Request"
    },
    {
      "parameters": {
        "modelName": "models/gemini-2.5-pro",
        "options": {}
      },
      "id": "eb6ead1d-5512-4686-941a-e72ec72a98fe",
      "name": "GeminiModel1",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -800,
        336
      ],
      "typeVersion": 1,
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=\u4f60\u662f SRE \u503c\u73ed\u52a9\u624b\u3002\u4f60\u5c07\u770b\u5230\u76e3\u63a7\u540d\u7a31\u3001URL\uff0c\u4ee5\u53ca HTTP \u8acb\u6c42\u7d50\u679c\uff08\u72c0\u614b\u78bc\u3001\u932f\u8aa4\u3001\u56de\u61c9\u6a19\u982d\u3001\u5167\u5bb9\u7247\u6bb5\uff09\u3002\n\n\u4f60\u7684\u4efb\u52d9\uff1a\u50c5\u5728\u300c\u9700\u8981\u4eba\u5de5\u8655\u7406\u7684\u751f\u7522\u6545\u969c\u300d\u6642\u56de\u8986\u4e00\u6bb5\u7e41\u9ad4\u4e2d\u6587\u8aaa\u660e\uff08\u2264180\u5b57\uff09\uff0c\n\u76f4\u63a5\u8aaa\u6e05\u695a\uff1a\u54ea\u500b\u7ad9\u9ede\u3001\u51fa\u4e86\u4ec0\u9ebc\u554f\u984c\u3001\u932f\u8aa4\u78bc\u3001\u53ef\u80fd\u539f\u56e0\uff0f\u4e0b\u4e00\u6b65\u5efa\u8b70\uff08\u7c21\u77ed\u5373\u53ef\uff09\u3002\n\u5982\u679c\u4e0d\u662f\u9700\u8981\u4eba\u5de5\u8655\u7406\u7684\u6545\u969c\uff08\u4f8b\u5982\uff1a2xx/3xx\uff1b\u53ef\u9810\u671f\u7684 4xx \u5982 401/403/404\uff1b\u77ed\u66ab\u9650\u6d41\uff1b\u5df2\u6062\u5fa9\uff09\uff0c\u8acb\u8f38\u51fa\u5b57\u4e32SILENT\uff08\u4e0d\u8981\u6709\u4efb\u4f55\u5176\u4ed6\u6587\u5b57\u548c\u7b26\u865f\u4ee5\u53ca\u7a7a\u683c\uff09\u3002\n\u82e5\u6c92\u6709\u72c0\u614b\u78bc\u4f46\u5b58\u5728\u9023\u7dda\u932f\u8aa4\uff0c\u8acb\u8aaa\u660e\u300c\u932f\u8aa4\u78bc\uff1a\u7121\uff08\u9023\u7dda\u5931\u6557\uff0f\u903e\u6642\uff09\u300d\u3002\n\n\u3010\u76e3\u63a7\u3011\n\u540d\u7a31\uff1a{{ $node[\"Webhook\"].json.monitor.name }}\nURL\uff1a{{ $node[\"Webhook\"].json.monitor.url }}\n\n\u3010HTTP\u3011\n\u72c0\u614b\u78bc\uff1a{{ $json.statusCode }}\n\u72c0\u614b\u8a9e\uff1a{{ $json.statusMessage }}\n\u932f\u8aa4\uff1a{{ $json.error }}\n\u56de\u61c9\u6a19\u982d\uff08\u5b8c\u6574 JSON\uff09\uff1a{{ JSON.stringify($json.headers || {}, null, 2) }}\n\u5167\u5bb9\u7247\u6bb5\uff08\u82e5\u70ba\u5b57\u4e32\u5247\u539f\u6a23\u8f38\u51fa\uff0c\u5426\u5247\u8f49 JSON\uff09\uff1a{{\n  (typeof $json.data === \"string\"\n    ? $json.data\n    : JSON.stringify($json.data ?? $json.body ?? {}, null, 2))\n}}",
        "batching": {}
      },
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        -800,
        96
      ],
      "id": "a239c060-d652-4d7d-a12d-0dbdf30e6c07",
      "name": "Basic LLM Chain"
    },
    {
      "parameters": {
        "jsCode": "// \u8b80 LLM \u539f\u6a23\u8f38\u51fa\nconst raw = String($json.text ?? $json.output ?? $json.response ?? \"\").trim();\n\n// \u56b4\u683c\u5354\u8b70\uff1a\u53ea\u6709\u7cbe\u78ba \"SILENT\"\uff08\u5927\u5c0f\u5beb\u4e0d\u654f\u611f\uff09\u624d\u4e0d\u901a\u77e5\nconst isSilent = /^SILENT$/i.test(raw);\n\nreturn {\n  shouldNotify: !isSilent,\n  message: isSilent ? \"\" : raw,\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -448,
        96
      ],
      "id": "5df05e6e-c63a-43c0-9cd9-9ea7f3f6ec48",
      "name": "Code"
    },
    {
      "parameters": {
        "amount": 3,
        "unit": "minutes"
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        -1744,
        128
      ],
      "id": "fd835730-4de6-4fb6-8475-a14c79b312de",
      "name": "Wait"
    },
    {
      "parameters": {
        "jsCode": "// Code1 \u2014 \u6536\u5230 UP\uff0c\u50c5\u8a18\u9304\u300c\u6700\u5f8c\u4e00\u6b21 UP \u7684\u6642\u9593\u6233\u300d\nconst store = $getWorkflowStaticData('global');\nif (!store.alerts) store.alerts = {};\n\nconst body = $json.body ?? {};\nconst monitorId =\n  body.monitorID ??\n  $json.monitor?.id ??\n  $json.monitorID ??\n  'unknown';\n\nconst now = Date.now();\nconst rec = store.alerts[monitorId] ?? (store.alerts[monitorId] = {\n  lastUpAt: 0,\n  lastDownAt: 0,\n  notified: {}\n});\nrec.lastUpAt = now;\n\nreturn [{ json: { ...$json, monitorId, lastUpAt: now } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1920,
        256
      ],
      "id": "a6c0463a-e343-4215-a57a-733d9540f0b1",
      "name": "Code1"
    },
    {
      "parameters": {
        "jsCode": "// Code2 \u2014 \u8a18\u9304\u4e00\u6b21\u300cDOWN \u4e8b\u4ef6\u300d\u7684\u6642\u9593\u6233\uff0c\u4e26\u7522\u751f\u4e8b\u4ef6ID\nconst store = $getWorkflowStaticData('global');\nif (!store.alerts) store.alerts = {};\n\nconst body = $json.body ?? {};\nconst monitorId =\n  body.monitorID ??\n  $json.monitor?.id ??\n  $json.monitorID ??\n  'unknown';\n\nconst downAt = Date.now();                    // \u9019\u6b21 DOWN \u7684\u6642\u9593\u6233\uff08\u6beb\u79d2\uff09\nconst incidentId = `${monitorId}:${downAt}`;  // \u672c\u6b21\u4e8b\u4ef6ID\uff08\u7528\u65bc\u53bb\u91cd\uff09\n\n// \u521d\u59cb\u5316/\u66f4\u65b0\u8a72\u76e3\u63a7\u7684\u5168\u57df\u72c0\u614b\uff08\u53ea\u5b58\u6700\u5f8c\u4e00\u6b21 UP/ DOWN \u7684\u6642\u9593\u6233\uff09\nconst rec = store.alerts[monitorId] ?? (store.alerts[monitorId] = {\n  lastUpAt: 0,\n  lastDownAt: 0,\n  notified: {}     // \u7528 incidentId \u505a\u53bb\u91cd\uff0c\u907f\u514d\u91cd\u8986\u901a\u77e5\n});\nrec.lastDownAt = downAt;\n\nreturn [{\n  json: {\n    ...$json,              // \u4fdd\u7559\u539f\u59cb webhook \u8cc7\u6599\n    monitorId,\n    downAt,\n    incidentId,\n    recovered: false,      // \u9810\u8a2d\u672a\u6062\u5fa9\uff08\u50c5\u4f54\u4f4d\uff0c\u771f\u6b63\u5224\u5b9a\u5728 Code3\uff09\n    alreadyNotified: false // \u9810\u8a2d\u672a\u901a\u77e5\uff08\u771f\u6b63\u5224\u5b9a\u5728 Code3\uff09\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1920,
        128
      ],
      "id": "af95469c-beba-447e-82c9-f37b7e90517c",
      "name": "Code2"
    },
    {
      "parameters": {
        "jsCode": "// Code3 \u2014 Wait \u4e4b\u5f8c\u624d\u5224\u65b7\u662f\u5426\u6062\u5fa9\uff1a\n// \u898f\u5247\uff1a\u82e5\u300c\u6700\u5f8c\u4e00\u6b21 UP \u7684\u6642\u9593\u6233 >= \u672c\u6b21 DOWN \u7684\u6642\u9593\u6233\u300d\u2192 recovered = true\n//      \u5426\u5247 recovered = false\nconst store = $getWorkflowStaticData('global');\nif (!store.alerts) store.alerts = {};\n\nconst monitorId =\n  $json.monitorId ??\n  $json.body?.monitorID ??\n  $json.monitor?.id ??\n  'unknown';\n\nconst downAt = Number($json.downAt ?? 0);\nconst incidentId = String($json.incidentId ?? `${monitorId}:${downAt}`);\n\nconst rec = store.alerts[monitorId] ?? { lastUpAt: 0, lastDownAt: 0, notified: {} };\nconst lastUpAt = Number(rec.lastUpAt || 0);\n\n// \u662f\u5426\u5728\u9019\u6b21 DOWN \u4e4b\u5f8c\u51fa\u73fe\u904e UP\nconst recovered = lastUpAt >= downAt;\n\n// \u4e8b\u4ef6\u7d1a\u5225\u7684\u53bb\u91cd\uff1a\u540c\u4e00 incidentId \u53ea\u901a\u77e5\u4e00\u6b21\nconst alreadyNotified = !!(rec.notified && rec.notified[incidentId]);\n\n// \u82e5\u672a\u6062\u5fa9\u4e14\u5c1a\u672a\u901a\u77e5\uff0c\u505a\u53bb\u91cd\u6a19\u8a18\uff08\u767c\u9001\u5f8c If2 \u6703\u653e\u884c\u5230\u901a\u77e5\u7bc0\u9ede\uff09\nif (!recovered && !alreadyNotified) {\n  rec.notified = rec.notified || {};\n  rec.notified[incidentId] = true;\n}\n\n// \u82e5\u5df2\u6062\u5fa9\uff0c\u53ef\u9078\u64c7\u6e05\u7406\u904e\u671f\u7684\u53bb\u91cd\u6a19\u8a18\uff08\u4fdd\u5b88\uff1a\u4fdd\u7559\uff0c\u4ee5\u514d race\uff09\nreturn [{\n  json: {\n    ...$json,\n    monitorId,\n    downAt,\n    incidentId,\n    recovered,        // \u7d66 If2 \u7528\n    alreadyNotified   // \u7d66 If2 \u7528\uff08\u4e8b\u4ef6\u53bb\u91cd\uff09\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1568,
        128
      ],
      "id": "c1f4877b-bee7-4b37-96e0-3fd153a6ba5b",
      "name": "Code3"
    },
    {
      "parameters": {
        "chatId": "REPLACE_WITH_DEMO_OPERATOR_CHAT_ID",
        "text": "={{ $json.message }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        0,
        192
      ],
      "id": "d38c7e2d-4a3b-485e-a956-edbb8b800414",
      "name": "Send Demo Operator",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "chatId": "REPLACE_WITH_DEMO_TEAM_CHAT_ID",
        "text": "=@demo_oncall {{ $json.message }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        0,
        0
      ],
      "id": "87b2f32b-f4dd-4322-a2d5-23b5389283d2",
      "name": "Send Demo Team",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "c3d6a352-e9fd-4f08-aae0-94124f557444",
              "leftValue": "={{ $json[\"body\"][\"heartbeat\"][\"status\"].toString() }}",
              "rightValue": "=0",
              "operator": {
                "type": "string",
                "operation": "equals",
                "name": "filter.operator.equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -2160,
        192
      ],
      "id": "8ecfb5dc-e50f-43f7-8085-2c72a3f818db",
      "name": "If down"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "46a209ab-9f05-4ed4-893b-0d4a2d0ffe8a",
              "leftValue": "={{$json.recovered.toString()}}",
              "rightValue": "=false",
              "operator": {
                "type": "string",
                "operation": "equals",
                "name": "filter.operator.equals"
              }
            },
            {
              "id": "d30ff03e-c33f-4cc2-9d7c-d263574cc832",
              "leftValue": "={{$json.alreadyNotified.toString()}}",
              "rightValue": "=false",
              "operator": {
                "type": "string",
                "operation": "equals",
                "name": "filter.operator.equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -1376,
        128
      ],
      "id": "dd24ac24-15bd-4e83-a3e2-ca8f81d21bf1",
      "name": "If no up"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "b4bbeb53-347f-438e-b1ad-3f20e599caec",
              "leftValue": "={{$json.shouldNotify.toString()}}",
              "rightValue": "true",
              "operator": {
                "type": "string",
                "operation": "equals",
                "name": "filter.operator.equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -272,
        96
      ],
      "id": "1673fdfc-6348-48cb-b0cb-8b54c8e9391d",
      "name": "If need alert"
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "If down",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GeminiModel1": {
      "ai_languageModel": [
        [
          {
            "node": "Basic LLM Chain",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Basic LLM Chain",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Basic LLM Chain": {
      "main": [
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code": {
      "main": [
        [
          {
            "node": "If need alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code2": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "Code3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code3": {
      "main": [
        [
          {
            "node": "If no up",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If down": {
      "main": [
        [
          {
            "node": "Code2",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Code1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If no up": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If need alert": {
      "main": [
        [
          {
            "node": "Send Demo Operator",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Demo Team",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "REPLACE_WITH_VERSION_ID",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "REPLACE_WITH_WORKFLOW_ID",
  "tags": []
}