DEV Community

Alex Kane
Alex Kane

Posted on

n8n for CleanTech & EnergyTech SaaS: 5 Automations for EPA, ISO 50001, CBAM, and REC Compliance (Free Workflow JSON)

If you're building CleanTech or EnergyTech software, your customers face a growing stack of environmental compliance obligations — EPA chemical reporting, ISO 50001 energy management, EU CBAM carbon border adjustments, and renewable energy certificate (REC) retirement requirements.

Routing that data through cloud automation platforms like Zapier creates audit trail gaps and data sovereignty risks that compliance teams and regulators will flag. Here are 5 production-ready n8n workflows for CleanTech and EnergyTech SaaS vendors — with full import-ready JSON.

This content targets CleanTech & EnergyTech SaaS *vendors* — companies building software for utilities, renewable energy operators, carbon accounting platforms, and environmental compliance teams. Distinct from our earlier CleanTech/Energy SaaS article (FERC/NERC CIP focus); this covers EPA, ISO 50001, CBAM, and REC compliance.


Why self-hosted n8n for CleanTech SaaS?

Compliance Obligation Cloud iPaaS Risk Self-hosted n8n Advantage
EPA TSCA CDR (4-yr cycle) Zapier 30-day log gone before EPA audit Permanent execution log = audit evidence
ISO 50001 §9.1 metering Energy data through external cloud = chain-of-custody gap On-prem metering data stays in your infrastructure
EU CBAM (Reg 2023/956) Carbon calculation methodology proprietary — cloud tool = sub-processor Self-hosted keeps IP confidential
SEC Climate Disclosure Rule (2026) Zapier log deleted before annual attestation Permanent audit trail for material climate risk
REC chain of custody Retirement records in multi-tenant cloud = GHG Protocol §6.3 gap Single-tenant log = clean chain of title

Cost driver: 1,000 IoT energy monitoring sites × 96 readings/day = 9.6M tasks/month → Zapier Enterprise ~$10,000+/mo vs self-hosted VPS ~$30/mo.


Workflow 1: EPA TSCA / CERCLA / TRI Compliance Deadline Tracker

Tracks EPA TSCA CDR (Chemical Data Reporting, 4-year cycle, next 2026), TSCA Section 8(a)/8(d) reporting, CERCLA EPCRA Section 313 TRI annual reports, RCRA Biennial Hazardous Waste Reports, and state environmental deadlines.

