DEV Community

Alex Kane
Alex Kane

Posted on

n8n for WaterTech & Environmental SaaS Vendors: 5 Automations for EPA CWA NPDES, CERCLA, SDWA, and RCRA Compliance

Cloud automation platforms sit between environmental data sensors and regulatory reporting systems.
When an EPA deadline fires — a CERCLA reportable quantity release, an NPDES DMR, an SDWA lead action level exceedance — the notification has to reach the right agency in the right window.
When your cloud iPaaS is between the monitoring system and the report, it owns part of that clock.


Why Cloud iPaaS Creates Environmental Compliance Exposure

CERCLA §9603(a): IMMEDIATE notification is non-negotiable. Any release of a hazardous substance at or above its reportable quantity (RQ) requires notification to the National Response Center (1-800-424-8802) immediately. There is no grace period for vendor downtime. A cloud iPaaS outage that delays the NRC call is an independent basis for EPA enforcement.

40 CFR §264.56(d)(1): RCRA Large Quantity Generator — 15 minutes. LQG emergency events require notification to the local emergency coordinator immediately and to the state environmental agency within 15 minutes. A cloud iPaaS in the notification path with a SLA > 15 minutes is architecturally noncompliant.

40 CFR §141.85: SDWA Lead Action Level — 24 hours. When lead sampling exceeds 0.015 mg/L, the primacy agency must be notified within 24 hours. Monitoring data flowing through a cloud iPaaS means the vendor can receive a regulatory subpoena for that data before the primacy agency notification is confirmed.

40 CFR §122.41(l)(4): NPDES DMR — 28 days. Discharge Monitoring Reports must be submitted within 28 days of the monitoring period end. Late DMR = Notice of Violation. A cloud iPaaS holding effluent data is an EPA subpoena target independent of your permit.


The Procurement-Level Question

When an environmental regulator investigates a missed notification or a permit violation, the subpoena goes to every system that touched the relevant data. Your cloud iPaaS vendor receives that subpoena regardless of whether you missed the deadline. Their data retention policy, their ToS log access clauses, their multi-tenant architecture — these are all now in scope.

For WaterTech and Environmental SaaS vendors selling to regulated water utilities and industrial facilities, the self-hosted n8n argument is procurement-level:

  • CERCLA IMMEDIATE notifications require architecture that works when vendor SaaS is unavailable
  • NPDES monitoring data should not reside in a third-party cloud with independent subpoena exposure
  • SDWA lead testing records are regulatory evidence — chain of custody matters
  • RCRA hazardous waste manifests require audit trail integrity your cloud iPaaS cannot guarantee under §264.74

Tier Segmentation for WaterTech SaaS Vendors

Tier Description Fastest Compliance Clock
WATER_UTILITY_DIGITAL_PLATFORM Municipal Water Utility Digital Platform CERCLA_HAZARDOUS_RELEASE: IMMEDIATE (42 USC §9603(a)) + SDWA_LEAD_ACTION_LEVEL: 24H primacy agency notice + NPDES_DMR: 28 DAYS after monitoring period
ENVIRONMENTAL_MONITORING_SAAS Environmental Monitoring & IoT SaaS RCRA_RELEASE_NOTIFY: 15 MINUTES state/local + CERCLA_RQ_RELEASE: IMMEDIATE NRC 1-800-424-8802 + NPDES_EFFLUENT_VIOLATION: 24H TDD
WASTEWATER_TREATMENT_SAAS Industrial Wastewater Treatment SaaS NPDES_SIGNIFICANT_VIOLATION: IMMEDIATE DMR deviation + EPA_TDD_REPORT: 24H telephonic + RCRA_LQG_EMERGENCY: 15 MIN §264.56(d)(1)
DRINKING_WATER_QUALITY_SAAS Drinking Water Quality & SDWA SaaS SDWA_LEAD_ACTION_LEVEL: 24H §141.85 primacy agency + SDWA_MCL_VIOLATION: 30 DAYS public notice Tier 2 + SDWA_ACUTE_VIOLATION: 24H Tier 1 immediate public notice
STORMWATER_COMPLIANCE_SAAS Stormwater & MS4 Permit SaaS MS4_ILLICIT_DISCHARGE: IMMEDIATE investigation + NPDES_CONSTRUCTION_NONCOMPLIANCE: 24H notice to operator + CWA_SPILL_SHEEN: IMMEDIATE NRC §311(b)
ENVIRONMENTAL_CONSULTING_SAAS Environmental Consulting & Multi-Permit Management SaaS CERCLA_MULTI_CLIENT_RELEASE: IMMEDIATE each client NRC report + RCRA_MANIFEST_EXCEPTION: 35 DAYS §263.21 + EPA_AUDIT_RESPONSE: varies by permit
WATERTECH_STARTUP WaterTech & CleanTech Startup NPDES_FIRST_PERMIT: site-specific calendar + SOC2_TYPE2: annual + CERCLA_AWARENESS: IMMEDIATE if any reportable quantity

