DEV Community

Alex Kane
Alex Kane

Posted on

n8n for SmartBuilding/PropClimateTech SaaS Vendors: 5 Automations for NYC LL97, EU EPBD, and ENERGY STAR Compliance

If you sell SaaS to building operators, property managers, or commercial real estate portfolios, your platform processes data that regulators now treat as compliance evidence. NYC Local Law 97 penalties started in 2024. The EU EPBD recast requires EPC certificates at every property transaction. ENERGY STAR benchmarking is mandatory in 30+ US jurisdictions. ISO 50001 audit cycles don't pause for API outages.

Your cloud automation stack sits in the middle of all of it. This article covers 5 n8n workflows — with full JSON — built specifically for SmartBuilding and PropClimateTech SaaS vendors navigating these overlapping frameworks.


The Regulatory Stack

NYC Local Law 97 (LL97) — Retroactive Carbon Penalty

Buildings >25,000 sq ft in NYC face annual carbon emissions caps. Penalties: $268 per tonne CO2e above the cap, retroactive for the prior calendar year. May 1 annual filing deadline — no extensions. The 2024–2029 caps are stricter than 2030–2034, then tighten again. Your BMS SaaS ingests utility data that determines penalty exposure. If your pipeline lags or fails, the building operator files wrong.

Clock trigger: calendar year end (Dec 31) locks the consumption period. May 1 = filing deadline. $268/tonne overage that was invisible in October is a six-figure liability in May.

EU EPBD 2024 Recast (Directive 2024/1275/EU)

Energy Performance Certificates required at every property transaction and new rental. Minimum Energy Performance Standards (MEPS): worst-performing non-residential buildings must reach EPC class F by 2030, class E by 2033, class D by 2040. Zero-emission buildings (ZEB) target for all new buildings. Building Renovation Passports introduced.

Clock trigger: property listing, lease signing, or transaction trigger — not annual. The automation must catch the event at intake, not batch-process weekly.

ENERGY STAR Portfolio Manager (EPA)

Annual benchmarking required in 30+ US jurisdictions (NYC Local Law 84, DC, Chicago, etc.). ENERGY STAR certification requires score ≥75 — scores are recertified annually. Score drop from 75 to 74.9 = certification lost = LEED EBOM compliance chain broken for tenants.

ISO 50001:2018 — Energy Management System

Annual energy review + significant energy uses (SEUs). Internal audit cycle. EnMS certification suspended if gap found → EU Taxonomy Art.10 green bond covenant may trigger for investors.

EU Taxonomy Art.10 (Regulation 2020/852) — Climate Mitigation

Buildings qualify if: near-zero energy building (NZEB) standard OR primary energy demand ≤10% below national NZEB benchmark OR top 15% national building stock. Banks and investors must verify before ESG disclosure. Annual assessment cycle tied to CSRD reporting.

ASHRAE 90.1 / CA Title 24

ASHRAE 90.1-2022 is the US baseline energy efficiency standard referenced by most state codes. CA Title 24 Part 6 (California Energy Code) updated every 3 years — applies to new construction and alterations. Both generate project-based compliance deadlines tied to permit issuance, not calendar year.

WELL Building Standard v2

WELL certification requires recertification every 3 years. In-progress certifications have active review windows. Failing a recertification mid-lease creates tenant covenant risk.


Customer Tier Segmentation

Not every building operator faces the same stack. Tier-segment your onboarding automation:

{
  "customer_tiers": [
    {
      "tier": "ENTERPRISE_BMS_SAAS",
      "description": "Large BMS platform serving Fortune 500 multi-site portfolios",
      "primary_regulations": [
        "NYC_LL97",
        "EU_EPBD",
        "ENERGY_STAR",
        "ISO_50001",
        "EU_TAXONOMY_ART10"
      ],
      "key_risk": "Portfolio-level carbon liability ($268/tonne) across 50+ buildings \u2014 one missed filing = seven-figure penalty"
    },
    {
      "tier": "COMMERCIAL_RE_ENERGY_SAAS",
      "description": "Energy management for multi-tenant commercial buildings",
      "primary_regulations": [
        "NYC_LL97",
        "ENERGY_STAR",
        "ASHRAE_90_1"
      ],
      "key_risk": "Tenant-level consumption attribution errors cause incorrect LL97 cap allocation and penalty disputes"
    },
    {
      "tier": "SMART_BUILDING_IOTANALYTICS_SAAS",
      "description": "IoT sensor analytics and building intelligence platform",
      "primary_regulations": [
        "ENERGY_STAR",
        "ASHRAE_90_1",
        "ISO_50001",
        "GDPR_ART5"
      ],
      "key_risk": "Sensor data gaps during critical consumption periods create unverifiable audit trails"
    },
    {
      "tier": "PROPTECH_ENERGY_MGMT_SAAS",
      "description": "PropTech energy optimization for real estate investment portfolios",
      "primary_regulations": [
        "EU_EPBD",
        "EU_TAXONOMY_ART10",
        "ENERGY_STAR",
        "NYC_LL97"
      ],
      "key_risk": "EU Taxonomy Art.10 misclassification on green bond prospectus \u2014 investor liability"
    },
    {
      "tier": "GREEN_BUILDING_CERTIFICATION_SAAS",
      "description": "LEED/BREEAM/WELL certification management platform",
      "primary_regulations": [
        "ENERGY_STAR",
        "WELL_BUILDING_V2",
        "ASHRAE_90_1",
        "EU_TAXONOMY_ART10"
      ],
      "key_risk": "Certification lapse mid-lease = tenant covenant breach = legal exposure"
    },
    {
      "tier": "HVAC_CONTROLS_SAAS",
      "description": "HVAC optimization and controls SaaS",
      "primary_regulations": [
        "ASHRAE_90_1",
        "CA_TITLE_24",
        "NYC_LL97",
        "ISO_50001"
      ],
      "key_risk": "ASHRAE 90.1 commissioning documentation gaps found during code inspection = project permit risk"
    },
    {
      "tier": "SMARTBUILDING_STARTUP_SAAS",
      "description": "Early-stage smart building tech startup (<$5M ARR)",
      "primary_regulations": [
        "ENERGY_STAR",
        "NYC_LL97"
      ],
      "key_risk": "First enterprise deal with NYC portfolio requires LL97 integration \u2014 missed = lost deal"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Workflow 1: NYC LL97 Carbon Emissions Monitor & Penalty Calculator

Why this workflow exists: LL97 penalties are retroactive. The building's carbon liability for calendar year 2024 is locked on December 31, 2024 — and not due until May 1, 2025. Without a daily trajectory monitor, building operators discover their penalty exposure in April when it's too late to change behavior.

The self-hosting angle: Granular monthly utility consumption data, equipment schedules, and overage trajectory projections are commercial intelligence. Cloud automation vendor staff with log access can see which buildings are over-cap — information your enterprise clients don't want shared.

{
  "name": "NYC LL97 Carbon Emissions Monitor & Penalty Calculator",
  "nodes": [
    {
      "id": "n1",
      "name": "Daily Trigger 6AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 24,
              "triggerAtHour": 6
            }
          ]
        }
      }
    },
    {
      "id": "n2",
      "name": "Query Building Portfolio",
      "type": "n8n-nodes-base.postgres",
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT b.building_id, b.name, b.address, b.gross_sq_ft, b.ll97_cap_2024_tonnes, b.ll97_cap_2030_tonnes, SUM(e.co2e_tonnes) AS ytd_co2e_tonnes, COUNT(e.id) AS meter_readings, MAX(e.reading_date) AS last_reading_date, b.may1_filing_status, b.energy_star_score FROM buildings b LEFT JOIN energy_readings e ON b.building_id = e.building_id AND EXTRACT(YEAR FROM e.reading_date) = EXTRACT(YEAR FROM CURRENT_DATE) WHERE b.nyc_ll97_subject = TRUE AND b.active = TRUE GROUP BY b.building_id, b.name, b.address, b.gross_sq_ft, b.ll97_cap_2024_tonnes, b.ll97_cap_2030_tonnes, b.may1_filing_status, b.energy_star_score"
      }
    },
    {
      "id": "n3",
      "name": "Calculate LL97 Exposure",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "\nconst buildings = $input.all();\nconst results = [];\nconst today = new Date();\nconst yearEnd = new Date(today.getFullYear(), 11, 31);\nconst may1 = new Date(today.getFullYear() + 1, 4, 1);\nconst dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / 86400000);\nconst daysInYear = 365;\nconst yearFraction = dayOfYear / daysInYear;\nfor (const b of buildings) {\n  const d = b.json;\n  const cap = parseFloat(d.ll97_cap_2024_tonnes) || 0;\n  const ytd = parseFloat(d.ytd_co2e_tonnes) || 0;\n  const projected = yearFraction > 0 ? ytd / yearFraction : 0;\n  const overage = Math.max(0, projected - cap);\n  const penaltyUSD = Math.round(overage * 268);\n  const daysToMay1 = Math.floor((may1 - today) / 86400000);\n  const daysSinceLastReading = d.last_reading_date ?\n    Math.floor((today - new Date(d.last_reading_date)) / 86400000) : 999;\n  let severity = 'NOTICE';\n  let alert = false;\n  if (projected > cap) { severity = 'CRITICAL'; alert = true; }\n  else if (projected > cap * 0.9) { severity = 'WARNING'; alert = true; }\n  else if (projected > cap * 0.75) { severity = 'WATCH'; alert = true; }\n  if (daysSinceLastReading > 3) { severity = 'DATA_GAP'; alert = true; }\n  results.push({\n    json: {\n      ...d, projected_annual_co2e: projected.toFixed(2),\n      overage_tonnes: overage.toFixed(2), penalty_usd: penaltyUSD,\n      severity, alert, days_to_may1: daysToMay1,\n      days_since_last_reading: daysSinceLastReading,\n      cap_utilization_pct: cap > 0 ? ((projected / cap) * 100).toFixed(1) : '0'\n    }\n  });\n}\nreturn results.filter(r => r.json.alert);\n"
      }
    },
    {
      "id": "n4",
      "name": "Route by Severity",
      "type": "n8n-nodes-base.if",
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json.severity}}",
              "operation": "equal",
              "value2": "CRITICAL"
            }
          ]
        }
      }
    },
    {
      "id": "n5",
      "name": "Slack Critical Alert",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#nyc-ll97-critical",
        "text": "CRITICAL: {{ $json.name }} \u2014 {{ $json.address }}\nProjected: {{ $json.projected_annual_co2e }} tonnes vs cap {{ $json.ll97_cap_2024_tonnes }} tonnes\nOverage: {{ $json.overage_tonnes }} tonnes | Penalty: ${{ $json.penalty_usd }} | Days to May 1: {{ $json.days_to_may1 }}"
      }
    },
    {
      "id": "n6",
      "name": "Slack Warning Alert",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#nyc-ll97-monitoring",
        "text": "{{ $json.severity }}: {{ $json.name }} \u2014 Cap utilization {{ $json.cap_utilization_pct }}% | Days to May 1: {{ $json.days_to_may1 }}"
      }
    }
  ],
  "connections": {
    "Daily Trigger 6AM": {
      "main": [
        [
          {
            "node": "Query Building Portfolio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Query Building Portfolio": {
      "main": [
        [
          {
            "node": "Calculate LL97 Exposure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate LL97 Exposure": {
      "main": [
        [
          {
            "node": "Route by Severity",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Severity": {
      "main": [
        [
          {
            "node": "Slack Critical Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack Warning Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 2: EU EPBD EPC Deadline Tracker & Transaction Alert

Why this workflow exists: EU EPBD requires an EPC at every property transaction AND new rental — not annually. The clock starts at the transaction event, not the calendar year. Your PropTech SaaS must catch the lease signing trigger at intake, not batch-process weekly.

The MEPS upgrade cascade: EPC class G → F by 2030 (non-residential), class E by 2033, class D by 2040. If your platform tracks renovation timelines, the 2030 deadline is 5 years away — but planning, permitting, and procurement chains mean the real trigger is now.

{
  "name": "EU EPBD EPC Deadline Tracker & Transaction Alert",
  "nodes": [
    {
      "id": "n1",
      "name": "Webhook: Property Event",
      "type": "n8n-nodes-base.webhook",
      "parameters": {
        "path": "epbd-property-event",
        "httpMethod": "POST"
      }
    },
    {
      "id": "n2",
      "name": "Classify Event Type",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "\nconst evt = $json;\nconst eventType = evt.event_type;\nconst epcClass = evt.epc_class || 'UNKNOWN';\nconst epcExpiry = evt.epc_expiry_date ? new Date(evt.epc_expiry_date) : null;\nconst today = new Date();\nconst epcExpired = epcExpiry ? epcExpiry < today : true;\nconst epcValid = !epcExpired;\nconst mepsDeadlines = {\n  G: { deadline: '2030-01-01', description: 'EPC class G must reach F by Jan 2030 (non-residential)' },\n  F: { deadline: '2033-01-01', description: 'EPC class F must reach E by Jan 2033 (non-residential)' },\n  E: { deadline: '2040-01-01', description: 'EPC class E must reach D by Jan 2040 (non-residential)' }\n};\nconst mepsDeadline = mepsDeadlines[epcClass];\nconst daysToMeps = mepsDeadline ?\n  Math.floor((new Date(mepsDeadline.deadline) - today) / 86400000) : null;\nconst requiresEpc = ['PROPERTY_SALE', 'NEW_RENTAL', 'PROPERTY_LISTED', 'LEASE_RENEWAL'].includes(eventType);\nconst alerts = [];\nif (requiresEpc && !epcValid) alerts.push({ type: 'EPC_REQUIRED_MISSING', severity: 'CRITICAL', message: `${eventType} event \u2014 EPC required but expired or missing` });\nif (requiresEpc && epcValid && epcExpiry) {\n  const daysToExpiry = Math.floor((epcExpiry - today) / 86400000);\n  if (daysToExpiry < 30) alerts.push({ type: 'EPC_EXPIRING_SOON', severity: 'WARNING', message: `EPC expires in ${daysToExpiry} days \u2014 renew before next transaction` });\n}\nif (mepsDeadline && daysToMeps !== null && daysToMeps < 365 * 3) alerts.push({ type: 'MEPS_UPGRADE_DUE', severity: daysToMeps < 365 ? 'CRITICAL' : 'WARNING', message: mepsDeadline.description, days_remaining: daysToMeps });\nreturn [{ json: { ...evt, epc_valid: epcValid, epc_class: epcClass, requires_epc: requiresEpc, meps_deadline: mepsDeadline, days_to_meps: daysToMeps, alerts } }];\n"
      }
    },
    {
      "id": "n3",
      "name": "Has Alerts?",
      "type": "n8n-nodes-base.if",
      "parameters": {
        "conditions": {
          "number": [
            {
              "value1": "={{$json.alerts.length}}",
              "operation": "larger",
              "value2": 0
            }
          ]
        }
      }
    },
    {
      "id": "n4",
      "name": "Notify Compliance Team",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "toList": "compliance@yourplatform.com",
        "subject": "EU EPBD Alert: {{ $json.property_id }} \u2014 {{ $json.alerts[0].type }}",
        "message": "Property: {{ $json.property_address }}\nEvent: {{ $json.event_type }}\nEPC Class: {{ $json.epc_class }} | Valid: {{ $json.epc_valid }}\nAlerts: {{ $json.alerts | json }}\n\nRef: EU EPBD 2024 (Directive 2024/1275/EU) Art.19 \u2014 EPC required at point of sale/rental"
      }
    },
    {
      "id": "n5",
      "name": "Log to Postgres",
      "type": "n8n-nodes-base.postgres",
      "parameters": {
        "operation": "insert",
        "table": "epbd_events",
        "columns": "property_id,event_type,epc_class,epc_valid,alerts_json,created_at",
        "values": "={{ $json.property_id }},={{ $json.event_type }},={{ $json.epc_class }},={{ $json.epc_valid }},={{ JSON.stringify($json.alerts) }},NOW()"
      }
    }
  ],
  "connections": {
    "Webhook: Property Event": {
      "main": [
        [
          {
            "node": "Classify Event Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Event Type": {
      "main": [
        [
          {
            "node": "Has Alerts?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Alerts?": {
      "main": [
        [
          {
            "node": "Notify Compliance Team",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log to Postgres",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 3: ENERGY STAR Portfolio Manager Benchmarking Pipeline

Why this workflow exists: ENERGY STAR scores are recertified annually. A score drop from 75.0 to 74.9 = certification lost. LEED EBOM compliance chains break for tenants. Many US benchmarking ordinances require submission by May 1 (same as NYC LL97). One API error in your automated submission = a manual catch for 100+ buildings.

{
  "name": "ENERGY STAR Portfolio Manager Benchmarking Pipeline",
  "nodes": [
    {
      "id": "n1",
      "name": "Weekly Trigger Monday 7AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "weeksInterval": 1,
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 7
            }
          ]
        }
      }
    },
    {
      "id": "n2",
      "name": "Fetch Building Scores",
      "type": "n8n-nodes-base.postgres",
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT building_id, name, energy_star_score, energy_star_score_prev, energy_star_certified, certification_expiry_date, benchmarking_jurisdiction, benchmarking_deadline, last_submission_date, submission_status FROM buildings WHERE energy_star_benchmarking_required = TRUE AND active = TRUE"
      }
    },
    {
      "id": "n3",
      "name": "Analyze Score Changes",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "\nconst buildings = $input.all();\nconst alerts = [];\nfor (const b of buildings) {\n  const d = b.json;\n  const score = parseFloat(d.energy_star_score);\n  const prevScore = parseFloat(d.energy_star_score_prev);\n  const certified = d.energy_star_certified;\n  const expiryDate = d.certification_expiry_date ? new Date(d.certification_expiry_date) : null;\n  const today = new Date();\n  const daysToExpiry = expiryDate ? Math.floor((expiryDate - today) / 86400000) : null;\n  const deadlineDate = d.benchmarking_deadline ? new Date(d.benchmarking_deadline) : null;\n  const daysToDeadline = deadlineDate ? Math.floor((deadlineDate - today) / 86400000) : null;\n  if (certified && score < 75) {\n    alerts.push({ ...d, alert_type: 'CERTIFICATION_LOST', severity: 'CRITICAL',\n      message: `Score dropped to ${score} \u2014 ENERGY STAR certification lost (was ${prevScore})` });\n  } else if (!certified && score >= 75) {\n    alerts.push({ ...d, alert_type: 'CERTIFICATION_ELIGIBLE', severity: 'INFO',\n      message: `Score ${score} \u2014 now eligible for ENERGY STAR certification` });\n  } else if (prevScore && Math.abs(score - prevScore) > 5) {\n    alerts.push({ ...d, alert_type: 'SCORE_SIGNIFICANT_CHANGE', severity: 'WARNING',\n      message: `Score changed ${prevScore} \u2192 ${score} (${score > prevScore ? '+' : ''}${(score - prevScore).toFixed(1)} pts)` });\n  }\n  if (daysToExpiry !== null && daysToExpiry < 60) {\n    alerts.push({ ...d, alert_type: 'CERTIFICATION_EXPIRING', severity: daysToExpiry < 30 ? 'CRITICAL' : 'WARNING',\n      message: `ENERGY STAR certification expires in ${daysToExpiry} days` });\n  }\n  if (daysToDeadline !== null && daysToDeadline < 45 && d.submission_status !== 'SUBMITTED') {\n    alerts.push({ ...d, alert_type: 'BENCHMARKING_DEADLINE_APPROACHING', severity: daysToDeadline < 14 ? 'CRITICAL' : 'WARNING',\n      message: `${d.benchmarking_jurisdiction} benchmarking deadline in ${daysToDeadline} days \u2014 status: ${d.submission_status}` });\n  }\n}\nreturn alerts.map(a => ({ json: a }));\n"
      }
    },
    {
      "id": "n4",
      "name": "Slack Energy Team",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#energy-star-alerts",
        "text": "{{ $json.severity }}: {{ $json.name }}\n{{ $json.alert_type }}: {{ $json.message }}\nJurisdiction: {{ $json.benchmarking_jurisdiction }} | Deadline: {{ $json.benchmarking_deadline }}"
      }
    },
    {
      "id": "n5",
      "name": "Log Alert",
      "type": "n8n-nodes-base.postgres",
      "parameters": {
        "operation": "insert",
        "table": "energy_star_alerts",
        "columns": "building_id,alert_type,severity,message,energy_star_score,created_at",
        "values": "={{ $json.building_id }},={{ $json.alert_type }},={{ $json.severity }},={{ $json.message }},={{ $json.energy_star_score }},NOW()"
      }
    }
  ],
  "connections": {
    "Weekly Trigger Monday 7AM": {
      "main": [
        [
          {
            "node": "Fetch Building Scores",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Building Scores": {
      "main": [
        [
          {
            "node": "Analyze Score Changes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze Score Changes": {
      "main": [
        [
          {
            "node": "Slack Energy Team",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack Energy Team": {
      "main": [
        [
          {
            "node": "Log Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 4: ISO 50001 / EU Taxonomy Compliance Incident Pipeline

Why this workflow exists: ISO 50001 internal audit gaps found by certifying bodies = certification suspended. EU Taxonomy Art.10 green bond covenants may trigger when certification lapses. The incident pipeline catches the event at source (SEU audit miss, consumption anomaly, corrective action overdue) before it reaches the certifying body.

{
  "name": "ISO 50001 / EU Taxonomy Compliance Incident Pipeline",
  "nodes": [
    {
      "id": "n1",
      "name": "Webhook: Compliance Event",
      "type": "n8n-nodes-base.webhook",
      "parameters": {
        "path": "iso50001-incident",
        "httpMethod": "POST"
      }
    },
    {
      "id": "n2",
      "name": "Route Incident Type",
      "type": "n8n-nodes-base.switch",
      "parameters": {
        "dataPropertyName": "incident_type",
        "rules": {
          "rules": [
            {
              "value": "ISO50001_INTERNAL_AUDIT_MISSED"
            },
            {
              "value": "ISO50001_SEU_CORRECTIVE_ACTION_OVERDUE"
            },
            {
              "value": "ISO50001_ENERGY_BASELINE_DEVIATION"
            },
            {
              "value": "EU_TAXONOMY_ART10_ASSESSMENT_OVERDUE"
            },
            {
              "value": "EU_TAXONOMY_NZEB_THRESHOLD_BREACH"
            },
            {
              "value": "WELL_CERTIFICATION_EXPIRING"
            },
            {
              "value": "ASHRAE_COMMISSIONING_DOC_MISSING"
            },
            {
              "value": "CA_TITLE24_PERMIT_COMPLIANCE_GAP"
            }
          ]
        }
      }
    },
    {
      "id": "n3",
      "name": "ISO Audit: Escalate",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "toList": "iso-coordinator@yourplatform.com",
        "subject": "ISO 50001 AUDIT MISSED \u2014 {{ $json.facility_name }} \u2014 Certification Risk",
        "message": "Facility: {{ $json.facility_name }}\nAudit Due: {{ $json.audit_due_date }}\nDays Overdue: {{ $json.days_overdue }}\n\nISO 50001:2018 Clause 9.2 \u2014 Internal audit must be conducted at planned intervals.\nRisk: Certifying body annual surveillance visit will find gap \u2192 suspension of EnMS certificate.\nEU Taxonomy Art.10: If ISO 50001 is the basis for climate mitigation classification, suspension triggers covenant review.\n\nRequired action: Schedule audit within 5 business days. Document corrective action."
      }
    },
    {
      "id": "n4",
      "name": "EU Taxonomy: Flag Investment",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#eu-taxonomy-compliance",
        "text": "EU TAXONOMY BREACH: {{ $json.building_name }}\nType: {{ $json.incident_type }}\nIssue: {{ $json.description }}\nInvestors affected: {{ $json.investor_count }}\n\nRef: EU Taxonomy Regulation 2020/852 Art.10 \u2014 Do No Significant Harm criteria. Notify ESG reporting team immediately."
      }
    },
    {
      "id": "n5",
      "name": "Log All Incidents",
      "type": "n8n-nodes-base.postgres",
      "parameters": {
        "operation": "insert",
        "table": "compliance_incidents",
        "columns": "facility_id,incident_type,severity,description,regulation_ref,reported_at,status",
        "values": "={{ $json.facility_id }},={{ $json.incident_type }},={{ $json.severity }},={{ $json.description }},={{ $json.regulation_ref }},NOW(),'OPEN'"
      }
    }
  ],
  "connections": {
    "Webhook: Compliance Event": {
      "main": [
        [
          {
            "node": "Route Incident Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route Incident Type": {
      "main": [
        [
          {
            "node": "ISO Audit: Escalate",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "ISO Audit: Escalate",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log All Incidents",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "EU Taxonomy: Flag Investment",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "EU Taxonomy: Flag Investment",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log All Incidents",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log All Incidents",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log All Incidents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ISO Audit: Escalate": {
      "main": [
        [
          {
            "node": "Log All Incidents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "EU Taxonomy: Flag Investment": {
      "main": [
        [
          {
            "node": "Log All Incidents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 5: Weekly SmartBuilding Platform KPI Dashboard

Why this workflow exists: Executive teams need a single Monday morning view: portfolio carbon exposure, ENERGY STAR score distribution, LL97 penalty trajectory, EU EPBD upgrade backlog, and revenue metrics — all in one email. Stop building this in Google Sheets.

{
  "name": "Weekly SmartBuilding Platform KPI Dashboard",
  "nodes": [
    {
      "id": "n1",
      "name": "Monday 7AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "weeksInterval": 1,
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 7
            }
          ]
        }
      }
    },
    {
      "id": "n2",
      "name": "Query Platform KPIs",
      "type": "n8n-nodes-base.postgres",
      "parameters": {
        "operation": "executeQuery",
        "query": "\nSELECT\n  COUNT(DISTINCT customer_id) FILTER (WHERE tier = 'ENTERPRISE_BMS_SAAS') AS enterprise_customers,\n  COUNT(DISTINCT customer_id) FILTER (WHERE tier = 'COMMERCIAL_RE_ENERGY_SAAS') AS commercial_re_customers,\n  COUNT(DISTINCT customer_id) FILTER (WHERE tier = 'SMARTBUILDING_STARTUP_SAAS') AS startup_customers,\n  COUNT(DISTINCT customer_id) FILTER (WHERE created_at >= NOW() - INTERVAL '7 days') AS new_customers_7d,\n  COUNT(DISTINCT customer_id) FILTER (WHERE created_at >= NOW() - INTERVAL '14 days' AND created_at < NOW() - INTERVAL '7 days') AS new_customers_prior_7d,\n  SUM(mrr_usd) AS total_mrr,\n  SUM(mrr_usd) FILTER (WHERE created_at >= NOW() - INTERVAL '7 days') AS new_mrr_7d,\n  COUNT(DISTINCT b.building_id) AS total_buildings_monitored,\n  AVG(b.energy_star_score) AS avg_energy_star_score,\n  COUNT(*) FILTER (WHERE b.energy_star_score < 75 AND b.energy_star_certified = TRUE) AS certification_at_risk,\n  SUM(b.ll97_projected_penalty_usd) AS total_ll97_exposure_usd,\n  COUNT(*) FILTER (WHERE b.nyc_ll97_subject = TRUE AND b.ll97_projected_penalty_usd > 0) AS buildings_over_cap,\n  COUNT(*) FILTER (WHERE b.epc_class IN ('G','F','E') AND b.eu_epbd_subject = TRUE) AS buildings_needing_meps_upgrade,\n  COUNT(*) FILTER (WHERE b.iso50001_certified = TRUE) AS iso50001_certified_buildings,\n  SUM(b.gross_sq_ft) AS total_portfolio_sqft\nFROM customers c\nJOIN buildings b ON c.customer_id = b.customer_id\nWHERE c.active = TRUE\n"
      }
    },
    {
      "id": "n3",
      "name": "Build KPI Email",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "\nconst d = $json;\nconst wowNew = d.new_customers_prior_7d > 0 ?\n  (((d.new_customers_7d - d.new_customers_prior_7d) / d.new_customers_prior_7d) * 100).toFixed(1) : 'N/A';\nconst flags = [];\nif (d.certification_at_risk > 0) flags.push(`\u26a0 ${d.certification_at_risk} buildings: ENERGY STAR certification at risk (score <75)`);\nif (d.buildings_over_cap > 0) flags.push(`\u26a0 ${d.buildings_over_cap} buildings over NYC LL97 cap \u2014 total exposure $${Number(d.total_ll97_exposure_usd || 0).toLocaleString()}`);\nif (d.buildings_needing_meps_upgrade > 0) flags.push(`\u26a0 ${d.buildings_needing_meps_upgrade} EU buildings need MEPS upgrade before 2030`);\nconst html = [\n  '<h2>SmartBuilding Platform \u2014 Weekly KPI</h2>',\n  '<table border=\"1\" cellpadding=\"6\" style=\"border-collapse:collapse;font-family:monospace\">',\n  '<tr><th>Metric</th><th>This Week</th></tr>',\n  `<tr><td>Enterprise BMS customers</td><td>${d.enterprise_customers}</td></tr>`,\n  `<tr><td>Commercial RE customers</td><td>${d.commercial_re_customers}</td></tr>`,\n  `<tr><td>Startup customers</td><td>${d.startup_customers}</td></tr>`,\n  `<tr><td>New customers (7d)</td><td>${d.new_customers_7d} (${wowNew}% WoW)</td></tr>`,\n  `<tr><td>Total MRR</td><td>$${Number(d.total_mrr || 0).toLocaleString()}</td></tr>`,\n  `<tr><td>New MRR (7d)</td><td>$${Number(d.new_mrr_7d || 0).toLocaleString()}</td></tr>`,\n  `<tr><td>Buildings monitored</td><td>${Number(d.total_buildings_monitored || 0).toLocaleString()}</td></tr>`,\n  `<tr><td>Portfolio sq ft</td><td>${Number(d.total_portfolio_sqft || 0).toLocaleString()}</td></tr>`,\n  `<tr><td>Avg ENERGY STAR score</td><td>${parseFloat(d.avg_energy_star_score || 0).toFixed(1)}</td></tr>`,\n  `<tr><td>ISO 50001 certified buildings</td><td>${d.iso50001_certified_buildings}</td></tr>`,\n  `<tr><td>NYC LL97 exposure</td><td>$${Number(d.total_ll97_exposure_usd || 0).toLocaleString()}</td></tr>`,\n  `<tr><td>EU MEPS upgrades needed</td><td>${d.buildings_needing_meps_upgrade}</td></tr>`,\n  '</table>',\n  flags.length ? '<h3>Flags</h3><ul>' + flags.map(f => `<li>${f}</li>`).join('') + '</ul>' : '',\n  '<p style=\"font-size:11px;color:#666\">SmartBuilding Platform KPI \u2014 generated by n8n. Ref: NYC LL97 (Local Law 97 of 2019), EU EPBD 2024 (Directive 2024/1275/EU), ENERGY STAR Portfolio Manager, ISO 50001:2018, EU Taxonomy Regulation 2020/852.</p>'\n].join('\\n');\nreturn [{ json: { html, flags_count: flags.length, ...d } }];\n"
      }
    },
    {
      "id": "n4",
      "name": "Email CEO",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "toList": "ceo@yourplatform.com",
        "bccList": "energy-director@yourplatform.com",
        "subject": "SmartBuilding KPI \u2014 Week of {{ $now.format('MMM DD') }} | MRR ${{ $json.total_mrr }} | LL97 Exposure ${{ $json.total_ll97_exposure_usd }}",
        "message": "={{ $json.html }}",
        "options": {
          "bodyType": "html"
        }
      }
    }
  ],
  "connections": {
    "Monday 7AM": {
      "main": [
        [
          {
            "node": "Query Platform KPIs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Query Platform KPIs": {
      "main": [
        [
          {
            "node": "Build KPI Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build KPI Email": {
      "main": [
        [
          {
            "node": "Email CEO",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The Self-Hosting Argument for Each Tier

Tier What's in the automation logs Why it's sensitive
ENTERPRISE_BMS_SAAS Monthly utility consumption per building, LL97 cap headroom, penalty projections Competitor intelligence — which buildings are over-cap before May 1
COMMERCIAL_RE_ENERGY_SAAS Tenant floor-by-floor consumption attribution Commercial tenant data, lease negotiation leverage
PROPTECH_ENERGY_MGMT_SAAS EU Taxonomy Art.10 classification evidence Green bond compliance — investor-sensitive before public disclosure
GREEN_BUILDING_CERTIFICATION_SAAS WELL/LEED certification scores and gaps Tenant covenant risk before remediation
HVAC_CONTROLS_SAAS Equipment performance baselines and commissioning gaps ASHRAE 90.1 permit-risk data

NYC LL97 annual reports are public record (filed with NYC Buildings). But the granular monthly consumption data, equipment schedules, and overage trajectory projections running through your BMS SaaS automation — that's commercial intelligence your enterprise clients don't want shared with a cloud iPaaS vendor's support staff.


Deadline Reference

Regulation Deadline Type Clock Trigger Consequence
NYC LL97 Annual filing May 1 each year $268/tonne CO2e above cap
NYC LL97 Penalty period Calendar year end (Dec 31) Retroactive — trajectory locked Jan 1
EU EPBD EPC required Property transaction / new rental Transaction may be void without valid EPC
EU EPBD MEPS EPC class G→F 2030-01-01 (non-residential) Non-compliance = renovation order
EU EPBD MEPS EPC class F→E 2033-01-01 (non-residential) Progressive tightening
ENERGY STAR Annual benchmarking Varies by jurisdiction (NYC = May 1) City fine, public scorecard
ENERGY STAR Certification Annual recertification Score <75 = certification lost
ISO 50001 Internal audit Annual — planned intervals (Clause 9.2) Certifying body finds gap = suspension
EU Taxonomy Art.10 Assessment Annual (tied to CSRD reporting) Green bond covenant trigger
WELL v2 Recertification Every 3 years Tenant covenant risk
CA Title 24 Compliance Permit-based Certificate of Occupancy withheld

All 5 workflows are available as part of the FlowKit n8n Template Bundle — 14 compliance-grade automation templates for SaaS vendors: stripeai.gumroad.com

If you're deploying these, the self-hosted n8n angle matters: building consumption data, EPC certificates, and ISO 50001 audit trails should run on infrastructure you control.

Top comments (0)