DEV Community

Alex Kane
Alex Kane

Posted on

n8n for CleanTech & EnergyTech SaaS: 5 Automations That Scale Energy Ops and Keep Your Platform Compliant (Free Workflow JSON)

If you're building CleanTech or EnergyTech SaaS — energy management platforms, DERMS, carbon accounting tools, smart grid analytics, solar/wind SCADA, or demand response software — you already know the compliance stack is brutal.

NERC CIP. FERC Order 2222. EPA 40 CFR Part 75. The SEC's new climate disclosure rule. California SB 253. GDPR for smart meter data. ISO 50001.

Every one of those regulations has a reporting window, an audit trail requirement, or a data retention rule that Zapier's 30-day task log quietly violates.

Here are five n8n workflows — with full import-ready JSON — that handle the operational and compliance work your team is probably still doing manually.


Workflow 1: New Energy Customer Onboarding Drip

The problem: Utility-scale clients and commercial aggregators have completely different compliance profiles. NERC CIP applies only to Bulk Electric System assets. FERC Order 2222 only to registered DER aggregators. EPA Part 75 only to large power plants with CEMS. Treating them identically = missed onboarding steps and compliance gaps.

This workflow fires when a new client row is added to Google Sheets. It classifies by tier (UTILITY_ENTERPRISE → COMMERCIAL_INDUSTRIAL → SMB_PROSUMER → RESIDENTIAL_AGGREGATOR), sets compliance flags (NERC_CIP_APPLICABLE, FERC_ORDER_2222_REGISTERED, EPA_PART75_REPORTER, SEC_CLIMATE_DISCLOSURE_APPLICABLE, EU_EED_APPLICABLE, GDPR_SMART_METER_DATA), logs to Postgres for SOC 2 CC7.1 vendor management, and sends tiered welcome emails with their specific regulatory profile at Day 0, 3, and 7.

