Skip to content

In n8n, copy this file and paste it onto the canvas, or use Import from File.

{
  "name": "12 - Uptime Monitor",
  "nodes": [
    {
      "parameters": {
        "rule": { "interval": [{ "field": "minutes", "minutesInterval": 5 }] }
      },
      "id": "m12-schedule",
      "name": "Every 5 Minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [0, 0]
    },
    {
      "parameters": {
        "path": "ops/health",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "m12-webhook",
      "name": "Health API",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [0, 200],
      "webhookId": "course-12-health"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "u1",
              "name": "urls",
              "value": "={{ $json.query?.urls ? $json.query.urls.split(',').map(u => u.trim()).filter(Boolean) : ['http://localhost:5678/healthz', 'https://example.com'] }}",
              "type": "array"
            }
          ]
        },
        "options": {}
      },
      "id": "m12-settings",
      "name": "Sites to Check (edit me)",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [220, 100]
    },
    {
      "parameters": { "fieldToSplitOut": "urls", "options": { "destinationFieldName": "url" } },
      "id": "m12-split",
      "name": "One Item per Site",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [440, 100]
    },
    {
      "parameters": {
        "url": "={{ $json.url }}",
        "options": {
          "response": { "response": { "fullResponse": true, "neverError": true, "responseFormat": "text" } },
          "timeout": 8000,
          "redirect": { "redirect": { "maxRedirects": 3 } }
        }
      },
      "id": "m12-check",
      "name": "Check Site",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [660, 100],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            { "id": "e1", "name": "url", "value": "={{ $('One Item per Site').item.json.url }}", "type": "string" },
            { "id": "e2", "name": "status", "value": "={{ $json.statusCode ?? 0 }}", "type": "number" },
            { "id": "e3", "name": "ok", "value": "={{ ($json.statusCode ?? 0) >= 200 && ($json.statusCode ?? 0) < 400 }}", "type": "boolean" },
            { "id": "e4", "name": "error", "value": "={{ $json.error?.message ?? ($json.statusCode >= 400 ? 'HTTP ' + $json.statusCode : '') }}", "type": "string" },
            { "id": "e5", "name": "checked_at", "value": "={{ $now.toISO() }}", "type": "string" }
          ]
        },
        "options": {}
      },
      "id": "m12-evaluate",
      "name": "Evaluate",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [880, 100]
    },
    {
      "parameters": { "aggregate": "aggregateAllItemData", "destinationFieldName": "checks", "options": {} },
      "id": "m12-agg",
      "name": "All Checks",
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [1100, 100]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            { "id": "s1", "name": "all_ok", "value": "={{ $json.checks.every(c => c.ok) }}", "type": "boolean" },
            { "id": "s2", "name": "down", "value": "={{ $json.checks.filter(c => !c.ok).map(c => c.url) }}", "type": "array" },
            { "id": "s3", "name": "checks", "value": "={{ $json.checks }}", "type": "array" },
            { "id": "s4", "name": "alert", "value": "={{ '🔴 DOWN: ' + $json.checks.filter(c => !c.ok).map(c => c.url + ' (' + (c.error || 'status ' + c.status) + ')').join(', ') }}", "type": "string" }
          ]
        },
        "options": {}
      },
      "id": "m12-summary",
      "name": "Summary",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [1320, 100]
    },
    {
      "parameters": {
        "conditions": {
          "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 },
          "conditions": [
            {
              "id": "a1",
              "leftValue": "={{ $('Health API').isExecuted }}",
              "rightValue": "",
              "operator": { "type": "boolean", "operation": "true", "singleValue": true }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "m12-api",
      "name": "Called via API?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [1540, 100]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { all_ok: $json.all_ok, down: $json.down, checks: $json.checks } }}",
        "options": { "responseCode": "={{ $json.all_ok ? 200 : 503 }}" }
      },
      "id": "m12-respond",
      "name": "Respond",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [1760, 0]
    },
    {
      "parameters": {
        "conditions": {
          "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 },
          "conditions": [
            {
              "id": "d1",
              "leftValue": "={{ $json.all_ok }}",
              "rightValue": "",
              "operator": { "type": "boolean", "operation": "false", "singleValue": true }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "m12-down",
      "name": "Anything Down?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [1760, 200]
    },
    {
      "parameters": {},
      "id": "m12-alert",
      "name": "Send Alert (Exercise 1)",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [1980, 120]
    }
  ],
  "connections": {
    "Every 5 Minutes": { "main": [[{ "node": "Sites to Check (edit me)", "type": "main", "index": 0 }]] },
    "Health API": { "main": [[{ "node": "Sites to Check (edit me)", "type": "main", "index": 0 }]] },
    "Sites to Check (edit me)": { "main": [[{ "node": "One Item per Site", "type": "main", "index": 0 }]] },
    "One Item per Site": { "main": [[{ "node": "Check Site", "type": "main", "index": 0 }]] },
    "Check Site": { "main": [[{ "node": "Evaluate", "type": "main", "index": 0 }]] },
    "Evaluate": { "main": [[{ "node": "All Checks", "type": "main", "index": 0 }]] },
    "All Checks": { "main": [[{ "node": "Summary", "type": "main", "index": 0 }]] },
    "Summary": { "main": [[{ "node": "Called via API?", "type": "main", "index": 0 }]] },
    "Called via API?": {
      "main": [
        [{ "node": "Respond", "type": "main", "index": 0 }],
        [{ "node": "Anything Down?", "type": "main", "index": 0 }]
      ]
    },
    "Anything Down?": { "main": [[{ "node": "Send Alert (Exercise 1)", "type": "main", "index": 0 }]] }
  },
  "pinData": {},
  "settings": { "executionOrder": "v1" }
}