DEV Community

Alex Kane
Alex Kane

Posted on • Originally published at stripeai.gumroad.com

n8n for ClimateRisk & Physical Risk Analytics SaaS Vendors: 5 Automations for TCFD, SEC Rule 33-11275, EU Taxonomy, and TNFD Compliance

If you're building physical risk analytics software — climate scenario modeling, TCFD reporting tools, catastrophe exposure platforms, or nature-related risk products — you already know the data problem.

TCFD wants scenario analysis documented and reproducible. SEC Rule 33-11275 wants climate-related disclosures that are material and accurate. The EU Taxonomy wants climate adaptation activity evidence chains. TNFD wants LEAP framework compliance.

Every time your customer's physical risk data flows through a cloud iPaaS — Zapier, Make, even Workato — you're creating a chain-of-custody problem that TCFD auditors, SEC examiners, and EU taxonomy supervisors will ask about.

The TCFD audit trail question: "Show me every transformation applied to your physical risk scenario data from ingestion to disclosure." If the answer includes "it passed through Zapier's cloud," the auditor writes a finding.

Self-hosted n8n solves this: every workflow transformation is a git commit, every run is logged in your infrastructure, every data flow stays inside your compliance boundary.

Here are 5 production-ready workflows for ClimateRisk and Physical Risk Analytics SaaS vendors.


Workflow 1: Customer Onboarding Drip — 7-Tier Segmented with Compliance Flags

ClimateRisk SaaS customers range from insurance underwriters who need Swiss Re sigma calibration to municipal climate planners who need FEMA NFIP flood zone integration. One generic onboarding email fails all of them.

This workflow segments across 7 customer tiers and injects relevant compliance framing on Day 0:

Tier Day 3 Onboarding Hook
INSURANCE_UNDERWRITING_SAAS FEMA flood zone ingestion + Swiss Re sigma calibration
REAL_ESTATE_CLIMATE_RISK_SAAS FEMA FIRM integration + IPCC AR6 sea-level pathways
FINANCIAL_PORTFOLIO_RISK_SAAS NGFS scenario ingestion + SEC Rule 33-11275 disclosure workflow
INFRASTRUCTURE_RESILIENCE_SAAS TNFD LEAP framework + FEMA hazard layer API
MUNICIPAL_CLIMATE_ADAPTATION_SAAS NOAA NWS API + FEMA NFIP Community Rating System
SUPPLY_CHAIN_PHYSICAL_RISK_SAAS TCFD value-chain scenario analysis
CLIMATERISK_STARTUP_SAAS EU Taxonomy Art.10 climate adaptation documentation

Import-ready workflow JSON:

{
  "name": "ClimateRisk SaaS \u2014 Customer Onboarding Drip",
  "nodes": [
    {
      "id": "1",
      "name": "Sheets Trigger \u2014 New Customer",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "parameters": {
        "sheetId": "{{YOUR_SHEET_ID}}",
        "range": "customers!A:Z",
        "event": "rowAdded"
      },
      "position": [
        240,
        300
      ]
    },
    {
      "id": "2",
      "name": "Code \u2014 Segment & Flags",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "const r=$input.first().json;const tier=r.customer_tier||'CLIMATERISK_STARTUP_SAAS';const flags={TCFD_PHYSICAL_RISK_REQUIRED:tier==='INSURANCE_UNDERWRITING_SAAS'||tier==='FINANCIAL_PORTFOLIO_RISK_SAAS',SEC_33_11275_APPLICABLE:tier==='FINANCIAL_PORTFOLIO_RISK_SAAS'||tier==='REAL_ESTATE_CLIMATE_RISK_SAAS',EU_TAXONOMY_CLIMATE_ADAPTATION:r.region==='EU',TNFD_LEAP_APPLICABLE:tier==='FINANCIAL_PORTFOLIO_RISK_SAAS'||tier==='INFRASTRUCTURE_RESILIENCE_SAAS',FEMA_NFIP_INTEGRATED:tier==='REAL_ESTATE_CLIMATE_RISK_SAAS'||tier==='MUNICIPAL_CLIMATE_ADAPTATION_SAAS'};const complianceNote=Object.entries(flags).filter(([,v])=>v).map(([k])=>k).join(', ')||'Standard';const msgs={INSURANCE_UNDERWRITING_SAAS:'Your TCFD physical risk model feeds directly into your loss ratio calculations \u2014 Day 3 we walk through FEMA flood zone ingestion and Swiss Re sigma calibration.',REAL_ESTATE_CLIMATE_RISK_SAAS:'Your physical risk scores become NAR disclosure inputs and lender underwriting data \u2014 Day 3 we walk through FEMA FIRM integration and IPCC AR6 sea-level pathways.',FINANCIAL_PORTFOLIO_RISK_SAAS:'Your TCFD portfolio alignment reports go to the SEC and the board \u2014 Day 3 we walk through NGFS scenario ingestion and SEC Rule 33-11275 disclosure workflow.',INFRASTRUCTURE_RESILIENCE_SAAS:'Your physical risk layers inform utility rate cases and bond covenants \u2014 Day 3 we walk through TNFD LEAP framework and FEMA hazard layer API.',MUNICIPAL_CLIMATE_ADAPTATION_SAAS:'Your risk assessments feed state HAZMAT plans and FEMA BRIC grants \u2014 Day 3 we walk through NOAA NWS API and FEMA NFIP Community Rating System.',SUPPLY_CHAIN_PHYSICAL_RISK_SAAS:'Your exposure scores feed SEC material event disclosures and supplier ESG audits \u2014 Day 3 we walk through TCFD value-chain scenario analysis.',CLIMATERISK_STARTUP_SAAS:'Your platform is at the center of the fastest-growing ESG data category \u2014 Day 3 we walk through TCFD alignment and EU Taxonomy Art.10 documentation.'};return [{json:{...r,tier,flags,complianceNote,tierMsg:msgs[tier]||msgs.CLIMATERISK_STARTUP_SAAS}}];"
      },
      "position": [
        460,
        300
      ]
    },
    {
      "id": "3",
      "name": "Gmail \u2014 Day 0 Welcome",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "to": "={{$json.email}}",
        "subject": "Welcome to {{YOUR_PLATFORM}} \u2014 your physical risk data environment is ready",
        "message": "Hi {{$json.name}},\n\nYour account is live. {{$json.tierMsg}}\n\nCompliance scope: {{$json.complianceNote}}\n\nDay 3 we schedule your onboarding call.\n\nTeam FlowKit"
      },
      "position": [
        680,
        200
      ]
    },
    {
      "id": "4",
      "name": "Sheets \u2014 Log Onboarding",
      "type": "n8n-nodes-base.googleSheets",
      "parameters": {
        "operation": "append",
        "sheetId": "{{ONBOARDING_LOG_SHEET}}",
        "data": {
          "customer_id": "={{$json.customer_id}}",
          "tier": "={{$json.tier}}",
          "compliance_scope": "={{$json.complianceNote}}",
          "started": "={{new Date().toISOString()}}"
        }
      },
      "position": [
        680,
        380
      ]
    },
    {
      "id": "5",
      "name": "Wait \u2014 3 Days",
      "type": "n8n-nodes-base.wait",
      "parameters": {
        "resume": "timeInterval",
        "amount": 3,
        "unit": "days"
      },
      "position": [
        900,
        300
      ]
    },
    {
      "id": "6",
      "name": "Gmail \u2014 Day 3 Setup Call",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "to": "={{$json.email}}",
        "subject": "Your Day 3 onboarding call \u2014 {{YOUR_PLATFORM}}",
        "message": "Hi {{$json.name}},\n\nTime to connect your first physical risk data feeds.\n\nBook your 30-min setup call: {{YOUR_CALENDLY_LINK}}\n\nTeam FlowKit"
      },
      "position": [
        1120,
        300
      ]
    },
    {
      "id": "7",
      "name": "Wait \u2014 4 Days",
      "type": "n8n-nodes-base.wait",
      "parameters": {
        "resume": "timeInterval",
        "amount": 4,
        "unit": "days"
      },
      "position": [
        1340,
        300
      ]
    },
    {
      "id": "8",
      "name": "Gmail \u2014 Day 7 First Report",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "to": "={{$json.email}}",
        "subject": "Your first TCFD physical risk summary is ready",
        "message": "Hi {{$json.name}},\n\nYour platform has been ingesting physical risk data for 7 days.\n\nLog in to view your first TCFD scenario report: {{YOUR_APP_URL}}/reports/tcfd\n\nTeam FlowKit"
      },
      "position": [
        1560,
        300
      ]
    }
  ],
  "connections": {
    "Sheets Trigger \u2014 New Customer": {
      "main": [
        [
          {
            "node": "Code \u2014 Segment & Flags",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 Segment & Flags": {
      "main": [
        [
          {
            "node": "Gmail \u2014 Day 0 Welcome",
            "type": "main",
            "index": 0
          },
          {
            "node": "Sheets \u2014 Log Onboarding",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail \u2014 Day 0 Welcome": {
      "main": [
        [
          {
            "node": "Wait \u2014 3 Days",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait \u2014 3 Days": {
      "main": [
        [
          {
            "node": "Gmail \u2014 Day 3 Setup Call",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail \u2014 Day 3 Setup Call": {
      "main": [
        [
          {
            "node": "Wait \u2014 4 Days",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait \u2014 4 Days": {
      "main": [
        [
          {
            "node": "Gmail \u2014 Day 7 First Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 2: Physical Risk Data Feed & Model API Health Monitor

Physical risk analytics depend on live data feeds: NOAA NWS hazard forecasts, FEMA FIRM map updates, SEC EDGAR comparable disclosures, EU Taxonomy activity codes. A 15-minute NOAA feed outage during an active hurricane advisory cycle is a TCFD scenario accuracy gap.

This workflow polls 5 critical API endpoints every 15 minutes with SLA-based deduplication:

API Regulation SLA
tcfd_scenario_api TCFD 2021 §D Physical Risk 30 min
fema_flood_api FEMA NFIP FIRM accuracy 60 min
noaa_hazard_api NOAA NWS hurricane/flood feeds 15 min
sec_edgar_api SEC 33-11275 benchmarking 120 min
eu_taxonomy_api EU Taxonomy Art.10 activity codes 120 min

Import-ready workflow JSON:

{
  "name": "ClimateRisk SaaS \u2014 Physical Risk Data Feed API Monitor",
  "nodes": [
    {
      "id": "1",
      "name": "Schedule \u2014 Every 15 Min",
      "type": "n8n-nodes-base.scheduleTrigger",
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "position": [
        240,
        300
      ]
    },
    {
      "id": "2",
      "name": "Code \u2014 API Endpoints",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "return [{json:{name:'tcfd_scenario_api',url:'{{TCFD_MODEL_ENDPOINT}}/health',regulation:'TCFD 2021 \u00a7D Physical Risk Scenario Analysis',sla_minutes:30,tier:'CRITICAL'}},{json:{name:'fema_flood_api',url:'{{FEMA_NFIP_ENDPOINT}}/status',regulation:'FEMA NFIP FIRM map data feed \u2014 60-min stale = flood zone accuracy gap',sla_minutes:60,tier:'HIGH'}},{json:{name:'noaa_hazard_api',url:'{{NOAA_ENDPOINT}}/health',regulation:'NOAA NWS API \u2014 hurricane/flood forecasts feed TCFD scenario models',sla_minutes:15,tier:'CRITICAL'}},{json:{name:'sec_edgar_api',url:'{{SEC_EDGAR_PROXY}}/status',regulation:'SEC EDGAR API \u2014 33-11275 comparable disclosures benchmarking',sla_minutes:120,tier:'MEDIUM'}},{json:{name:'eu_taxonomy_api',url:'{{EU_TAXONOMY_ENDPOINT}}/health',regulation:'EU Taxonomy 2020/852 Art.10 climate adaptation activity codes',sla_minutes:120,tier:'MEDIUM'}}];"
      },
      "position": [
        460,
        300
      ]
    },
    {
      "id": "3",
      "name": "HTTP \u2014 Ping Each API",
      "type": "n8n-nodes-base.httpRequest",
      "parameters": {
        "url": "={{$json.url}}",
        "method": "GET",
        "timeout": 10000,
        "continueOnFail": true
      },
      "position": [
        680,
        300
      ]
    },
    {
      "id": "4",
      "name": "Code \u2014 Evaluate Status",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "const r=$input.first().json;const status=r.statusCode===200?'OK':'DOWN';const now=new Date();const state=$getWorkflowStaticData('global');const key=r.name+'_last_alert';const lastAlert=state[key]?new Date(state[key]):null;const dedup=lastAlert&&(now-lastAlert)<r.sla_minutes*60*1000;if(status==='DOWN'&&!dedup){state[key]=now.toISOString();return [{json:{...r,status,alert:true,msg:`PHYSICAL RISK DATA FEED DOWN: ${r.name} | Regulation: ${r.regulation} | Tier: ${r.tier}`}}];}return [{json:{...r,status,alert:false}}];"
      },
      "position": [
        900,
        300
      ]
    },
    {
      "id": "5",
      "name": "IF \u2014 Alert Needed",
      "type": "n8n-nodes-base.if",
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{$json.alert}}",
              "value2": true
            }
          ]
        }
      },
      "position": [
        1120,
        300
      ]
    },
    {
      "id": "6",
      "name": "Slack \u2014 Data Feed Alert",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#climate-data-ops",
        "text": "={{$json.msg}}"
      },
      "position": [
        1340,
        200
      ]
    },
    {
      "id": "7",
      "name": "Sheets \u2014 Incident Log",
      "type": "n8n-nodes-base.googleSheets",
      "parameters": {
        "operation": "append",
        "sheetId": "{{INCIDENT_LOG_SHEET}}",
        "data": {
          "api": "={{$json.name}}",
          "status": "={{$json.status}}",
          "regulation": "={{$json.regulation}}",
          "ts": "={{new Date().toISOString()}}"
        }
      },
      "position": [
        1340,
        380
      ]
    }
  ],
  "connections": {
    "Schedule \u2014 Every 15 Min": {
      "main": [
        [
          {
            "node": "Code \u2014 API Endpoints",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 API Endpoints": {
      "main": [
        [
          {
            "node": "HTTP \u2014 Ping Each API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP \u2014 Ping Each API": {
      "main": [
        [
          {
            "node": "Code \u2014 Evaluate Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 Evaluate Status": {
      "main": [
        [
          {
            "node": "IF \u2014 Alert Needed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF \u2014 Alert Needed": {
      "main": [
        [
          {
            "node": "Slack \u2014 Data Feed Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sheets \u2014 Incident Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 3: TCFD / SEC / EU Taxonomy / TNFD Compliance Deadline Tracker

Physical risk analytics vendors face 12 distinct compliance deadlines. The one that catches vendors off guard: SEC Rule 33-11275 is in effect for accelerated filers for FY2025. If your platform doesn't yet generate SEC 33-11275-formatted physical risk disclosures, your customers are discovering that gap during annual report filing — not during onboarding.

Deadline Type Regulation Hard Date
TCFD_ANNUAL_PHYSICAL_RISK_REPORT TCFD 2021 §D.2 With annual report
SEC_33_11275_CLIMATE_DISCLOSURE SEC Final Rule 33-11275 Form 10-K filing date
EU_TAXONOMY_CLIMATE_ADAPTATION_REPORT EU Taxonomy 2020/852 Art.10 Annual
TNFD_ANNUAL_NATURE_RISK_DISCLOSURE TNFD v1.0 LEAP Pillar D Annual
FEMA_NFIP_CRS_ANNUAL_RECERTIFICATION FEMA NFIP CRS §73.7 Annual
EU_SFDR_ARTICLE_9_ANNUAL_REPORT EU SFDR 2019/2088 Art.9 Annual
IPCC_SCENARIO_MODEL_RECALIBRATION TCFD 2021 §D.1 On IPCC AR6 update
SOC2_TYPE2_RENEWAL AICPA SOC 2 CC6 12-month window
ANNUAL_PENTEST SOC 2 CC7.2 Annual
SWISS_RE_SIGMA_DATA_LICENSE_RENEWAL Swiss Re sigma license Annual
NGFS_PORTFOLIO_ALIGNMENT_REVIEW NGFS 2023 Scenarios On NGFS update
GDPR_DPIA_REVIEW GDPR Art.35 Annual

Import-ready workflow JSON:

{
  "name": "ClimateRisk SaaS \u2014 TCFD/SEC/EU Taxonomy/TNFD Compliance Deadline Tracker",
  "nodes": [
    {
      "id": "1",
      "name": "Schedule \u2014 Weekdays 8 AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1-5"
            }
          ]
        }
      },
      "position": [
        240,
        300
      ]
    },
    {
      "id": "2",
      "name": "Sheets \u2014 Load Deadlines",
      "type": "n8n-nodes-base.googleSheets",
      "parameters": {
        "operation": "read",
        "sheetId": "{{COMPLIANCE_DEADLINES_SHEET}}",
        "range": "deadlines!A:G"
      },
      "position": [
        460,
        300
      ]
    },
    {
      "id": "3",
      "name": "Code \u2014 Evaluate Urgency",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "const deadlineTypes={TCFD_ANNUAL_PHYSICAL_RISK_REPORT:{label:'TCFD Annual Physical Risk Scenario Report',regulation:'TCFD 2021 Recommendations \u00a7D.2 \u2014 physical risk disclosure due with annual report'},SEC_33_11275_CLIMATE_DISCLOSURE:{label:'SEC Rule 33-11275 Annual Climate Disclosure',regulation:'SEC Final Rule 33-11275 \u2014 climate-related disclosures in Form 10-K (accelerated filers FY2025)'},EU_TAXONOMY_CLIMATE_ADAPTATION_REPORT:{label:'EU Taxonomy Climate Adaptation KPI Report',regulation:'EU Taxonomy 2020/852 Art.10 \u2014 annual climate adaptation activity reporting'},TNFD_ANNUAL_NATURE_RISK_DISCLOSURE:{label:'TNFD Annual Nature-Related Risk Disclosure',regulation:'TNFD v1.0 LEAP Framework Pillar D \u2014 annual metrics and targets disclosure'},FEMA_NFIP_CRS_ANNUAL_RECERTIFICATION:{label:'FEMA NFIP CRS Annual Recertification',regulation:'FEMA NFIP Community Rating System \u2014 annual recertification to maintain flood insurance discount'},EU_SFDR_ARTICLE_9_ANNUAL_REPORT:{label:'EU SFDR Article 9 Annual Sustainability Report',regulation:'EU SFDR 2019/2088 Art.9 \u2014 annual principal adverse impact report'},IPCC_SCENARIO_MODEL_RECALIBRATION:{label:'IPCC AR6 Scenario Model Annual Recalibration',regulation:'TCFD 2021 \u00a7D.1 \u2014 physical risk scenario analysis must use IPCC AR6 RCP/SSP pathways'},SOC2_TYPE2_RENEWAL:{label:'SOC 2 Type II Annual Audit Renewal',regulation:'AICPA SOC 2 \u2014 12-month continuous evidence window'},ANNUAL_PENTEST:{label:'Annual Penetration Test',regulation:'SOC 2 CC7.2 \u2014 annual pentest of physical risk model APIs'},SWISS_RE_SIGMA_DATA_LICENSE_RENEWAL:{label:'Swiss Re Sigma Data License Renewal',regulation:'Swiss Re sigma NatCat database license \u2014 annual renewal for catastrophe model calibration'},NGFS_PORTFOLIO_ALIGNMENT_REVIEW:{label:'NGFS Climate Scenario Annual Portfolio Alignment Review',regulation:'NGFS 2023 Scenarios \u2014 annual review for TCFD transition/physical pathway consistency'},GDPR_DPIA_REVIEW:{label:'GDPR DPIA Annual Review',regulation:'GDPR Art.35 \u2014 annual Data Protection Impact Assessment for high-risk location + financial risk data processing'}};const rows=$input.all().map(i=>i.json);const now=new Date();const alerts=[];for(const row of rows){if(!row.deadline_date||!row.deadline_type)continue;const deadline=new Date(row.deadline_date);const daysLeft=Math.ceil((deadline-now)/(1000*60*60*24));let urgency=null;if(daysLeft<0)urgency='OVERDUE';else if(daysLeft<=14)urgency='CRITICAL';else if(daysLeft<=30)urgency='URGENT';else if(daysLeft<=60)urgency='WARNING';else if(daysLeft<=90)urgency='NOTICE';if(!urgency)continue;const meta=deadlineTypes[row.deadline_type]||{label:row.deadline_type,regulation:'See compliance sheet'};alerts.push({json:{...row,urgency,daysLeft,...meta,msg:`[${urgency}] ${meta.label} \u2014 ${daysLeft<0?'OVERDUE by '+Math.abs(daysLeft)+' days':daysLeft+' days remaining'} | ${meta.regulation}`}});}return alerts.length?alerts:[{json:{urgency:null,msg:'No compliance deadlines require attention today'}}];"
      },
      "position": [
        680,
        300
      ]
    },
    {
      "id": "4",
      "name": "IF \u2014 Has Alerts",
      "type": "n8n-nodes-base.if",
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json.urgency}}",
              "operation": "isNotEmpty"
            }
          ]
        }
      },
      "position": [
        900,
        300
      ]
    },
    {
      "id": "5",
      "name": "Switch \u2014 Route by Urgency",
      "type": "n8n-nodes-base.switch",
      "parameters": {
        "dataType": "string",
        "value1": "={{$json.urgency}}",
        "rules": {
          "rules": [
            {
              "value2": "OVERDUE"
            },
            {
              "value2": "CRITICAL"
            },
            {
              "value2": "URGENT"
            },
            {
              "value2": "WARNING"
            }
          ]
        }
      },
      "position": [
        1120,
        300
      ]
    },
    {
      "id": "6",
      "name": "Slack \u2014 OVERDUE/CRITICAL",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#compliance-critical",
        "text": "={{$json.msg}}"
      },
      "position": [
        1340,
        160
      ]
    },
    {
      "id": "7",
      "name": "Gmail \u2014 Owner Notification",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "to": "={{$json.owner_email}}",
        "subject": "=[COMPLIANCE {{$json.urgency}}] {{$json.label}} \u2014 action required",
        "message": "={{$json.msg}}"
      },
      "position": [
        1340,
        300
      ]
    },
    {
      "id": "8",
      "name": "Slack \u2014 WARNING/NOTICE",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#compliance-watch",
        "text": "={{$json.msg}}"
      },
      "position": [
        1340,
        440
      ]
    }
  ],
  "connections": {
    "Schedule \u2014 Weekdays 8 AM": {
      "main": [
        [
          {
            "node": "Sheets \u2014 Load Deadlines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets \u2014 Load Deadlines": {
      "main": [
        [
          {
            "node": "Code \u2014 Evaluate Urgency",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 Evaluate Urgency": {
      "main": [
        [
          {
            "node": "IF \u2014 Has Alerts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF \u2014 Has Alerts": {
      "main": [
        [
          {
            "node": "Switch \u2014 Route by Urgency",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Switch \u2014 Route by Urgency": {
      "main": [
        [
          {
            "node": "Slack \u2014 OVERDUE/CRITICAL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Gmail \u2014 Owner Notification",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Gmail \u2014 Owner Notification",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack \u2014 WARNING/NOTICE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 4: Climate Physical Risk Event Alert Pipeline

When a major hurricane makes landfall, a flash flood warning is issued, or a wildfire evacuation zone changes, your customers need to know — with context about which regulatory clocks just started.

This webhook pipeline handles 8 physical risk event types and maps each to its compliance clock:

Event Type Compliance Clock Key Regulation
MAJOR_HURRICANE_TRACK_UPDATE 6h NHC advisory cycle TCFD 2021 §D.1 scenario refresh
FLASH_FLOOD_WARNING_ISSUED 1-6h NWS action window FEMA NFIP CRS §73.7 accuracy
WILDFIRE_EVACUATION_ZONE_CHANGE Immediate / SEC material SEC 33-11275 8-K 4 biz days
EXTREME_HEAT_EVENT_THRESHOLD 72h NOAA forecast TCFD §D.2 chronic risk logging
COASTAL_FLOOD_MODEL_UPDATE 12h NOAA NOS cycle FEMA FIRM reconciliation
SUPPLY_CHAIN_PHYSICAL_DISRUPTION 72h SEC evaluation window SEC 33-11275 material event
INSURANCE_LOSS_EVENT_TRIGGER Immediate sigma classification TCFD §D.1 model response
DATA_BREACH_CLIMATE_MODEL 72h GDPR Art.33 clock GDPR Art.33-34 notification

Import-ready workflow JSON:

{
  "name": "ClimateRisk SaaS \u2014 Physical Risk Event Alert Pipeline",
  "nodes": [
    {
      "id": "1",
      "name": "Webhook \u2014 Receive Risk Event",
      "type": "n8n-nodes-base.webhook",
      "parameters": {
        "path": "climate-risk-event",
        "method": "POST"
      },
      "position": [
        240,
        300
      ]
    },
    {
      "id": "2",
      "name": "Code \u2014 Classify & Route Event",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "const event=$input.first().json;const eventType=event.event_type||'UNKNOWN';const now=new Date();const clockMap={MAJOR_HURRICANE_TRACK_UPDATE:{clock:'6h NHC advisory update cycle',urgency:'CRITICAL',note:'NHC issues advisories every 6h \u2014 TCFD physical risk scenario refresh required before next advisory cycle',regulation:'TCFD 2021 \u00a7D.1 scenario analysis must reflect current NHC track data'},FLASH_FLOOD_WARNING_ISSUED:{clock:'1-6h NWS action window',urgency:'CRITICAL',note:'FEMA NFIP CRS requires flood exposure updates within 6h of NWS flash flood warning for portfolio properties',regulation:'FEMA NFIP \u00a773.7 CRS crediting \u2014 flood map accuracy window'},WILDFIRE_EVACUATION_ZONE_CHANGE:{clock:'Immediate \u2014 Cal OES/state emergency order',urgency:'CRITICAL',note:'Real estate and infrastructure models must reflect evacuation zone changes immediately for SEC material event evaluation',regulation:'SEC 33-11275 \u2014 material physical risk event may require 8-K within 4 business days'},EXTREME_HEAT_EVENT_THRESHOLD:{clock:'72h NOAA forecast window',urgency:'HIGH',note:'Extreme heat crossing IPCC RCP8.5 threshold triggers TCFD scenario boundary condition review',regulation:'TCFD 2021 \u00a7D.2 chronic physical risk \u2014 heat stress threshold exceedance to be logged in scenario documentation'},COASTAL_FLOOD_MODEL_UPDATE:{clock:'12h NOAA NOS tide gauge cycle',urgency:'HIGH',note:'NOAA NOS coastal flood model update requires FEMA FIRM reconciliation for affected portfolio properties',regulation:'FEMA NFIP FIRM accuracy \u2014 coastal flood baseline must track NOS updated models'},SUPPLY_CHAIN_PHYSICAL_DISRUPTION:{clock:'72h SEC material event evaluation window',urgency:'HIGH',note:'Physical disruption to supply chain facility may constitute material physical risk event under SEC 33-11275',regulation:'SEC Rule 33-11275 \u2014 material supply chain disruption; 8-K within 4 business days if material'},INSURANCE_LOSS_EVENT_TRIGGER:{clock:'Immediate Swiss Re sigma classification',urgency:'HIGH',note:'NatCat loss event triggers Swiss Re sigma loss threshold check \u2014 insured exposure recalculation required',regulation:'TCFD 2021 \u00a7D.1 \u2014 insurance underwriters must document physical risk model response to sigma-classified events'},DATA_BREACH_CLIMATE_MODEL:{clock:'72h GDPR Art.33 notification clock',urgency:'CRITICAL',note:'Breach of proprietary physical risk model data or customer geospatial exposure data triggers GDPR 72h clock',regulation:'GDPR Art.33 \u2014 supervisory authority notification within 72h; Art.34 \u2014 customer notification if high risk to rights'}};const meta=clockMap[eventType]||{clock:'Review required',urgency:'MEDIUM',note:'Unclassified physical risk event',regulation:'TCFD 2021 general disclosure requirements'};const deadline=new Date(now.getTime()+(meta.urgency==='CRITICAL'?6*3600000:72*3600000));return [{json:{...event,...meta,eventType,detected_at:now.toISOString(),action_deadline:deadline.toISOString(),slack_msg:`[${meta.urgency}] CLIMATE RISK EVENT: ${eventType} | Clock: ${meta.clock} | ${meta.note} | Regulation: ${meta.regulation} | Action by: ${deadline.toISOString()}`}}];"
      },
      "position": [
        460,
        300
      ]
    },
    {
      "id": "3",
      "name": "Slack \u2014 #climate-risk-ops",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#climate-risk-ops",
        "text": "={{$json.slack_msg}}"
      },
      "position": [
        680,
        200
      ]
    },
    {
      "id": "4",
      "name": "Postgres \u2014 Audit Log",
      "type": "n8n-nodes-base.postgres",
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO climate_risk_events (event_type, urgency, detected_at, action_deadline, regulation, notes) VALUES ($1,$2,$3,$4,$5,$6)",
        "additionalFields": {
          "queryParams": "={{[$json.eventType, $json.urgency, $json.detected_at, $json.action_deadline, $json.regulation, $json.note]}}"
        }
      },
      "position": [
        680,
        380
      ]
    },
    {
      "id": "5",
      "name": "IF \u2014 CRITICAL",
      "type": "n8n-nodes-base.if",
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json.urgency}}",
              "value2": "CRITICAL",
              "operation": "equal"
            }
          ]
        }
      },
      "position": [
        900,
        300
      ]
    },
    {
      "id": "6",
      "name": "Gmail \u2014 Risk Officer Immediate",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "to": "={{$env.CHIEF_RISK_OFFICER_EMAIL}}",
        "cc": "={{$env.CTO_EMAIL}}",
        "subject": "=[CRITICAL] Physical Risk Event Requires Immediate Action: {{$json.eventType}}",
        "message": "={{$json.slack_msg}}\n\nAction deadline: {{$json.action_deadline}}\n\nLog in to review: {{YOUR_APP_URL}}/events/{{$json.event_id}}"
      },
      "position": [
        1120,
        200
      ]
    }
  ],
  "connections": {
    "Webhook \u2014 Receive Risk Event": {
      "main": [
        [
          {
            "node": "Code \u2014 Classify & Route Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 Classify & Route Event": {
      "main": [
        [
          {
            "node": "Slack \u2014 #climate-risk-ops",
            "type": "main",
            "index": 0
          },
          {
            "node": "Postgres \u2014 Audit Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack \u2014 #climate-risk-ops": {
      "main": [
        [
          {
            "node": "IF \u2014 CRITICAL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF \u2014 CRITICAL": {
      "main": [
        [
          {
            "node": "Gmail \u2014 Risk Officer Immediate",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 5: Weekly ClimateRisk Platform KPI Dashboard

Monday 8 AM: your CEO gets a full-stack report — customer counts by tier, MRR, physical risk events processed, TCFD reports auto-triggered, and on-time resolution rate. The Chief Risk Officer is BCC'd.

The TCFD disclosure chain argument is embedded in the footer: every transformation logged, every data flow on-premise.

Import-ready workflow JSON:

{
  "name": "ClimateRisk SaaS \u2014 Weekly Platform KPI Dashboard",
  "nodes": [
    {
      "id": "1",
      "name": "Schedule \u2014 Monday 8 AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1"
            }
          ]
        }
      },
      "position": [
        240,
        300
      ]
    },
    {
      "id": "2",
      "name": "Postgres \u2014 Platform KPIs",
      "type": "n8n-nodes-base.postgres",
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT COUNT(DISTINCT customer_id) AS active_customers, COUNT(DISTINCT customer_id) FILTER (WHERE created_at >= NOW() - INTERVAL '7 days') AS new_this_week, SUM(mrr_usd) AS total_mrr, AVG(tcfd_reports_generated) AS avg_tcfd_reports_per_customer, COUNT(*) FILTER (WHERE tier = 'INSURANCE_UNDERWRITING_SAAS') AS insurance_customers, COUNT(*) FILTER (WHERE tier = 'FINANCIAL_PORTFOLIO_RISK_SAAS') AS portfolio_customers, COUNT(*) FILTER (WHERE tier = 'REAL_ESTATE_CLIMATE_RISK_SAAS') AS realestate_customers FROM customers WHERE status = 'active'"
      },
      "position": [
        460,
        220
      ]
    },
    {
      "id": "3",
      "name": "Postgres \u2014 Risk Events This Week",
      "type": "n8n-nodes-base.postgres",
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT COUNT(*) AS total_risk_events, COUNT(*) FILTER (WHERE urgency = 'CRITICAL') AS critical_events, COUNT(*) FILTER (WHERE tcfd_report_triggered = true) AS tcfd_reports_triggered, COUNT(*) FILTER (WHERE deadline_met = true) AS events_resolved_on_time FROM climate_risk_events WHERE detected_at >= NOW() - INTERVAL '7 days'"
      },
      "position": [
        460,
        420
      ]
    },
    {
      "id": "4",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "parameters": {
        "mode": "mergeByIndex"
      },
      "position": [
        680,
        320
      ]
    },
    {
      "id": "5",
      "name": "Code \u2014 Build HTML Report",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "const d=$input.first().json;const state=$getWorkflowStaticData('global');const lastMrr=state.last_mrr_usd||d.total_mrr;const mrrWoW=lastMrr?(((d.total_mrr-lastMrr)/lastMrr)*100).toFixed(1):'0.0';state.last_mrr_usd=d.total_mrr;const html=`<h2>ClimateRisk Platform \u2014 Weekly KPI Report</h2><table border='1' cellpadding='6' style='border-collapse:collapse'><tr><th>Metric</th><th>This Week</th><th>WoW</th></tr><tr><td>Active Customers</td><td>${d.active_customers}</td><td>+${d.new_this_week} new</td></tr><tr><td>Total MRR</td><td>$${Number(d.total_mrr).toLocaleString()}</td><td>${mrrWoW}%</td></tr><tr><td>Insurance Underwriting Customers</td><td>${d.insurance_customers}</td><td>\u2014</td></tr><tr><td>Financial Portfolio Customers</td><td>${d.portfolio_customers}</td><td>\u2014</td></tr><tr><td>Real Estate Risk Customers</td><td>${d.realestate_customers}</td><td>\u2014</td></tr><tr><td>Avg TCFD Reports/Customer</td><td>${Number(d.avg_tcfd_reports_per_customer).toFixed(1)}</td><td>\u2014</td></tr><tr><td>Physical Risk Events This Week</td><td>${d.total_risk_events}</td><td>${d.critical_events} CRITICAL</td></tr><tr><td>TCFD Reports Auto-Triggered</td><td>${d.tcfd_reports_triggered}</td><td>\u2014</td></tr><tr><td>Events Resolved On Time</td><td>${d.events_resolved_on_time}/${d.total_risk_events}</td><td>\u2014</td></tr></table><p style='color:#666;font-size:12px'>ClimateRisk Platform KPI \u2014 n8n self-hosted. TCFD audit trail: every metric transformation is git-versioned. Routing climate risk data through Zapier/Make creates SEC 33-11275 material information disclosure risk via third-party cloud processing.</p>`;return [{json:{...d,html_report:html,mrrWoW,subject:`ClimateRisk Platform Weekly KPI \u2014 MRR $${Number(d.total_mrr).toLocaleString()} | ${d.active_customers} customers | ${d.total_risk_events} risk events`}}];"
      },
      "position": [
        900,
        320
      ]
    },
    {
      "id": "6",
      "name": "Gmail \u2014 CEO + CRO BCC",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "to": "={{$env.CEO_EMAIL}}",
        "bcc": "={{$env.CRO_EMAIL}}",
        "subject": "={{$json.subject}}",
        "message": "={{$json.html_report}}",
        "options": {
          "bodyContentType": "html"
        }
      },
      "position": [
        1120,
        320
      ]
    }
  ],
  "connections": {
    "Schedule \u2014 Monday 8 AM": {
      "main": [
        [
          {
            "node": "Postgres \u2014 Platform KPIs",
            "type": "main",
            "index": 0
          },
          {
            "node": "Postgres \u2014 Risk Events This Week",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Postgres \u2014 Platform KPIs": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Postgres \u2014 Risk Events This Week": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Code \u2014 Build HTML Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 Build HTML Report": {
      "main": [
        [
          {
            "node": "Gmail \u2014 CEO + CRO BCC",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Why Self-Hosted n8n for Physical Risk Analytics

Risk Cloud iPaaS Self-Hosted n8n
TCFD audit trail Data passes through vendor cloud — chain of custody break Every transformation in your git repo — full TCFD documentation
SEC 33-11275 material information Physical risk data flows through third-party servers Stays in your infrastructure — no third-party disclosure risk
EU Taxonomy climate adaptation Processing in US cloud may fail EU data sovereignty Deploy in EU VPC — GDPR Art.44-46 compliant
TNFD LEAP framework evidence Vendor timestamps not in your LEAP documentation n8n run logs become TNFD evidence artifacts
FEMA NFIP data licensing License restricts redistribution — cloud routes may violate Data never leaves your licensed environment

Get All 5 Workflows

These workflows are available as import-ready JSON at stripeai.gumroad.com.

The full FlowKit bundle (15 workflows + setup guides) is available for $97.


Have questions about adapting these for your physical risk analytics stack? Drop a comment below.

Top comments (0)