{
  "name": "CleanTech Client Onboarding Drip",
  "nodes": [
    {
      "id": "1",
      "name": "Watch New Energy Clients",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "typeVersion": 4,
      "position": [
        240,
        300
      ],
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "YOUR_SHEET_ID",
          "mode": "list"
        },
        "sheetName": {
          "__rl": true,
          "value": "clients",
          "mode": "list"
        },
        "event": "rowAdded"
      }
    },
    {
      "id": "2",
      "name": "Classify Client Tier",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        300
      ],
      "parameters": {
        "jsCode": "\nconst r = $input.first().json;\nconst arr = Number(r.arr_usd || 0);\nconst gwh = Number(r.portfolio_gwh || 0);\n\nlet tier = 'RESIDENTIAL_AGGREGATOR';\nif (arr >= 1000000 || gwh >= 10000) tier = 'UTILITY_ENTERPRISE';\nelse if (arr >= 100000 || gwh >= 1000) tier = 'COMMERCIAL_INDUSTRIAL';\nelse if (arr >= 10000 || gwh >= 100) tier = 'SMB_PROSUMER';\n\nconst flags = {\n  NERC_CIP_APPLICABLE: r.is_bulk_electric_system === 'yes',\n  FERC_ORDER_2222_REGISTERED: r.ferc_registration === 'yes',\n  EPA_PART75_REPORTER: r.epa_part75 === 'yes',\n  SEC_CLIMATE_DISCLOSURE_APPLICABLE: r.sec_filer === 'yes',\n  ISO_50001_CERTIFIED: r.iso_50001 === 'yes',\n  EU_EED_APPLICABLE: r.eu_operations === 'yes',\n  GDPR_SMART_METER_DATA: r.eu_smart_meters === 'yes',\n  CCPA_APPLICABLE: r.california_customers === 'yes'\n};\n\nreturn [{ json: { ...r, tier, ...flags, classified_at: new Date().toISOString() } }];\n"
      }
    },
    {
      "id": "3",
      "name": "Log to Postgres",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        680,
        300
      ],
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO energy_clients (id, company, tier, arr_usd, portfolio_gwh, flags, created_at) VALUES ('{{ $json.client_id }}', '{{ $json.company }}', '{{ $json.tier }}', {{ $json.arr_usd }}, {{ $json.portfolio_gwh }}, '{{ JSON.stringify($json) }}'::jsonb, NOW()) ON CONFLICT (id) DO UPDATE SET tier = EXCLUDED.tier, flags = EXCLUDED.flags, updated_at = NOW()"
      }
    },
    {
      "id": "4",
      "name": "Send Day 0 Welcome",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        900,
        300
      ],
      "parameters": {
        "operation": "send",
        "toList": "={{ $json.email }}",
        "subject": "Welcome to [YourPlatform] \u2014 your clean energy stack is ready",
        "message": "Hi {{ $json.name }},\n\nYour account is live. Here's what's configured:\n- Energy asset monitoring: active\n- Compliance deadline tracker: loaded with your regulatory profile ({{ $json.tier }})\n- Reporting pipeline: connected\n\nYour CSM {{ $json.csm_name }} will reach out within 24 hours.\n\nBest,\nThe [YourPlatform] Team"
      }
    },
    {
      "id": "5",
      "name": "Alert CSM in Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        900,
        460
      ],
      "parameters": {
        "operation": "post",
        "channel": "#cs-energy-onboarding",
        "text": "New {{ $json.tier }} client: *{{ $json.company }}* ({{ $json.portfolio_gwh }} GWh portfolio, ${{ $json.arr_usd }} ARR)\nFlags: NERC_CIP={{ $json.NERC_CIP_APPLICABLE }}, FERC={{ $json.FERC_ORDER_2222_REGISTERED }}, EPA_75={{ $json.EPA_PART75_REPORTER }}, SEC_CLIMATE={{ $json.SEC_CLIMATE_DISCLOSURE_APPLICABLE }}\nAssigned CSM: {{ $json.csm_name }}"
      }
    },
    {
      "id": "6",
      "name": "Wait 3 Days",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [
        1120,
        300
      ],
      "parameters": {
        "amount": 3,
        "unit": "days"
      }
    },
    {
      "id": "7",
      "name": "Send Day 3 Check-In",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1340,
        300
      ],
      "parameters": {
        "operation": "send",
        "toList": "={{ $json.email }}",
        "subject": "Day 3 \u2014 your energy assets are being monitored",
        "message": "Hi {{ $json.name }},\n\nQuick check-in: your platform is monitoring {{ $json.asset_count || 'your' }} energy assets.\n\nUpcoming compliance deadlines loaded for your profile:\n{{ $json.NERC_CIP_APPLICABLE ? '- NERC CIP-003-8 audit schedule\\n' : '' }}{{ $json.EPA_PART75_REPORTER ? '- EPA 40 CFR Part 75 quarterly report\\n' : '' }}{{ $json.SEC_CLIMATE_DISCLOSURE_APPLICABLE ? '- SEC 17 CFR \u00a7229.1500 climate disclosure (10-K)\\n' : '' }}\nNeed help? Reply here or ping your CSM.\n\nBest,\nThe [YourPlatform] Team"
      }
    },
    {
      "id": "8",
      "name": "Wait 4 More Days",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [
        1560,
        300
      ],
      "parameters": {
        "amount": 4,
        "unit": "days"
      }
    },
    {
      "id": "9",
      "name": "Send Day 7 Health Review",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1780,
        300
      ],
      "parameters": {
        "operation": "send",
        "toList": "={{ $json.email }}",
        "subject": "Week 1 complete \u2014 your energy platform health report",
        "message": "Hi {{ $json.name }},\n\nYour first week summary:\n- Assets monitored: {{ $json.asset_count || '\u2014' }}\n- API uptime: {{ $json.uptime_pct || '99.9' }}%\n- Compliance deadlines tracked: {{ $json.deadline_count || '\u2014' }}\n\nSchedule a 30-min review with your CSM: [CALENDAR LINK]\n\nBest,\nThe [YourPlatform] Team"
      }
    }
  ],
  "connections": {
    "Watch New Energy Clients": {
      "main": [
        [
          {
            "node": "Classify Client Tier",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Client Tier": {
      "main": [
        [
          {
            "node": "Log to Postgres",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Postgres": {
      "main": [
        [
          {
            "node": "Send Day 0 Welcome",
            "type": "main",
            "index": 0
          },
          {
            "node": "Alert CSM in Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Day 0 Welcome": {
      "main": [
        [
          {
            "node": "Wait 3 Days",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 3 Days": {
      "main": [
        [
          {
            "node": "Send Day 3 Check-In",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Day 3 Check-In": {
      "main": [
        [
          {
            "node": "Wait 4 More Days",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 4 More Days": {
      "main": [
        [
          {
            "node": "Send Day 7 Health Review",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 2: Energy Asset & SCADA/DERMS API Health Monitor

The problem: Your SCADA system, DERMS data feeds, and settlement APIs are operational infrastructure — but also compliance infrastructure. A 15-minute data gap in your CEMS feed is an EPA Part 75 audit flag. A FERC Order 2222 settlement data failure is a direct compliance exposure. A NERC CIP cybersecurity event has a 1-hour reporting window to E-ISAC.

This workflow runs every 3 minutes, polls all registered energy endpoints from Postgres, and evaluates three failure modes: DOWN (critical — NERC CIP-008 incident clock starts), STALE_DATA >15 min (EPA Part 75 data gap), and DEGRADED >5s (ISO 50001 real-time monitoring SLA). Deduplicates alerts to 30-minute windows per endpoint. Logs all incidents to Postgres for audit trail.

{
  "name": "Energy Asset & SCADA/DERMS Health Monitor",
  "nodes": [
    {
      "id": "1",
      "name": "Every 3 Minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        240,
        300
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 3
            }
          ]
        }
      }
    },
    {
      "id": "2",
      "name": "Load Energy Endpoints",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        460,
        300
      ],
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT id, name, url, type, sla_ms, regulation FROM energy_endpoints WHERE active = true"
      }
    },
    {
      "id": "3",
      "name": "Batch Endpoints",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        680,
        300
      ],
      "parameters": {
        "batchSize": 5
      }
    },
    {
      "id": "4",
      "name": "Ping Endpoint",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        900,
        300
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "method": "GET",
        "timeout": 8000,
        "response": {
          "response": {
            "responseFormat": "text"
          }
        }
      }
    },
    {
      "id": "5",
      "name": "Evaluate Status",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1120,
        300
      ],
      "parameters": {
        "jsCode": "\nconst ep = $('Load Energy Endpoints').first().json;\nconst resp = $input.first();\nconst latencyMs = resp.json.$responseMetadata?.latencyMs || 0;\nconst body = resp.json.data || '';\nconst cacheAge = parseInt(resp.json.$responseHeaders?.['x-cache-age'] || '0');\n\nlet status = 'OK', severity = null, regulation = null;\n\nif (!resp.json.$responseMetadata?.statusCode || resp.json.$responseMetadata.statusCode >= 500) {\n  status = 'DOWN';\n  severity = 'CRITICAL';\n  regulation = ep.type === 'NERC_CIP' ? 'NERC CIP-008-6 \u00a7R1 incident reporting within 1 hour' : ep.type === 'FERC_SETTLEMENT' ? 'FERC settlement data integrity \u2014 Order 2222 compliance gap' : 'API outage';\n} else if (cacheAge > 900) {\n  status = 'STALE_DATA';\n  severity = 'HIGH';\n  regulation = 'Stale energy data (>15 min) \u2014 NERC CIP-007-6 patch management data feed SLA; EPA Part 75 CEMS data gap risk';\n} else if (latencyMs > 5000) {\n  status = 'DEGRADED';\n  severity = 'MEDIUM';\n  regulation = 'Latency >5s \u2014 ISO 50001 real-time energy performance monitoring SLA; FERC real-time pricing data latency';\n}\n\nconst dedup = $getWorkflowStaticData('global');\nif (status !== 'OK') {\n  const key = ep.id + '_' + status;\n  const last = dedup[key] || 0;\n  if (Date.now() - last < 30 * 60 * 1000) return [];\n  dedup[key] = Date.now();\n}\n\nreturn [{ json: { ...ep, status, severity, regulation, latencyMs, cacheAge, checked_at: new Date().toISOString() } }];\n"
      }
    },
    {
      "id": "6",
      "name": "Alert Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        1340,
        300
      ],
      "parameters": {
        "operation": "post",
        "channel": "#energytech-ops",
        "text": "*{{ $json.severity }}* \u2014 {{ $json.name }} is {{ $json.status }}\nURL: {{ $json.url }}\nLatency: {{ $json.latencyMs }}ms | Cache age: {{ $json.cacheAge }}s\nRegulation note: {{ $json.regulation }}\nChecked: {{ $json.checked_at }}"
      }
    },
    {
      "id": "7",
      "name": "Log Incident",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        1340,
        460
      ],
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO energy_incidents (endpoint_id, endpoint_name, status, severity, regulation, latency_ms, cache_age_s, created_at) VALUES ('{{ $json.id }}', '{{ $json.name }}', '{{ $json.status }}', '{{ $json.severity }}', '{{ $json.regulation }}', {{ $json.latencyMs }}, {{ $json.cacheAge }}, NOW()) ON CONFLICT DO NOTHING"
      }
    }
  ],
  "connections": {
    "Every 3 Minutes": {
      "main": [
        [
          {
            "node": "Load Energy Endpoints",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Energy Endpoints": {
      "main": [
        [
          {
            "node": "Batch Endpoints",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Batch Endpoints": {
      "main": [
        [
          {
            "node": "Ping Endpoint",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ping Endpoint": {
      "main": [
        [
          {
            "node": "Evaluate Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Evaluate Status": {
      "main": [
        [
          {
            "node": "Alert Slack",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log Incident",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 3: FERC/NERC CIP/EPA/SEC Climate Compliance Deadline Tracker

The problem: Energy compliance calendars are a mess of overlapping deadlines across five regulatory bodies. NERC CIP-007-6 patches are due 35 days after identification. EPA Part 75 quarterly reports are due 30 days after quarter end. SEC climate disclosures go in the 10-K. California SB 253 Scope 1/2/3 reports are due in 2026. And GDPR smart meter consent audits run annually.

The Zapier problem: Running this tracker on Zapier means Zapier is in your NERC CIP compliance workflow. NERC defines any software with access to your Electronic Security Perimeter (ESP) as potentially in-scope for CIP-003-8. Your NERC CIP auditor will ask about Zapier's access controls.

This workflow checks a Google Sheet of deadlines every weekday at 8 AM, prioritizes by urgency (OVERDUE → CRITICAL → URGENT → WARNING → NOTICE), deduplicates to 4-hour windows, and routes alerts to Slack (urgent) or email (notice-level). Covers 15 deadline types across FERC, NERC CIP, EPA, SEC, California SB 253/261, ISO 50001, EU EED, GDPR, and SOC 2.

{
  "name": "FERC/NERC CIP/EPA/SEC Climate Compliance Tracker",
  "nodes": [
    {
      "id": "1",
      "name": "Weekdays at 8 AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        240,
        300
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1-5"
            }
          ]
        }
      }
    },
    {
      "id": "2",
      "name": "Load Compliance Deadlines",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        460,
        300
      ],
      "parameters": {
        "operation": "readRows",
        "documentId": {
          "__rl": true,
          "value": "YOUR_COMPLIANCE_SHEET_ID",
          "mode": "list"
        },
        "sheetName": {
          "__rl": true,
          "value": "energy_deadlines",
          "mode": "list"
        }
      }
    },
    {
      "id": "3",
      "name": "Prioritize Deadlines",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        680,
        300
      ],
      "parameters": {
        "jsCode": "\nconst actionMap = {\n  FERC_ORDER_2222_QUARTERLY: { action: 'Submit FERC Order 2222 distributed energy resource quarterly data filing', reg: 'FERC Order 2222 (86 FR 55116) \u00a735.28(g)', horizon_days: 14 },\n  FERC_FORM_1_ANNUAL: { action: 'Submit FERC Form 1 annual electric utility report', reg: 'FERC 18 CFR Part 141', horizon_days: 30 },\n  NERC_CIP_003_8_AUDIT: { action: 'Complete NERC CIP-003-8 cybersecurity management controls audit', reg: 'NERC CIP-003-8 R1-R6 (critical cyber asset management)', horizon_days: 60 },\n  NERC_CIP_007_6_PATCH: { action: 'Apply NERC CIP-007-6 system security management patches', reg: 'NERC CIP-007-6 R2 (35-day remediation for high/medium BES Cyber Systems)', horizon_days: 21 },\n  NERC_CIP_011_2_INFO_PROTECTION: { action: 'Annual NERC CIP-011-2 BES Cyber System Information protection review', reg: 'NERC CIP-011-2 R1 (information protection program)', horizon_days: 45 },\n  EPA_PART75_QUARTERLY: { action: 'Submit EPA 40 CFR Part 75 CEMS quarterly emissions report to EPA ECMPS', reg: 'EPA 40 CFR Part 75 \u00a775.64 (30 days after quarter end)', horizon_days: 21 },\n  EPA_TITLE_V_ANNUAL: { action: 'Submit EPA Title V permit annual compliance certification', reg: 'EPA 40 CFR Part 70 \u00a770.6(c)(1) (60 days after year end)', horizon_days: 30 },\n  SEC_CLIMATE_10K: { action: 'Prepare SEC 17 CFR \u00a7229.1500 climate-related risk disclosures for 10-K', reg: 'SEC Climate Disclosure Rule 17 CFR \u00a7229.1500 (effective FY2025 for large accelerated filers)', horizon_days: 45 },\n  CA_SB253_SCOPE_REPORT: { action: 'Prepare California SB 253 Scope 1/2/3 GHG emissions report (CARB)', reg: 'California SB 253 (Health & Safety Code \u00a738532) \u2014 $1B+ revenue companies, first report due 2026', horizon_days: 60 },\n  CA_SB261_RISK_REPORT: { action: 'Prepare California SB 261 climate-related financial risk report', reg: 'California SB 261 (Insurance Code \u00a712968.6) \u2014 $500M+ revenue, biennial report', horizon_days: 60 },\n  ISO_50001_MANAGEMENT_REVIEW: { action: 'Conduct ISO 50001:2018 management review of energy performance', reg: 'ISO 50001:2018 \u00a79.3 management review (annual)', horizon_days: 30 },\n  ISO_50001_INTERNAL_AUDIT: { action: 'Complete ISO 50001:2018 internal energy audit', reg: 'ISO 50001:2018 \u00a79.2 internal audit (annual)', horizon_days: 45 },\n  EU_EED_ENERGY_AUDIT: { action: 'Complete EU Energy Efficiency Directive Article 8 energy audit', reg: 'EU EED 2023/1791 Art.8 (every 4 years for non-SMEs in EU)', horizon_days: 90 },\n  GDPR_SMART_METER_AUDIT: { action: 'Conduct GDPR privacy review for smart meter / IoT data processing', reg: 'GDPR Art.5(1)(e) storage limitation + Art.13 transparency \u2014 EU smart meter data = personal data (CJEU C-683/21)', horizon_days: 30 },\n  SOC2_TYPE2_RENEWAL: { action: 'Schedule SOC 2 Type II audit with certified auditor', reg: 'SOC 2 CC9.2 vendor risk \u2014 enterprise customers require annual Type II', horizon_days: 60 }\n};\n\nconst today = new Date();\nconst results = [];\n\nfor (const row of $input.all()) {\n  const d = row.json;\n  const due = new Date(d.due_date);\n  const daysLeft = Math.round((due - today) / 86400000);\n  const cfg = actionMap[d.deadline_type] || {};\n  const horizon = cfg.horizon_days || 30;\n\n  let priority = null;\n  if (daysLeft < 0) priority = 'OVERDUE';\n  else if (daysLeft <= 3) priority = 'CRITICAL';\n  else if (daysLeft <= 7) priority = 'URGENT';\n  else if (daysLeft <= horizon * 0.5) priority = 'WARNING';\n  else if (daysLeft <= horizon) priority = 'NOTICE';\n\n  if (!priority) continue;\n\n  const dedup = $getWorkflowStaticData('global');\n  const key = d.id + '_' + priority;\n  const last = dedup[key] || 0;\n  if (Date.now() - last < 4 * 3600 * 1000) continue;\n  dedup[key] = Date.now();\n\n  results.push({ json: { ...d, daysLeft, priority, action: cfg.action, regulation: cfg.reg } });\n}\n\nreturn results.length ? results : [{ json: { skip: true } }];\n"
      }
    },
    {
      "id": "4",
      "name": "Skip if None",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        900,
        300
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.skip }}",
              "value2": true
            }
          ]
        }
      }
    },
    {
      "id": "5",
      "name": "Route by Priority",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        1120,
        300
      ],
      "parameters": {
        "dataPropertyName": "priority",
        "rules": {
          "rules": [
            {
              "value": "OVERDUE"
            },
            {
              "value": "CRITICAL"
            },
            {
              "value": "URGENT"
            },
            {
              "value": "WARNING"
            },
            {
              "value": "NOTICE"
            }
          ]
        }
      }
    },
    {
      "id": "6",
      "name": "Alert Slack Urgent",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        1340,
        180
      ],
      "parameters": {
        "operation": "post",
        "channel": "#energytech-compliance",
        "text": "*{{ $json.priority }}* \u2014 {{ $json.deadline_type }} due in {{ $json.daysLeft }} days\n*Action:* {{ $json.action }}\n*Regulation:* {{ $json.regulation }}\n*Owner:* {{ $json.owner }}"
      }
    },
    {
      "id": "7",
      "name": "Send Email Notice",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1340,
        360
      ],
      "parameters": {
        "operation": "send",
        "toList": "={{ $json.owner_email }}",
        "subject": "{{ $json.priority }}: {{ $json.deadline_type }} due in {{ $json.daysLeft }} days",
        "message": "Action required: {{ $json.action }}\n\nRegulation: {{ $json.regulation }}\nDue: {{ $json.due_date }} ({{ $json.daysLeft }} days)\nOwner: {{ $json.owner }}\n\nPlease confirm completion in the compliance tracker."
      }
    }
  ],
  "connections": {
    "Weekdays at 8 AM": {
      "main": [
        [
          {
            "node": "Load Compliance Deadlines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Compliance Deadlines": {
      "main": [
        [
          {
            "node": "Prioritize Deadlines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prioritize Deadlines": {
      "main": [
        [
          {
            "node": "Skip if None",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip if None": {
      "main": [
        [],
        [
          {
            "node": "Route by Priority",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Priority": {
      "main": [
        [
          {
            "node": "Alert Slack Urgent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Alert Slack Urgent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Alert Slack Urgent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Email Notice",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Email Notice",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 4: Carbon Emission Anomaly & EPA Reporting Alert Pipeline

The problem: Emission spikes, NERC CIP cybersecurity incidents, FERC settlement data failures, and GDPR smart meter data requests all have hard response windows. Missing the 1-hour NERC CIP-008 reporting window to E-ISAC is a NERC violation. A FERC settlement gap triggers audit exposure. An SEC-reportable climate event needs legal review before it hits the 10-K.

The Zapier irony: If you're building a carbon accounting SaaS platform and running your emission anomaly alerts through Zapier, you've added Zapier to your SEC climate disclosure audit chain. Any Zapier SLA failure that delays a material climate event notification could create disclosure timing questions under 17 CFR §229.1500.

This webhook-based pipeline classifies seven event types with per-asset 30-minute dedup, routes to Slack with regulation context, logs to Postgres for audit trail, and ACKs the webhook with the regulation note so the calling system knows what window applies.

{
  "name": "Carbon Emission Anomaly & EPA Reporting Alert Pipeline",
  "nodes": [
    {
      "id": "1",
      "name": "Emission Event Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        240,
        300
      ],
      "parameters": {
        "path": "energy-emission-alert",
        "responseMode": "onReceived",
        "responseData": "allEntries"
      }
    },
    {
      "id": "2",
      "name": "Classify Emission Event",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        300
      ],
      "parameters": {
        "jsCode": "\nconst ev = $input.first().json.body || $input.first().json;\nconst type = ev.event_type;\nconst delta_pct = Number(ev.delta_pct || 0);\nconst source = ev.source_type || '';\n\nconst priorities = {\n  CARBON_EMISSION_SPIKE: { sev: 'CRITICAL', window: '2h', reg: 'EPA 40 CFR Part 75 \u00a775.64 CEMS data integrity; SEC 17 CFR \u00a7229.1500 material climate event disclosure; CA SB 253 Scope 1 reporting anomaly', note: delta_pct > 20 ? `${delta_pct}% above baseline \u2014 potential EPA audit trigger` : 'Emission spike detected' },\n  NERC_CIP_CYBERSECURITY_INCIDENT: { sev: 'CRITICAL', window: '1h', reg: 'NERC CIP-008-6 R1 \u2014 reportable Cyber Security Incident to E-ISAC and applicable Reliability Coordinator within 1 hour of identification', note: 'Mandatory NERC CIP incident report required' },\n  FERC_SETTLEMENT_DATA_FAILURE: { sev: 'CRITICAL', window: '30min', reg: 'FERC Order 2222 settlement data integrity \u2014 DER aggregator settlement gap triggers FERC compliance exposure', note: 'Real-time settlement data missing \u2014 FERC audit risk' },\n  SEC_CLIMATE_MATERIAL_EVENT: { sev: 'HIGH', window: '4h', reg: 'SEC 17 CFR \u00a7229.1500 \u2014 material climate-related risk event may require 10-K/8-K disclosure; consult legal', note: 'SEC climate disclosure review needed' },\n  ISO_50001_PERFORMANCE_DEVIATION: { sev: 'HIGH', window: '4h', reg: 'ISO 50001:2018 \u00a76.3 energy performance indicator deviation \u2014 corrective action plan required for >10% variance from EnPI baseline', note: `${delta_pct}% deviation from EnPI baseline` },\n  EPA_PERMIT_EXCEEDANCE_RISK: { sev: 'HIGH', window: '8h', reg: 'EPA Title V permit \u00a770.6(a)(3)(i) \u2014 approaching permit limit; excess emissions report may be required', note: 'Title V permit limit approach \u2014 monitor hourly' },\n  GDPR_SMART_METER_DSR: { sev: 'MEDIUM', window: '30d', reg: 'GDPR Art.17 erasure + Art.20 portability \u2014 EU smart meter data subject request; 30-day response window (CJEU C-683/21 smart meters = personal data)', note: 'EU smart meter data subject request received' }\n};\n\nconst cfg = priorities[type] || { sev: 'LOW', window: '24h', reg: 'Internal review', note: 'Unknown event type' };\n\nconst dedup = $getWorkflowStaticData('global');\nconst key = (ev.asset_id || type) + '_' + type;\nconst last = dedup[key] || 0;\nif (Date.now() - last < 30 * 60 * 1000) return [];\ndedup[key] = Date.now();\n\nreturn [{ json: { ...ev, severity: cfg.sev, response_window: cfg.window, regulation: cfg.reg, note: cfg.note, received_at: new Date().toISOString() } }];\n"
      }
    },
    {
      "id": "3",
      "name": "Alert Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        680,
        300
      ],
      "parameters": {
        "operation": "post",
        "channel": "#energytech-compliance",
        "text": "*{{ $json.severity }} \u2014 {{ $json.event_type }}*\nAsset: {{ $json.asset_id || 'N/A' }} | {{ $json.note }}\nResponse window: {{ $json.response_window }}\n*Regulation:* {{ $json.regulation }}\nReceived: {{ $json.received_at }}"
      }
    },
    {
      "id": "4",
      "name": "Log to Postgres",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        680,
        460
      ],
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO emission_incidents (asset_id, event_type, severity, response_window, regulation, note, payload, created_at) VALUES ('{{ $json.asset_id }}', '{{ $json.event_type }}', '{{ $json.severity }}', '{{ $json.response_window }}', '{{ $json.regulation }}', '{{ $json.note }}', '{{ JSON.stringify($json) }}'::jsonb, NOW()) ON CONFLICT DO NOTHING"
      }
    },
    {
      "id": "5",
      "name": "ACK 200",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        680,
        600
      ],
      "parameters": {
        "responseCode": 200,
        "responseBody": "{ \"status\": \"received\", \"event_type\": \"{{ $json.event_type }}\", \"severity\": \"{{ $json.severity }}\", \"response_window\": \"{{ $json.response_window }}\", \"regulation\": \"{{ $json.regulation }}\" }"
      }
    }
  ],
  "connections": {
    "Emission Event Webhook": {
      "main": [
        [
          {
            "node": "Classify Emission Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Emission Event": {
      "main": [
        [
          {
            "node": "Alert Slack",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log to Postgres",
            "type": "main",
            "index": 0
          },
          {
            "node": "ACK 200",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 5: Weekly CleanTech Platform KPI Dashboard

The problem: CleanTech SaaS CEOs need to track ARR, GWh under management, CO2e tracked, and API uptime in one view — but the CSO also needs the uptime number (ISO 50001 performance indicator) and the CFO needs the ARR trend for the SEC climate risk materiality assessment.

Every Monday at 8 AM, this workflow queries two Postgres tables in parallel (platform metrics + account health), merges them, computes week-over-week ARR delta using $getWorkflowStaticData (no external state store), renders a color-coded HTML dashboard (green ≥99.9% uptime / orange ≥99% / red below), emails CEO BCC'd to CTO and CSO, and posts a summary to Slack.

{
  "name": "Weekly CleanTech Platform KPI Dashboard",
  "nodes": [
    {
      "id": "1",
      "name": "Every Monday 8 AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        240,
        300
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1"
            }
          ]
        }
      }
    },
    {
      "id": "2",
      "name": "Query Platform Metrics",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        460,
        260
      ],
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT COUNT(*) as active_clients, SUM(portfolio_gwh) as total_gwh_monitored, SUM(arr_usd) as total_arr, AVG(api_uptime_pct) as avg_uptime, COUNT(CASE WHEN tier='UTILITY_ENTERPRISE' THEN 1 END) as enterprise_count, SUM(co2e_tons_tracked) as co2e_tracked FROM energy_clients WHERE active = true"
      }
    },
    {
      "id": "3",
      "name": "Query Account Health",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        460,
        420
      ],
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT COUNT(CASE WHEN health_score >= 80 THEN 1 END) as healthy, COUNT(CASE WHEN health_score >= 60 AND health_score < 80 THEN 1 END) as at_risk, COUNT(CASE WHEN health_score < 60 THEN 1 END) as critical, COUNT(*) as total FROM energy_clients WHERE active = true"
      }
    },
    {
      "id": "4",
      "name": "Merge Metrics",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        700,
        340
      ],
      "parameters": {
        "mode": "combine",
        "combinationMode": "mergeByPosition"
      }
    },
    {
      "id": "5",
      "name": "Build Dashboard",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        920,
        340
      ],
      "parameters": {
        "jsCode": "\nconst m = $input.first().json;\nconst prev = $getWorkflowStaticData('global');\nconst prevArr = prev.last_arr || m.total_arr;\nconst arrWoW = prevArr ? (((m.total_arr - prevArr) / prevArr) * 100).toFixed(1) : '\u2014';\nprev.last_arr = m.total_arr;\n\nconst uptimeColor = m.avg_uptime >= 99.9 ? '#36a64f' : m.avg_uptime >= 99.0 ? '#ffaa00' : '#ff0000';\nconst arrColor = parseFloat(arrWoW) > 0 ? '#36a64f' : parseFloat(arrWoW) < 0 ? '#ff0000' : '#888888';\n\nconst html = `\n<h2>CleanTech Platform \u2014 Weekly KPI Dashboard</h2>\n<table border=\"1\" cellpadding=\"6\" style=\"border-collapse:collapse\">\n<tr><th>Metric</th><th>Value</th><th>WoW</th></tr>\n<tr><td>Active Clients</td><td>${m.active_clients}</td><td>\u2014</td></tr>\n<tr><td>Total ARR</td><td>$${Number(m.total_arr).toLocaleString()}</td><td style=\"color:${arrColor}\">${arrWoW}%</td></tr>\n<tr><td>GWh Monitored</td><td>${Number(m.total_gwh_monitored).toLocaleString()} GWh</td><td>\u2014</td></tr>\n<tr><td>CO2e Tracked</td><td>${Number(m.co2e_tracked).toLocaleString()} tons</td><td>\u2014</td></tr>\n<tr><td>Avg API Uptime</td><td style=\"color:${uptimeColor}\">${m.avg_uptime}%</td><td>\u2014</td></tr>\n<tr><td>Enterprise Clients</td><td>${m.enterprise_count}</td><td>\u2014</td></tr>\n</table>\n<h3>Account Health</h3>\n<table border=\"1\" cellpadding=\"6\" style=\"border-collapse:collapse\">\n<tr><th>Status</th><th>Count</th><th>%</th></tr>\n<tr><td style=\"color:#36a64f\">Healthy (\u226580)</td><td>${m.healthy}</td><td>${((m.healthy/m.total)*100).toFixed(0)}%</td></tr>\n<tr><td style=\"color:#ffaa00\">At Risk (60\u201379)</td><td>${m.at_risk}</td><td>${((m.at_risk/m.total)*100).toFixed(0)}%</td></tr>\n<tr><td style=\"color:#ff0000\">Critical (<60)</td><td>${m.critical}</td><td>${((m.critical/m.total)*100).toFixed(0)}%</td></tr>\n</table>`;\n\nreturn [{ json: { ...m, html, arrWoW } }];\n"
      }
    },
    {
      "id": "6",
      "name": "Email Dashboard",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1140,
        340
      ],
      "parameters": {
        "operation": "send",
        "toList": "ceo@yourenergy.com",
        "ccList": "cto@yourenergy.com",
        "bccList": "cso@yourenergy.com",
        "subject": "Weekly CleanTech KPI \u2014 {{ new Date().toISOString().slice(0,10) }}",
        "message": "={{ $json.html }}",
        "messageType": "html"
      }
    },
    {
      "id": "7",
      "name": "Post to Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        1140,
        480
      ],
      "parameters": {
        "operation": "post",
        "channel": "#exec-kpis",
        "text": "Weekly CleanTech KPI | Clients: {{ $json.active_clients }} | ARR: ${{ $json.total_arr }} ({{ $json.arrWoW }}% WoW) | GWh: {{ $json.total_gwh_monitored }} | Uptime: {{ $json.avg_uptime }}%"
      }
    }
  ],
  "connections": {
    "Every Monday 8 AM": {
      "main": [
        [
          {
            "node": "Query Platform Metrics",
            "type": "main",
            "index": 0
          },
          {
            "node": "Query Account Health",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Query Platform Metrics": {
      "main": [
        [
          {
            "node": "Merge Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Query Account Health": {
      "main": [
        [
          {
            "node": "Merge Metrics",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Metrics": {
      "main": [
        [
          {
            "node": "Build Dashboard",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Dashboard": {
      "main": [
        [
          {
            "node": "Email Dashboard",
            "type": "main",
            "index": 0
          },
          {
            "node": "Post to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The compliance case for n8n over Zapier in energy tech

NERC CIP-003-8: Any software that touches your Electronic Security Perimeter (ESP) or Bulk Electric System Cyber Systems (BCS) is potentially in-scope for NERC CIP. Zapier's multi-tenant cloud cannot satisfy NERC CIP access control and audit trail requirements by design. Your NERC CIP auditor will ask for a vendor inventory — Zapier will appear on it.

EPA 40 CFR Part 75: CEMS quarterly reports go to EPA ECMPS. A Zapier task failure creating a 24-hour gap in your CEMS data pipeline is an EPA audit flag. n8n self-hosted = your infrastructure, your SLA, your audit trail.

SEC 17 CFR §229.1500 (climate disclosure, effective FY2025): Material climate-related risks must be disclosed in 10-K filings. If your CleanTech SaaS platform uses Zapier and Zapier's SLA failure delays a material climate event notification, you've created a disclosure timing question. n8n's workflow execution logs are yours — Zapier's are Zapier's.

California SB 253 (signed Oct 2023): Companies with $1B+ revenue must report Scope 1/2/3 GHG emissions annually starting 2026. CleanTech SaaS platforms enabling this reporting need their own infrastructure to be ISO 50001-trackable.

GDPR smart meter data: The EU Court of Justice ruled in C-683/21 that smart meter data is personal data. If your EnergyTech SaaS processes EU smart meter data through Zapier, you need a GDPR Art.28 Data Processing Agreement with Zapier covering that data flow — and Zapier's standard terms may not satisfy EU supervisory authority scrutiny for energy consumption patterns.

Cost at scale: Energy companies generate massive data volumes. 1 billion energy data points/month on Zapier = ~$1M+/year. Same workload on a $500/month VPS running n8n = 99.95% cost reduction. That's not a rounding error.


All five workflows are import-ready JSON. Copy the JSON, go to n8n → Workflows → Import from JSON, paste, and configure your credentials.

If you want pre-built, tested versions of these workflows: stripeai.gumroad.com — FlowKit has 14 production-ready n8n automation templates starting at $12.

Questions or want a sixth workflow for your specific energy compliance use case? Drop a comment.

Top comments (0)