{
  "name": "SSLChecker",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -544,
        -288
      ],
      "id": "6e4af331-20e5-4850-b7e9-1505b486ff50",
      "name": "Schedule Trigger"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        240,
        -448
      ],
      "id": "f96d61de-4ae3-4e1a-877f-398156901522",
      "name": "No Operation, do nothing"
    },
    {
      "parameters": {
        "content": "# Workflow Guide\n\nThis workflow runs daily, checks SSL certificate expiry date based on a list of websites, calculates the number of days left before expiry, sends a notification if the SSL is expiring in less than 10 days, and send an error message if fetching the certificate fails.\n\n- Use **Schedule Trigger** node to set when should the workflow run.\n\n- Use **Website List** node to include all the domains inside the list (urlList).\n\n- Use **Condition** node to set how many days before the expiry date should the notification be sent.\n\n- Modify **Alert** nodes if you want to customize the message sent or use a different platform like emails, telegram, etc..",
        "height": 400,
        "width": 624
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -576,
        -752
      ],
      "typeVersion": 1,
      "id": "544064e2-e27e-401f-98b0-4f0be60084e4",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "jsCode": "const urlList = ['opensourcehustle.com', 'nasa.gov', 'example.com'];\n\nreturn urlList.map(url => ({ json: { url } }));"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -320,
        -288
      ],
      "id": "b2169112-334e-4993-b47f-13bbff900d91",
      "name": "Website List"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        -64,
        -288
      ],
      "id": "2b783464-e975-4752-a94a-4d41d11a5722",
      "name": "Loop Over Websites"
    },
    {
      "parameters": {
        "command": "=echo | openssl s_client -servername {{ $json.url }} -connect {{ $json.url }}:443 2>/dev/null | openssl x509 -noout -enddate"
      },
      "type": "n8n-nodes-base.executeCommand",
      "typeVersion": 1,
      "position": [
        240,
        -272
      ],
      "id": "0a39fc99-f2e3-4438-933f-d0abc200ee65",
      "name": "OpenSSL Command",
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "jsCode": "// Get the first item’s stdout\nconst data = $input.first().json.stdout;\n\n// Extract the date string from stdout (e.g. \"notAfter=Jan  4 08:06:00 2026 GMT\")\nconst match = data.match(/notAfter=(.*)/);\n\nif (!match) {\n  throw new Error(\"Could not find expiry date in stdout\");\n}\n\nconst dateString = match[1].trim();\n\n// Parse the date string to a JS Date object\nconst expiryDate = new Date(dateString);\n\n// Get current date (UTC)\nconst now = new Date();\n\n// Calculate the difference in milliseconds\nconst diffMs = expiryDate - now;\n\n// Convert milliseconds to days\nconst daysLeft = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n\n// Return as new JSON output\nreturn [\n  {\n    json: {\n      expiryDate: expiryDate.toISOString(),\n      daysLeft,\n    },\n  },\n];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        544,
        -288
      ],
      "id": "5855beb2-16f8-4cbd-ade1-e3f16638601f",
      "name": "Calculate Days Left"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "d8ae49c8-6052-4960-ba27-45cd5f3b11b3",
              "leftValue": "={{ $json.daysLeft }}",
              "rightValue": 10,
              "operator": {
                "type": "number",
                "operation": "lt"
              }
            }
          ],
          "combinator": "or"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        784,
        -288
      ],
      "id": "e0b8db18-942f-4abc-8333-0cf48d0ff5b7",
      "name": "Condition"
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "C069C9YNFB8",
          "mode": "list",
          "cachedResultName": "random"
        },
        "text": "=SSL checkup for {{ $('Loop Over Websites').item.json.url }} failed due to:\n{{ $json.error }}",
        "otherOptions": {}
      },
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.3,
      "position": [
        656,
        -64
      ],
      "id": "906bc58b-303d-43e4-8780-57c041c8baff",
      "name": "Alert - Failed to Fetch SSL",
      "webhookId": "e4ec06cb-6515-4bbf-9b62-f2369b3acd69",
      "credentials": {
        "slackApi": {
          "id": "jwgOsgjjJ1uQb044",
          "name": "Slack account"
        }
      }
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "C069C9YNFB8",
          "mode": "list",
          "cachedResultName": "random"
        },
        "text": "={{ $('Loop Over Websites').item.json.url }} ssl certificate will expire in {{ $('Calculate Days Left').item.json.daysLeft }} days",
        "otherOptions": {}
      },
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.3,
      "position": [
        1264,
        48
      ],
      "id": "d0963493-d75e-41ff-a6e4-c6e5b54149a0",
      "name": "Alert - Domain + # of Days Left to Expire",
      "webhookId": "e4ec06cb-6515-4bbf-9b62-f2369b3acd69",
      "credentials": {
        "slackApi": {
          "id": "jwgOsgjjJ1uQb044",
          "name": "Slack account"
        }
      }
    }
  ],
  "pinData": {},
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Website List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Website List": {
      "main": [
        [
          {
            "node": "Loop Over Websites",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Websites": {
      "main": [
        [
          {
            "node": "No Operation, do nothing",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "OpenSSL Command",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenSSL Command": {
      "main": [
        [
          {
            "node": "Calculate Days Left",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Alert - Failed to Fetch SSL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Days Left": {
      "main": [
        [
          {
            "node": "Condition",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Condition": {
      "main": [
        [
          {
            "node": "Alert - Domain + # of Days Left to Expire",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Loop Over Websites",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Alert - Failed to Fetch SSL": {
      "main": [
        [
          {
            "node": "Loop Over Websites",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Alert - Domain + # of Days Left to Expire": {
      "main": [
        [
          {
            "node": "Loop Over Websites",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "c3a501ee-d968-45fd-864c-e3f435a65079",
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "694a3d5c27f03d8411639c24916819fed13a5eeff3a25f9815d468e6945cb0ff"
  },
  "id": "7jWhkfNO2S4ZkSis",
  "tags": []
}