5 deadline types tracked:

  • TSCA_CDR_QUADRENNIAL — TSCA §8(a) CDR Report (IUR), every 4 years, penalty $37,500/day
  • EPCRA_SECTION_313_TRI — TRI Annual Report to EPA & state, July 1
  • RCRA_BIENNIAL_REPORT — Hazardous waste generator report, even years March 1
  • CERCLA_EMERGENCY_NOTIFICATION — CERCLA §103 immediate release reporting
  • STATE_ENVIRONMENTAL_PERMIT_RENEWAL — State-level air/water/waste permits
{"name": "EPA TSCA/CERCLA/TRI Compliance Deadline Tracker", "nodes": [{"id": "n1", "name": "Every Weekday 7AM", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.1, "position": [240, 300], "parameters": {"rule": {"interval": [{"field": "cronExpression", "expression": "0 7 * * 1-5"}]}}}, {"id": "n2", "name": "Get EPA Deadlines", "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.4, "position": [440, 300], "parameters": {"operation": "getAll", "documentId": "YOUR_SHEET_ID", "sheetName": "epa_deadlines", "returnAllMatches": true}}, {"id": "n3", "name": "Classify Urgency", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [640, 300], "parameters": {"jsCode": "const items=[];\nfor(const item of $input.all()){\n  const d=item.json;\n  const days=Math.floor((new Date(d.due_date)-new Date())/86400000);\n  let tier='NOTICE',color='#27ae60';\n  if(days<0){tier='OVERDUE';color='#c0392b';}\n  else if(days<=7){tier='CRITICAL';color='#e74c3c';}\n  else if(days<=14){tier='URGENT';color='#e67e22';}\n  else if(days<=30){tier='WARNING';color='#f39c12';}\n  const last=d.alert_sent_date||'';\n  const today=new Date().toISOString().slice(0,10);\n  if((tier==='OVERDUE'||tier==='CRITICAL'||tier==='URGENT')&&last!==today){\n    items.push({json:{...d,days_until_due:days,urgency_tier:tier,color,today}});\n  }\n}\nreturn items;"}}, {"id": "n4", "name": "Route by Tier", "type": "n8n-nodes-base.switch", "typeVersion": 3, "position": [840, 300], "parameters": {"mode": "rules", "rules": {"values": [{"conditions": {"options": {"caseSensitive": true, "leftValue": "", "typeValidation": "strict"}, "conditions": [{"leftValue": "={{$json.urgency_tier}}", "rightValue": "OVERDUE", "operator": {"type": "string", "operation": "equals"}}]}, "outputIndex": 0}, {"conditions": {"options": {"caseSensitive": true, "leftValue": "", "typeValidation": "strict"}, "conditions": [{"leftValue": "={{$json.urgency_tier}}", "rightValue": "CRITICAL", "operator": {"type": "string", "operation": "equals"}}]}, "outputIndex": 0}, {"conditions": {"options": {"caseSensitive": true, "leftValue": "", "typeValidation": "strict"}, "conditions": [{"leftValue": "={{$json.urgency_tier}}", "rightValue": "URGENT", "operator": {"type": "string", "operation": "equals"}}]}, "outputIndex": 1}]}}}, {"id": "n5", "name": "Slack #epa-compliance", "type": "n8n-nodes-base.slack", "typeVersion": 2.2, "position": [1040, 200], "parameters": {"operation": "post", "channel": "#epa-compliance", "text": "=[{{$json.urgency_tier}}] *{{$json.regulation}}* deadline: {{$json.due_date}} ({{$json.days_until_due}} days) \u2014 Owner: {{$json.responsible_party}}\\nAction: {{$json.required_action}}\\nPenalty: {{$json.penalty_risk}}"}}, {"id": "n6", "name": "Email Owner", "type": "n8n-nodes-base.gmail", "typeVersion": 2.1, "position": [1040, 360], "parameters": {"operation": "send", "sendTo": "={{$json.owner_email}}", "subject": "=[{{$json.urgency_tier}}] EPA Deadline: {{$json.regulation}} \u2014 {{$json.due_date}}", "message": "=Regulation: {{$json.regulation}}\\nDue: {{$json.due_date}} ({{$json.days_until_due}} days)\\nAction: {{$json.required_action}}\\nPenalty: {{$json.penalty_risk}}"}}, {"id": "n7", "name": "Mark Alert Sent", "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.4, "position": [1240, 300], "parameters": {"operation": "update", "documentId": "YOUR_SHEET_ID", "sheetName": "epa_deadlines", "columns": {"mappingMode": "defineBelow", "value": {"alert_sent_date": "={{$json.today}}"}}}}], "connections": {"Every Weekday 7AM": {"main": [[{"node": "Get EPA Deadlines", "type": "main", "index": 0}]]}, "Get EPA Deadlines": {"main": [[{"node": "Classify Urgency", "type": "main", "index": 0}]]}, "Classify Urgency": {"main": [[{"node": "Route by Tier", "type": "main", "index": 0}]]}, "Route by Tier": {"main": [[{"node": "Slack #epa-compliance", "type": "main", "index": 0}], [{"node": "Email Owner", "type": "main", "index": 0}]]}, "Slack #epa-compliance": {"main": [[{"node": "Mark Alert Sent", "type": "main", "index": 0}]]}, "Email Owner": {"main": [[{"node": "Mark Alert Sent", "type": "main", "index": 0}]]}}}
Enter fullscreen mode Exit fullscreen mode

Self-hosting angle: EPA TSCA CDR audit windows span 5+ years. Zapier's 30-day log retention means execution records are gone long before an EPA inspector arrives. With self-hosted n8n, every Mark Alert Sent node write is a timestamped database record — retrievable by case number.


Workflow 2: ISO 50001 Energy Management KPI Monitor

Tracks Energy Performance Indicators (EnPI) vs baseline, ISO 50001 certification expiry, and annual internal audit compliance per ISO 50001:2018 §9.1 (monitoring, measurement, analysis, and evaluation of energy performance).

Triggers on:

  • EnPI deviation ≥ 10% vs baseline (ISO 50001 §6.3 significant energy use threshold)
  • ISO 50001 certification expiring ≤ 30 days
  • Internal audit overdue (>365 days since last §9.2 audit)
{"name": "ISO 50001 Energy Management KPI Monitor", "nodes": [{"id": "n1", "name": "Weekday 8AM", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.1, "position": [240, 300], "parameters": {"rule": {"interval": [{"field": "cronExpression", "expression": "0 8 * * 1-5"}]}}}, {"id": "n2", "name": "Query Energy Data", "type": "n8n-nodes-base.postgres", "typeVersion": 2.5, "position": [440, 300], "parameters": {"operation": "executeQuery", "query": "SELECT site_id, site_name, account_manager_email, enpi_baseline_kwh_per_unit, enpi_current_kwh_per_unit, iso_50001_certified, certification_expiry_date, last_internal_audit_date, significant_energy_use_flag FROM energy_management WHERE active = true"}}, {"id": "n3", "name": "Calculate Deviations", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [640, 300], "parameters": {"jsCode": "const items=[];\nfor(const item of $input.all()){\n  const d=item.json;\n  const deviation=d.enpi_baseline_kwh_per_unit>0?((d.enpi_current_kwh_per_unit-d.enpi_baseline_kwh_per_unit)/d.enpi_baseline_kwh_per_unit*100):0;\n  const certExpiry=new Date(d.certification_expiry_date);\n  const daysToCertExpiry=Math.floor((certExpiry-new Date())/86400000);\n  const lastAudit=new Date(d.last_internal_audit_date);\n  const daysSinceAudit=Math.floor((new Date()-lastAudit)/86400000);\n  let issues=[];\n  if(Math.abs(deviation)>=10) issues.push({type:'ISO_50001_SIGNIFICANT_DEVIATION',detail:`EnPI deviation ${deviation.toFixed(1)}% vs baseline (\u00a79.1 significant energy use threshold)`,severity:'HIGH'});\n  if(daysToCertExpiry<=30) issues.push({type:'ISO_50001_CERT_EXPIRING',detail:`Certification expires in ${daysToCertExpiry} days`,severity:daysToCertExpiry<=7?'CRITICAL':'URGENT'});\n  if(daysSinceAudit>365) issues.push({type:'ISO_50001_AUDIT_OVERDUE',detail:`Internal audit ${daysSinceAudit} days ago (\u00a79.2 annual requirement)`,severity:'WARNING'});\n  if(issues.length>0) items.push({json:{...d,deviation_pct:deviation.toFixed(1),daysToCertExpiry,daysSinceAudit,issues}});\n}\nreturn items;"}}, {"id": "n4", "name": "Slack #sustainability", "type": "n8n-nodes-base.slack", "typeVersion": 2.2, "position": [840, 300], "parameters": {"operation": "post", "channel": "#sustainability", "text": "=:leaf: *ISO 50001 Alert \u2014 {{$json.site_name}}*\\n{{#each $json.issues}}[{{this.severity}}] {{this.type}}: {{this.detail}}\\n{{/each}}\\nEnPI Current: {{$json.enpi_current_kwh_per_unit}} kWh/unit | Baseline: {{$json.enpi_baseline_kwh_per_unit}} kWh/unit | Deviation: {{$json.deviation_pct}}%"}}, {"id": "n5", "name": "Email Energy Manager", "type": "n8n-nodes-base.gmail", "typeVersion": 2.1, "position": [1040, 300], "parameters": {"operation": "send", "sendTo": "={{$json.account_manager_email}}", "subject": "=ISO 50001 Energy Management Alert \u2014 {{$json.site_name}}", "message": "=ISO 50001 issues detected for {{$json.site_name}}:\\n\\n{{#each $json.issues}}- [{{this.severity}}] {{this.type}}: {{this.detail}}\\n{{/each}}\\nEnPI Current: {{$json.enpi_current_kwh_per_unit}} kWh/unit\\nBaseline: {{$json.enpi_baseline_kwh_per_unit}} kWh/unit\\nDeviation: {{$json.deviation_pct}}%"}}], "connections": {"Weekday 8AM": {"main": [[{"node": "Query Energy Data", "type": "main", "index": 0}]]}, "Query Energy Data": {"main": [[{"node": "Calculate Deviations", "type": "main", "index": 0}]]}, "Calculate Deviations": {"main": [[{"node": "Slack #sustainability", "type": "main", "index": 0}]]}, "Slack #sustainability": {"main": [[{"node": "Email Energy Manager", "type": "main", "index": 0}]]}}}
Enter fullscreen mode Exit fullscreen mode

Self-hosting angle: ISO 50001 certification bodies audit automation systems during surveillance audits. An energy data pipeline running through Zapier raises metering chain-of-custody questions — can the auditor verify that Zapier didn't modify the energy readings before logging? Self-hosted n8n in the same network as the energy metering system removes that uncertainty.


Workflow 3: EU CBAM Carbon Border Adjustment Mechanism Reporting Pipeline

EU Regulation 2023/956 (effective Oct 2023 for transitional period, full obligations Jan 2026): importers of iron/steel, aluminium, cement, fertilisers, electricity, and hydrogen into the EU must file quarterly CBAM declarations and purchase CBAM certificates covering embedded carbon.

This webhook-triggered pipeline logs every CBAM import declaration, calculates the quarterly filing deadline, classifies urgency (OVERDUE/CRITICAL/URGENT/OK), and routes to Slack and compliance team.

{"name": "EU CBAM Carbon Border Adjustment Mechanism Reporting Pipeline", "nodes": [{"id": "n1", "name": "Webhook Trigger", "type": "n8n-nodes-base.webhook", "typeVersion": 2, "position": [240, 300], "parameters": {"path": "cbam-declaration", "httpMethod": "POST", "responseMode": "onReceived"}}, {"id": "n2", "name": "Classify CBAM Event", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [440, 300], "parameters": {"jsCode": "const d=$input.first().json;\nconst eventType=d.event_type||'CBAM_IMPORT_DECLARATION';\nconst cbamSectors={'IRON_STEEL':'Direct+indirect+MEE emissions','ALUMINIUM':'Direct+indirect emissions','CEMENT':'Process+fuel combustion emissions','FERTILISERS':'N2O+nitric acid process emissions','ELECTRICITY':'Direct emissions only','HYDROGEN':'SMR process emissions'};\nconst sectorDetail=cbamSectors[d.cn_code_sector]||'Standard embedded carbon';\n// CBAM transition period: full obligations from Jan 2026\nconst quarterlyDeadlines={'Q1':new Date(new Date().getFullYear(),3,31),'Q2':new Date(new Date().getFullYear(),6,31),'Q3':new Date(new Date().getFullYear(),9,31),'Q4':new Date(new Date().getFullYear()+1,0,31)};\nconst now=new Date();\nconst currentMonth=now.getMonth();\nlet nextDeadline,quarterLabel;\nif(currentMonth<3){nextDeadline=quarterlyDeadlines.Q1;quarterLabel='Q1';}\nelse if(currentMonth<6){nextDeadline=quarterlyDeadlines.Q2;quarterLabel='Q2';}\nelse if(currentMonth<9){nextDeadline=quarterlyDeadlines.Q3;quarterLabel='Q3';}\nelse{nextDeadline=quarterlyDeadlines.Q4;quarterLabel='Q4';}\nconst daysToDeadline=Math.floor((nextDeadline-now)/86400000);\nconst urgency=daysToDeadline<0?'OVERDUE':daysToDeadline<=14?'CRITICAL':daysToDeadline<=30?'URGENT':'OK';\nreturn [{json:{...d,event_type:eventType,sector_detail:sectorDetail,next_deadline:nextDeadline.toISOString().slice(0,10),quarter_label:quarterLabel,days_to_deadline:daysToDeadline,urgency,regulation_ref:'EU Regulation 2023/956 Art.35'}}];\n"}}, {"id": "n3", "name": "Log to Postgres", "type": "n8n-nodes-base.postgres", "typeVersion": 2.5, "position": [640, 300], "parameters": {"operation": "insert", "table": "cbam_declarations", "columns": "importer_id,cn_code_sector,import_date,embedded_carbon_tco2e,country_of_origin,event_type,urgency,next_deadline,quarter_label,regulation_ref,created_at", "values": "='{{$json.importer_id}}','{{$json.cn_code_sector}}','{{$json.import_date}}',{{$json.embedded_carbon_tco2e}},'{{$json.country_of_origin}}','{{$json.event_type}}','{{$json.urgency}}','{{$json.next_deadline}}','{{$json.quarter_label}}','{{$json.regulation_ref}}',NOW()"}}, {"id": "n4", "name": "Alert if Critical", "type": "n8n-nodes-base.if", "typeVersion": 2.1, "position": [840, 300], "parameters": {"conditions": {"options": {"caseSensitive": true, "leftValue": "", "typeValidation": "strict"}, "conditions": [{"leftValue": "={{$json.urgency}}", "rightValue": "OK", "operator": {"type": "string", "operation": "notEquals"}}]}}}, {"id": "n5", "name": "Slack #regulatory-trade", "type": "n8n-nodes-base.slack", "typeVersion": 2.2, "position": [1040, 200], "parameters": {"operation": "post", "channel": "#regulatory-trade", "text": "=[CBAM {{$json.urgency}}] *{{$json.importer_id}}* \u2014 {{$json.cn_code_sector}} import\\nEmbedded carbon: {{$json.embedded_carbon_tco2e}} tCO\u2082e | Country: {{$json.country_of_origin}}\\n{{$json.quarter_label}} filing deadline: {{$json.next_deadline}} ({{$json.days_to_deadline}} days)\\nRef: {{$json.regulation_ref}}"}}, {"id": "n6", "name": "ACK", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.1, "position": [840, 400], "parameters": {"respondWith": "json", "responseBody": "={\"status\":\"logged\",\"urgency\":\"{{$json.urgency}}\",\"days_to_cbam_deadline\":{{$json.days_to_deadline}},\"regulation\":\"EU 2023/956\"}"}}], "connections": {"Webhook Trigger": {"main": [[{"node": "Classify CBAM Event", "type": "main", "index": 0}]]}, "Classify CBAM Event": {"main": [[{"node": "Log to Postgres", "type": "main", "index": 0}]]}, "Log to Postgres": {"main": [[{"node": "Alert if Critical", "type": "main", "index": 0}]]}, "Alert if Critical": {"main": [[{"node": "Slack #regulatory-trade", "type": "main", "index": 0}], [{"node": "ACK", "type": "main", "index": 0}]]}}}
Enter fullscreen mode Exit fullscreen mode

Self-hosting angle: CBAM embedded carbon calculation methodology is proprietary competitive intelligence (production efficiency data, supplier emission factors, energy mix). Running CBAM calculations through a multi-tenant cloud platform risks exposing that IP to the cloud provider's data handling. Self-hosted n8n keeps calculations in your infrastructure.


Workflow 4: Renewable Energy Certificate (REC / I-REC / GECS) Tracking & Retirement Monitor

Tracks REC inventory across registries (M-RETS, NAR, WREGIS, I-REC, GECS), monitors expiry dates, calculates coverage gaps against renewable energy targets, and flags SEC Climate Disclosure risk for accounts with attestation obligations.

Why this matters:

  • GHG Protocol Scope 2 market-based: unretired or expired RECs cannot support renewable energy claims
  • SEC Climate Disclosure Rule (effective 2026): large accelerated filers must attest Scope 1/2 emissions — REC retirement records are direct supporting evidence
  • Science-Based Targets (SBTi): net-zero pathways require annual REC retirement matching renewable energy targets
{"name": "Renewable Energy Certificate (REC/I-REC/GECS) Tracking & Retirement Monitor", "nodes": [{"id": "n1", "name": "Daily 6AM", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.1, "position": [240, 300], "parameters": {"rule": {"interval": [{"field": "cronExpression", "expression": "0 6 * * *"}]}}}, {"id": "n2", "name": "Query REC Inventory", "type": "n8n-nodes-base.postgres", "typeVersion": 2.5, "position": [440, 300], "parameters": {"operation": "executeQuery", "query": "SELECT account_id, account_name, cso_email, rec_type, rec_registry, rec_quantity_mwh, rec_expiry_date, rec_status, renewable_energy_claim_target_mwh, retired_mwh_ytd, sec_climate_disclosure_applicable FROM rec_inventory WHERE rec_status NOT IN ('RETIRED','CANCELLED')"}}, {"id": "n3", "name": "Calculate REC Status", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [640, 300], "parameters": {"jsCode": "const items=[];\nfor(const item of $input.all()){\n  const d=item.json;\n  const expiry=new Date(d.rec_expiry_date);\n  const daysToExpiry=Math.floor((expiry-new Date())/86400000);\n  const coveragePct=d.renewable_energy_claim_target_mwh>0?(d.retired_mwh_ytd/d.renewable_energy_claim_target_mwh*100):0;\n  let tier='OK';\n  if(daysToExpiry<0) tier='EXPIRED';\n  else if(daysToExpiry<=30) tier='CRITICAL';\n  else if(daysToExpiry<=60) tier='URGENT';\n  else if(daysToExpiry<=90) tier='WARNING';\n  const shortfall=d.renewable_energy_claim_target_mwh-d.retired_mwh_ytd;\n  const secDisclosureRisk=d.sec_climate_disclosure_applicable&&coveragePct<100;\n  if(tier!=='OK'||secDisclosureRisk){\n    items.push({json:{...d,days_to_expiry:daysToExpiry,coverage_pct:coveragePct.toFixed(1),shortfall_mwh:shortfall,urgency_tier:tier,sec_disclosure_risk:secDisclosureRisk}});\n  }\n}\nreturn items;"}}, {"id": "n4", "name": "Slack #sustainability", "type": "n8n-nodes-base.slack", "typeVersion": 2.2, "position": [840, 300], "parameters": {"operation": "post", "channel": "#sustainability", "text": "=:solar_panels: *REC Alert \u2014 {{$json.account_name}}*\\nStatus: [{{$json.urgency_tier}}] {{$json.rec_type}} ({{$json.rec_registry}})\\n{{$json.rec_quantity_mwh}} MWh | Expires: {{$json.rec_expiry_date}} ({{$json.days_to_expiry}} days)\\nCoverage: {{$json.coverage_pct}}% of {{$json.renewable_energy_claim_target_mwh}} MWh target{{#if $json.sec_disclosure_risk}}\\n\u26a0\ufe0f SEC Climate Disclosure risk: coverage gap {{$json.shortfall_mwh}} MWh{{/if}}"}}, {"id": "n5", "name": "Email CSO", "type": "n8n-nodes-base.gmail", "typeVersion": 2.1, "position": [1040, 300], "parameters": {"operation": "send", "sendTo": "={{$json.cso_email}}", "subject": "=REC [{{$json.urgency_tier}}] \u2014 {{$json.account_name}}: {{$json.rec_type}} expiry {{$json.rec_expiry_date}}", "message": "=REC inventory alert for {{$json.account_name}}:\\n\\nREC Type: {{$json.rec_type}} ({{$json.rec_registry}})\\nQuantity: {{$json.rec_quantity_mwh}} MWh\\nExpiry: {{$json.rec_expiry_date}} ({{$json.days_to_expiry}} days)\\nCoverage: {{$json.coverage_pct}}% of {{$json.renewable_energy_claim_target_mwh}} MWh target\\n\\nNote: Unretired RECs cannot support renewable energy claims. Expired RECs invalidate Scope 2 market-based reporting (GHG Protocol Scope 2 Guidance)."}}], "connections": {"Daily 6AM": {"main": [[{"node": "Query REC Inventory", "type": "main", "index": 0}]]}, "Query REC Inventory": {"main": [[{"node": "Calculate REC Status", "type": "main", "index": 0}]]}, "Calculate REC Status": {"main": [[{"node": "Slack #sustainability", "type": "main", "index": 0}]]}, "Slack #sustainability": {"main": [[{"node": "Email CSO", "type": "main", "index": 0}]]}}}
Enter fullscreen mode Exit fullscreen mode

Workflow 5: Weekly CleanTech Platform Compliance KPI Dashboard

Monday 8AM executive summary: active accounts, ISO 50001 certified count, total REC inventory, CBAM account exposure, average EnPI deviation, and open compliance deadlines — with dynamic subject line flags.

{"name": "Weekly CleanTech Platform Compliance KPI Dashboard", "nodes": [{"id": "n1", "name": "Every Monday 8AM", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.1, "position": [240, 300], "parameters": {"rule": {"interval": [{"field": "cronExpression", "expression": "0 8 * * 1"}]}}}, {"id": "n2", "name": "Query Platform Metrics", "type": "n8n-nodes-base.postgres", "typeVersion": 2.5, "position": [440, 200], "parameters": {"operation": "executeQuery", "query": "SELECT COUNT(DISTINCT account_id) AS active_accounts, COUNT(DISTINCT CASE WHEN iso_50001_certified=true THEN account_id END) AS iso_certified_accounts, SUM(rec_quantity_mwh) AS total_rec_inventory_mwh, COUNT(DISTINCT CASE WHEN cbam_applicable=true THEN account_id END) AS cbam_accounts, AVG(enpi_deviation_pct) AS avg_enpi_deviation FROM customer_sustainability_view WHERE active=true"}}, {"id": "n3", "name": "Query Compliance Events", "type": "n8n-nodes-base.postgres", "typeVersion": 2.5, "position": [440, 400], "parameters": {"operation": "executeQuery", "query": "SELECT COUNT(*) FILTER (WHERE urgency_tier='OVERDUE') AS overdue_deadlines, COUNT(*) FILTER (WHERE urgency_tier='CRITICAL') AS critical_deadlines, COUNT(*) FILTER (WHERE event_type LIKE 'CBAM%') AS cbam_declarations_pending, COUNT(*) FILTER (WHERE tier='EXPIRED' OR tier='CRITICAL') AS rec_expiring_30d FROM compliance_events WHERE created_at >= NOW()-INTERVAL '7 days'"}}, {"id": "n4", "name": "Merge", "type": "n8n-nodes-base.merge", "typeVersion": 3, "position": [640, 300], "parameters": {"mode": "combine", "combinationMode": "mergeByPosition"}}, {"id": "n5", "name": "Build KPI Report", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [840, 300], "parameters": {"jsCode": "const d=$input.first().json;\nconst state=$getWorkflowStaticData('global');\nconst prevAccounts=state.prev_active_accounts||d.active_accounts;\nconst accountWoW=(((d.active_accounts-prevAccounts)/Math.max(prevAccounts,1))*100).toFixed(1);\nstate.prev_active_accounts=d.active_accounts;\nconst cbamdFlag=d.cbam_declarations_pending>0?`[CBAM: ${d.cbam_declarations_pending} PENDING] `:\\'\\'; \nconst recFlag=d.rec_expiring_30d>0?`[REC: ${d.rec_expiring_30d} EXPIRING] `:\\'\\'; \nconst overdueFlag=d.overdue_deadlines>0?`[OVERDUE: ${d.overdue_deadlines}] `:\\'\\'; \nconst subject=`${cbamdFlag}${recFlag}${overdueFlag}Weekly CleanTech Compliance KPI \u2014 ${new Date().toISOString().slice(0,10)}`;\nconst html=`<h2>CleanTech & EnergyTech Weekly KPI</h2>\n<table border=\\'1\\'><tr><th>Metric</th><th>Value</th><th>WoW</th></tr>\n<tr><td>Active Accounts</td><td>${d.active_accounts}</td><td>${accountWoW}%</td></tr>\n<tr><td>ISO 50001 Certified</td><td>${d.iso_certified_accounts}</td><td>\u2014</td></tr>\n<tr><td>REC Inventory (MWh)</td><td>${Math.round(d.total_rec_inventory_mwh||0)}</td><td>\u2014</td></tr>\n<tr><td>CBAM Accounts</td><td>${d.cbam_accounts}</td><td>\u2014</td></tr>\n<tr><td>Avg EnPI Deviation</td><td style=\\'color:${Math.abs(d.avg_enpi_deviation||0)>=10?\\'#c0392b\\':\\'#27ae60\\'\\'>${(d.avg_enpi_deviation||0).toFixed(1)}%</td><td>\u2014</td></tr>\n<tr><td>Overdue Compliance Deadlines</td><td style=\\'color:${d.overdue_deadlines>0?\\'#c0392b\\':\\'#27ae60\\'\\'>${d.overdue_deadlines}</td><td>\u2014</td></tr>\n<tr><td>CBAM Declarations Pending</td><td>${d.cbam_declarations_pending}</td><td>\u2014</td></tr>\n<tr><td>RECs Expiring \u226430 Days</td><td style=\\'color:${d.rec_expiring_30d>0?\\'#e67e22\\':\\'#27ae60\\'\\'>${d.rec_expiring_30d}</td><td>\u2014</td></tr>\n</table>`;\nreturn [{json:{...d,subject,html,account_wow:accountWoW}}];\n"}}, {"id": "n6", "name": "Email CEO", "type": "n8n-nodes-base.gmail", "typeVersion": 2.1, "position": [1040, 300], "parameters": {"operation": "send", "sendTo": "ceo@cleantech-saas.com", "bccList": "cto@cleantech-saas.com,cso@cleantech-saas.com,cfp@cleantech-saas.com", "subject": "={{$json.subject}}", "message": "={{$json.html}}", "options": {"replyTo": ""}}}], "connections": {"Every Monday 8AM": {"main": [[{"node": "Query Platform Metrics", "type": "main", "index": 0}, {"node": "Query Compliance Events", "type": "main", "index": 0}]]}, "Query Platform Metrics": {"main": [[{"node": "Merge", "type": "main", "index": 0}]]}, "Query Compliance Events": {"main": [[{"node": "Merge", "type": "main", "index": 1}]]}, "Merge": {"main": [[{"node": "Build KPI Report", "type": "main", "index": 0}]]}, "Build KPI Report": {"main": [[{"node": "Email CEO", "type": "main", "index": 0}]]}}}
Enter fullscreen mode Exit fullscreen mode

Subject line example: [CBAM: 3 PENDING] [REC: 7 EXPIRING] Weekly CleanTech Compliance KPI — 2026-05-24


Deployment notes

  1. Edge deployment for IoT sites: Raspberry Pi or industrial PC running n8n on-site can process energy telemetry without sending data to external clouds — critical for NERC CIP BCSI (BES Cyber System Information) if you serve electric utilities.
  2. Science-Based Targets (SBTi) integration: Add an HTTP Request node polling the SBTi API to verify customer targets and flag coverage gaps vs REC inventory.
  3. CBAM Registry integration: EU CBAM Transitional Registry API (available Q3 2026) can replace the manual Sheets tracker in Workflow 3.
  4. Scope 3 Category 11 tracking: Add downstream customer emissions tracking for SaaS vendors with net-zero commitments.

Free templates

All 5 workflows above are available as import-ready JSON at stripeai.gumroad.com — FlowKit n8n automation templates. If you're building CleanTech or EnergyTech SaaS and need custom compliance workflows, reach out.


Tags: n8n, EPA TSCA, ISO 50001, CBAM, REC tracking, CleanTech SaaS, renewable energy compliance, carbon accounting automation

Top comments (0)