7 Compliance Flags

  • NPDES_PERMIT_HOLDER
  • SDWA_COVERED_SYSTEM
  • CERCLA_REPORTER
  • RCRA_LARGE_QUANTITY_GENERATOR
  • LEAD_COPPER_RULE_APPLICABLE
  • EPA_EDRMS_REPORTER
  • SOC2_REQUIRED

12 Deadline Types

Deadline Type Clock Authority Notes
CERCLA_HAZARDOUS_RELEASE IMMEDIATE 42 USC §9603(a) Release at/above RQ → NRC 1-800-424-8802 immediately; cloud outage = missed report = EPA enforcement
RCRA_LQG_EMERGENCY_NOTIFY 15 MINUTES 40 CFR §264.56(d)(1) Large Quantity Generator release → immediate local emergency coordinator + 15-min state agency notify
SDWA_LEAD_ACTION_LEVEL 24H 40 CFR §141.85 Lead >0.015 mg/L → primacy agency within 24h; 30-day public notice Tier 2
SDWA_ACUTE_MCL_VIOLATION 24H 40 CFR §141.201(a)(1) Acute maximum contaminant level violation → Tier 1 immediate public notice within 24h
CWA_SPILL_SHEEN IMMEDIATE 40 CFR §110.6 / CWA §311(b) Oil sheen on navigable waters → NRC immediate; cloud data delay = delayed response = civil penalty
NPDES_DMR_SUBMITTAL 28 DAYS 40 CFR §122.41(l)(4) Discharge Monitoring Report due 28 days after monitoring period end; late = NOV (Notice of Violation)
NPDES_SIGNIFICANT_VIOLATION 24H_TELEPHONIC 40 CFR §122.41(l)(6) Any anticipated or actual noncompliance → oral report within 24h + written 5 days
RCRA_BIENNIAL_REPORT MARCH_1_BIENNIAL 40 CFR §262.41 Large Quantity Generator biennial waste report; cloud outage = missed EPA deadline
SDWA_CCR_ANNUAL JULY_1_ANNUAL 40 CFR §141.155 Consumer Confidence Report annual distribution by July 1
EPA_ECRR_QUARTERLY QUARTERLY 40 CFR §122.22 eDMR Electronic submission via NetDMR/myDMR; cloud iPaaS holds monitoring data = subpoena target
SOC2_TYPE2_ANNUAL ANNUAL AICPA TSC SOC2 for water/environmental SaaS customers
SDWA_LEAD_PUBLIC_NOTICE 30 DAYS 40 CFR §141.85(a)(1) Tier 2 public notice within 30 days of lead action level exceedance

Workflow 1: WaterTech SaaS Tier-Segmented Onboarding Drip

Routes new accounts by tier (WATER_UTILITY_DIGITAL_PLATFORM, DRINKING_WATER_QUALITY_SAAS, ENVIRONMENTAL_MONITORING_SAAS, etc.) and delivers a tier-specific compliance brief: NPDES DMR windows, CERCLA immediate reporting requirements, SDWA lead action level timelines.

{
  "name": "WaterTech SaaS Tier-Segmented Onboarding Drip",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "watertech-onboarding",
        "responseMode": "responseNode"
      },
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        100,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json[\"tier\"]}}",
              "operation": "equals",
              "value2": "WATER_UTILITY_DIGITAL_PLATFORM"
            }
          ]
        }
      },
      "name": "Is Water Utility?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        350,
        200
      ]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json[\"tier\"]}}",
              "operation": "equals",
              "value2": "DRINKING_WATER_QUALITY_SAAS"
            }
          ]
        }
      },
      "name": "Is Drinking Water?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        350,
        400
      ]
    },
    {
      "parameters": {
        "fromEmail": "onboarding@flowkit.io",
        "toEmail": "={{$json[\"email\"]}}",
        "subject": "NPDES & CERCLA Compliance Automation \u2014 Your Water Utility Brief",
        "text": "Welcome to FlowKit.\n\nYour NPDES permit requires:\n- Monthly DMR via NetDMR/myDMR (28 days post-period)\n- 24h oral report for any significant deviation (40 CFR \u00a7122.41(l)(6))\n- IMMEDIATE NRC call for any hazardous release at/above RQ (CERCLA \u00a79603(a))\n\nCloud iPaaS outage during a CERCLA event = missed NRC report = EPA enforcement. Your n8n instance runs on your infrastructure \u2014 the report fires even when vendor SaaS is down.\n\nDay 3: NPDES deadline tracker walkthrough.\nDay 7: CERCLA/RCRA emergency notification pipeline."
      },
      "name": "Send Water Utility Brief",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2,
      "position": [
        600,
        100
      ]
    },
    {
      "parameters": {
        "fromEmail": "onboarding@flowkit.io",
        "toEmail": "={{$json[\"email\"]}}",
        "subject": "SDWA Lead Action Level & MCL Violation Response \u2014 Your Compliance Automation Brief",
        "text": "Welcome to FlowKit.\n\nSDWA compliance clocks for drinking water systems:\n- Lead >0.015 mg/L \u2192 primacy agency within 24h (40 CFR \u00a7141.85)\n- Acute MCL violation \u2192 Tier 1 public notice within 24h (40 CFR \u00a7141.201)\n- Consumer Confidence Report \u2192 July 1 annual (40 CFR \u00a7141.155)\n\nCloud iPaaS holding sensor data = EPA subpoena reaches vendor before your compliance team. Self-hosted n8n keeps monitoring data in your infrastructure boundary.\n\nDay 3: SDWA lead/copper rule tracker setup.\nDay 7: Tier 1/Tier 2 public notice pipeline."
      },
      "name": "Send Drinking Water Brief",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2,
      "position": [
        600,
        400
      ]
    },
    {
      "parameters": {
        "fromEmail": "onboarding@flowkit.io",
        "toEmail": "={{$json[\"email\"]}}",
        "subject": "Environmental SaaS Compliance Automation \u2014 Your WaterTech Brief",
        "text": "Welcome to FlowKit.\n\nCore environmental compliance clocks:\n- CERCLA hazardous release \u2192 IMMEDIATE NRC report (42 USC \u00a79603(a))\n- RCRA LQG emergency \u2192 15 minutes state/local notify (40 CFR \u00a7264.56)\n- NPDES DMR \u2192 28 days post-monitoring period\n\nDay 3: NPDES/CERCLA/RCRA deadline tracker.\nDay 7: Incident pipeline setup."
      },
      "name": "Send Default Brief",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2,
      "position": [
        600,
        600
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\"status\": \"ok\", \"tier\": \"{{$json['tier']}}\", \"brief\": \"queued\"}"
      },
      "name": "Respond OK",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        850,
        400
      ]
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Is Water Utility?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Water Utility?": {
      "main": [
        [
          {
            "node": "Send Water Utility Brief",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Is Drinking Water?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Drinking Water?": {
      "main": [
        [
          {
            "node": "Send Drinking Water Brief",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Default Brief",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Water Utility Brief": {
      "main": [
        [
          {
            "node": "Respond OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Drinking Water Brief": {
      "main": [
        [
          {
            "node": "Respond OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Default Brief": {
      "main": [
        [
          {
            "node": "Respond OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 2: EPA NPDES/SDWA/CERCLA/RCRA Compliance Deadline Tracker

Polls your compliance database hourly. Routes IMMEDIATE clocks to Slack PagerDuty-style, 24h clocks to email, all events logged to Google Sheets.

{
  "name": "EPA NPDES/SDWA/CERCLA/RCRA Compliance Deadline Tracker",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "minutesInterval": 1
            }
          ]
        }
      },
      "name": "Every Hour",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        100,
        300
      ]
    },
    {
      "parameters": {
        "url": "https://your-watertech-api.io/api/compliance/deadlines",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "name": "Fetch Compliance Items",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        350,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "number": [
            {
              "value1": "={{$json[\"hours_until_due\"]}}",
              "operation": "smallerEqual",
              "value2": 1
            }
          ]
        }
      },
      "name": "IMMEDIATE (<1h)?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        600,
        200
      ]
    },
    {
      "parameters": {
        "conditions": {
          "number": [
            {
              "value1": "={{$json[\"hours_until_due\"]}}",
              "operation": "smallerEqual",
              "value2": 24
            }
          ]
        }
      },
      "name": "24H Clock?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        600,
        400
      ]
    },
    {
      "parameters": {
        "url": "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "text",
              "value": "=\ud83d\udea8 IMMEDIATE REGULATORY CLOCK: {{$json['type']}} \u2014 {{$json['authority']}} \u2014 CLIENT: {{$json['client_name']}} \u2014 ACTION: {{$json['required_action']}} \u2014 CONTACT: {{$json['nrc_or_agency_contact']}}"
            }
          ]
        }
      },
      "name": "Slack IMMEDIATE Alert",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        850,
        100
      ]
    },
    {
      "parameters": {
        "fromEmail": "compliance@flowkit.io",
        "toEmail": "={{$json[\"compliance_officer_email\"]}}",
        "subject": "=24H REGULATORY DEADLINE: {{$json['type']}} \u2014 {{$json['authority']}}",
        "text": "=24-HOUR COMPLIANCE CLOCK \u2014 {{$json['type']}}\n\nAuthority: {{$json['authority']}}\nClient: {{$json['client_name']}}\nDue: {{$json['due_date']}}\nRequired action: {{$json['required_action']}}\n\nMissed deadline consequence: {{$json['enforcement_risk']}}"
      },
      "name": "Email 24H Alert",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2,
      "position": [
        850,
        400
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "value": "YOUR_SHEETS_DOC_ID"
        },
        "sheetName": "DeadlineLog",
        "columnToMatchOn": "deadline_id",
        "columnsUi": {
          "values": [
            {
              "column": "deadline_id",
              "fieldValue": "={{$json['id']}}"
            },
            {
              "column": "type",
              "fieldValue": "={{$json['type']}}"
            },
            {
              "column": "authority",
              "fieldValue": "={{$json['authority']}}"
            },
            {
              "column": "clock",
              "fieldValue": "={{$json['clock']}}"
            },
            {
              "column": "client",
              "fieldValue": "={{$json['client_name']}}"
            },
            {
              "column": "due_date",
              "fieldValue": "={{$json['due_date']}}"
            },
            {
              "column": "status",
              "fieldValue": "ALERTED"
            },
            {
              "column": "alerted_at",
              "fieldValue": "={{new Date().toISOString()}}"
            }
          ]
        }
      },
      "name": "Log to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        850,
        600
      ]
    }
  ],
  "connections": {
    "Every Hour": {
      "main": [
        [
          {
            "node": "Fetch Compliance Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Compliance Items": {
      "main": [
        [
          {
            "node": "IMMEDIATE (<1h)?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IMMEDIATE (<1h)?": {
      "main": [
        [
          {
            "node": "Slack IMMEDIATE Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "24H Clock?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "24H Clock?": {
      "main": [
        [
          {
            "node": "Email 24H Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log to Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 3: WaterTech IoT Sensor & NPDES Health Monitor

15-minute polling of sensor API endpoints. Compares readings to action levels (lead 0.015 mg/L, NPDES permit limits, DO thresholds). Fires Slack alert with regulatory citation and notification clock when threshold exceeded.

{
  "name": "WaterTech IoT Sensor & NPDES Health Monitor",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "name": "Every 15min",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        100,
        300
      ]
    },
    {
      "parameters": {
        "url": "={{$env['SENSOR_API_BASE_URL']}}/api/v1/readings/current",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "name": "Fetch Sensor Readings",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        350,
        300
      ]
    },
    {
      "parameters": {
        "fieldToSplitOut": "readings",
        "include": "allOtherFields"
      },
      "name": "Split Readings",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        600,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "number": [
            {
              "value1": "={{$json[\"value\"]}}",
              "operation": "largerEqual",
              "value2": "={{$json['action_level']}}"
            }
          ]
        }
      },
      "name": "Exceeds Action Level?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        850,
        300
      ]
    },
    {
      "parameters": {
        "url": "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "text",
              "value": "=\ud83d\udea8 SENSOR ALERT: {{$json['parameter']}} = {{$json['value']}} {{$json['unit']}} at {{$json['location']}} \u2014 EXCEEDS {{$json['action_level']}} {{$json['unit']}} ({{$json['regulatory_authority']}}) \u2014 CLOCK: {{$json['notification_clock']}} \u2014 Compliance officer: {{$json['compliance_officer']}}"
            }
          ]
        }
      },
      "name": "Slack Threshold Alert",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1100,
        200
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "value": "YOUR_SHEETS_DOC_ID"
        },
        "sheetName": "SensorAlerts",
        "columnsUi": {
          "values": [
            {
              "column": "timestamp",
              "fieldValue": "={{new Date().toISOString()}}"
            },
            {
              "column": "parameter",
              "fieldValue": "={{$json['parameter']}}"
            },
            {
              "column": "value",
              "fieldValue": "={{$json['value']}}"
            },
            {
              "column": "action_level",
              "fieldValue": "={{$json['action_level']}}"
            },
            {
              "column": "location",
              "fieldValue": "={{$json['location']}}"
            },
            {
              "column": "authority",
              "fieldValue": "={{$json['regulatory_authority']}}"
            },
            {
              "column": "clock",
              "fieldValue": "={{$json['notification_clock']}}"
            }
          ]
        }
      },
      "name": "Log Alert",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        1100,
        400
      ]
    }
  ],
  "connections": {
    "Every 15min": {
      "main": [
        [
          {
            "node": "Fetch Sensor Readings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Sensor Readings": {
      "main": [
        [
          {
            "node": "Split Readings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Readings": {
      "main": [
        [
          {
            "node": "Exceeds Action Level?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Exceeds Action Level?": {
      "main": [
        [
          {
            "node": "Slack Threshold Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 4: Environmental Compliance Incident Pipeline

Webhook-triggered incident pipeline. Routes CERCLA releases to NRC notification workflow, RCRA LQG emergencies to 15-minute state/local alert, SDWA violations to primacy agency email notification.

{
  "name": "Environmental Compliance Incident Pipeline",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "env-incident",
        "responseMode": "responseNode"
      },
      "name": "Incident Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        100,
        400
      ]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json[\"incident_type\"]}}",
              "operation": "equals",
              "value2": "CERCLA_HAZARDOUS_RELEASE"
            }
          ]
        }
      },
      "name": "CERCLA Release?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        350,
        200
      ]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json[\"incident_type\"]}}",
              "operation": "equals",
              "value2": "RCRA_LQG_EMERGENCY"
            }
          ]
        }
      },
      "name": "RCRA Emergency?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        350,
        450
      ]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json[\"incident_type\"]}}",
              "operation": "contains",
              "value2": "SDWA"
            }
          ]
        }
      },
      "name": "SDWA Violation?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        350,
        700
      ]
    },
    {
      "parameters": {
        "url": "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "text",
              "value": "=\ud83d\udea8 CERCLA IMMEDIATE \u2014 Call NRC NOW: 1-800-424-8802\nSubstance: {{$json['substance']}} | Quantity released: {{$json['quantity']}} {{$json['unit']}} | RQ: {{$json['reportable_quantity']}} {{$json['unit']}}\nLocation: {{$json['location']}} | Client: {{$json['client_name']}}\n42 USC \u00a79603(a): IMMEDIATE report required. Cloud outage is not an exemption.\nFollowup written report: 30 days per \u00a7103(c)"
            }
          ]
        }
      },
      "name": "CERCLA NRC Alert",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        600,
        100
      ]
    },
    {
      "parameters": {
        "url": "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "text",
              "value": "=\u26a0\ufe0f RCRA LQG EMERGENCY \u2014 15-MINUTE CLOCK (40 CFR \u00a7264.56(d)(1))\nNotify: Local emergency coordinator IMMEDIATELY + State agency within 15 min\nClient: {{$json['client_name']}} | Location: {{$json['location']}}\nWaste stream: {{$json['waste_stream']}} | Emergency coordinator: {{$json['emergency_coordinator']}}"
            }
          ]
        }
      },
      "name": "RCRA Emergency Alert",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        600,
        400
      ]
    },
    {
      "parameters": {
        "fromEmail": "compliance@flowkit.io",
        "toEmail": "={{$json[\"primacy_agency_email\"]}}",
        "subject": "=SDWA Compliance Notice \u2014 {{$json['violation_type']}} \u2014 {{$json['pws_name']}}",
        "text": "=SDWA COMPLIANCE NOTIFICATION\n\nPublic Water System: {{$json['pws_name']}} (PWSID: {{$json['pwsid']}})\nViolation: {{$json['violation_type']}}\nAuthority: {{$json['regulatory_authority']}}\nMeasured value: {{$json['measured_value']}}\nAction level/MCL: {{$json['action_level']}}\nSampling date: {{$json['sample_date']}}\n\nThis notification is transmitted pursuant to 40 CFR \u00a7141.85 / \u00a7141.201.\nPublic notice timeline: {{$json['public_notice_deadline']}}."
      },
      "name": "SDWA Primacy Notice",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2,
      "position": [
        600,
        700
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\"status\": \"ok\", \"incident_type\": \"{{$json['incident_type']}}\", \"routed\": true, \"ts\": \"{{new Date().toISOString()}}\"}"
      },
      "name": "Respond OK",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        900,
        400
      ]
    }
  ],
  "connections": {
    "Incident Webhook": {
      "main": [
        [
          {
            "node": "CERCLA Release?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CERCLA Release?": {
      "main": [
        [
          {
            "node": "CERCLA NRC Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "RCRA Emergency?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RCRA Emergency?": {
      "main": [
        [
          {
            "node": "RCRA Emergency Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "SDWA Violation?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SDWA Violation?": {
      "main": [
        [
          {
            "node": "SDWA Primacy Notice",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CERCLA NRC Alert": {
      "main": [
        [
          {
            "node": "Respond OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RCRA Emergency Alert": {
      "main": [
        [
          {
            "node": "Respond OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SDWA Primacy Notice": {
      "main": [
        [
          {
            "node": "Respond OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Workflow 5: Weekly WaterTech SaaS KPI & Compliance Dashboard

Monday 7AM: pulls MRR by tier (WATER_UTILITY/ENVIRONMENTAL_MONITORING/WASTEWATER/DRINKING_WATER/STORMWATER/CONSULTING/STARTUP), open compliance deadline counts by type, soonest due dates. HTML email to CEO + BCC compliance lead.

{
  "name": "Weekly WaterTech SaaS KPI & Compliance Dashboard",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "weeksInterval": 1,
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 7
            }
          ]
        }
      },
      "name": "Monday 7AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        100,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT tier, COUNT(*) as accounts, SUM(mrr) as tier_mrr, SUM(npdes_permits) as npdes_permits, SUM(sdwa_systems) as sdwa_systems, SUM(cercla_reporters) as cercla_reporters FROM watertech_accounts WHERE active = true GROUP BY tier ORDER BY tier_mrr DESC"
      },
      "name": "Fetch KPI Data",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        350,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT type, COUNT(*) as open_count, MIN(due_date) as soonest_due FROM compliance_deadlines WHERE status = 'OPEN' AND due_date > NOW() GROUP BY type ORDER BY soonest_due ASC LIMIT 10"
      },
      "name": "Fetch Open Deadlines",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        350,
        500
      ]
    },
    {
      "parameters": {
        "fromEmail": "kpi@flowkit.io",
        "toEmail": "ceo@yourcompany.io",
        "additionalFields": {
          "bcc": "compliance@yourcompany.io"
        },
        "subject": "=WaterTech SaaS Weekly KPI \u2014 {{new Date().toLocaleDateString('en-US', {weekday:'long',year:'numeric',month:'long',day:'numeric'})}}",
        "html": "=<h2>WaterTech SaaS Weekly KPI</h2><p><b>Total MRR:</b> ${{$node['Fetch KPI Data'].json.reduce((s,r)=>s+Number(r.tier_mrr),0).toLocaleString()}}</p><h3>By Tier</h3><table border='1' cellpadding='4'>{{$node['Fetch KPI Data'].json.map(r=>`<tr><td>${r.tier}</td><td>${r.accounts} accounts</td><td>$${Number(r.tier_mrr).toLocaleString()} MRR</td><td>${r.npdes_permits} NPDES</td><td>${r.sdwa_systems} SDWA</td></tr>`).join('')}}</table><h3>Open Compliance Deadlines (Top 10)</h3><table border='1' cellpadding='4'>{{$node['Fetch Open Deadlines'].json.map(r=>`<tr><td>${r.type}</td><td>${r.open_count} open</td><td>Soonest: ${r.soonest_due}</td></tr>`).join('')}}</table>"
      },
      "name": "Send KPI Email",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2,
      "position": [
        600,
        400
      ]
    }
  ],
  "connections": {
    "Monday 7AM": {
      "main": [
        [
          {
            "node": "Fetch KPI Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch KPI Data": {
      "main": [
        [
          {
            "node": "Fetch Open Deadlines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Open Deadlines": {
      "main": [
        [
          {
            "node": "Send KPI Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Self-Hosting Argument for WaterTech SaaS Vendors

The case for self-hosted n8n in environmental compliance infrastructure:

  1. CERCLA immediate reporting requires architecture that functions independently of third-party SaaS availability. 42 USC §9603(a) has no vendor downtime exception.
  2. NPDES monitoring data flowing through a cloud iPaaS creates independent subpoena exposure for that vendor — your eDMR data lives in their infrastructure.
  3. SDWA lead testing records are regulatory evidence. Chain of custody integrity is undermined when data transits a cloud iPaaS with third-party access rights.
  4. RCRA hazardous waste audit trail — 40 CFR §264.74 requires records be maintained for 3 years. Cloud iPaaS ToS may not guarantee preservation on your schedule.

Self-hosted n8n keeps monitoring data, incident records, and reporting workflows inside the client's infrastructure boundary — eliminating the independent vendor subpoena vector.

Ready-to-deploy workflow templates: https://stripeai.gumroad.com


This article is for informational purposes. Consult qualified environmental counsel for site-specific compliance requirements.

Top comments (0)