DEV Community

Alex Kane
Alex Kane

Posted on

n8n for WasteManagement & Environmental SaaS Vendors: 5 Automations for EPA RCRA, TSCA, CWA, and CERCLA

If you sell environmental compliance software — waste management platforms, water treatment SaaS, chemical inventory tools, air quality monitoring — you're managing customers whose regulatory exposure spans RCRA, TSCA, CWA §311, CERCLA, EPCRA, and CAA Title V simultaneously.

That's six overlapping federal frameworks. Each has its own reporting deadlines, incident clocks, and enforcement penalties. And unlike most software verticals, your customers face criminal exposure for missed reporting — not just civil fines.

Here are 5 n8n workflows that handle the compliance automation layer so your platform team doesn't have to build it from scratch.


Why Self-Hosted n8n for Environmental SaaS?

Regulatory driver Self-hosting argument
CERCLA §103(a) NRC call log Call chain-of-custody is evidence in §107 cost recovery — third-party cloud = uncontrolled government record
TSCA §8(e) CBI chemicals Cloud automation routing = unauthorized CBI disclosure to third-party subprocessor
CWA NPDES monitoring data Permit terms often specify facility-controlled data management for discharge records
RCRA cradle-to-grave manifest Manifest chain must be defensible in EPA/DOJ criminal enforcement — no vendor middleware
CAA Title V CEMS deviation CEMS data alteration is a criminal offense (18 USC §1519) — chain of custody matters
SOC 2 CC9.2 subprocessor scope Customer's environmental auditor will ask who touches their regulatory reporting data

Workflow 1: EnviroTech Customer Onboarding & Compliance Drip

Trigger: New customer webhook
Logic: Inject compliance tier + flags (RCRA LQG / TSCA reporter / NPDES holder / CERCLA applicable / EPCRA Tier II / CAA Title V / DOT hazmat) → Day 0 welcome with activated clocks → Day 3 endpoint integration guide → Day 7 incident pipeline activation

7 customer tiers: ENTERPRISE_ENVIRONMENTAL_COMPLIANCE_PLATFORM / HAZARDOUS_WASTE_MANAGEMENT_SAAS / INDUSTRIAL_WATER_TREATMENT_SAAS / ENVIRONMENTAL_MONITORING_SAAS / CHEMICAL_INVENTORY_MANAGEMENT_SAAS / AIR_QUALITY_COMPLIANCE_SAAS / ENVIRONMENTAL_CONSULTING_SAAS

7 compliance flags: EPA_RCRA_HAZARDOUS_WASTE_GENERATOR / TSCA_CHEMICAL_MANUFACTURER_IMPORTER / CWA_NPDES_PERMIT_HOLDER / CERCLA_REPORTABLE_QUANTITY_APPLICABLE / EPCRA_TIER_II_REPORTER / CAA_TITLE_V_MAJOR_SOURCE / DOT_HAZMAT_TRANSPORTER

{
  "name": "1. EnviroTech Customer Onboarding & Compliance Drip",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "envirosass-onboard",
        "responseMode": "responseNode"
      },
      "name": "Webhook \u2014 New Customer",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const d = $input.first().json;\nconst TIERS = {\n  ENTERPRISE_ENVIRONMENTAL_COMPLIANCE_PLATFORM: { day0_focus: 'RCRA LQG compliance + CERCLA \u00a7103 emergency protocols', priority: 'ENTERPRISE' },\n  HAZARDOUS_WASTE_MANAGEMENT_SAAS: { day0_focus: 'RCRA \u00a73002 manifest tracking + 40 CFR \u00a7264.56 emergency procedures', priority: 'CORE' },\n  INDUSTRIAL_WATER_TREATMENT_SAAS: { day0_focus: 'CWA NPDES DMR monthly + \u00a7311 spill notification protocols', priority: 'CORE' },\n  ENVIRONMENTAL_MONITORING_SAAS: { day0_focus: 'CAA Title V CEMS + EPCRA \u00a7313 TRI reporting calendar', priority: 'CORE' },\n  CHEMICAL_INVENTORY_MANAGEMENT_SAAS: { day0_focus: 'TSCA \u00a78(a) CDR + EPCRA \u00a7312 Tier II inventory thresholds', priority: 'CORE' },\n  AIR_QUALITY_COMPLIANCE_SAAS: { day0_focus: 'CAA Title V permit + NSPS 40 CFR Part 60 CEMS deviation reporting', priority: 'CORE' },\n  ENVIRONMENTAL_CONSULTING_SAAS: { day0_focus: 'Multi-regulation calendar: RCRA + CWA + CAA + CERCLA + EPCRA', priority: 'PROFESSIONAL' }\n};\nconst tier = TIERS[d.customer_tier] || TIERS['ENVIRONMENTAL_CONSULTING_SAAS'];\nconst flags = {\n  rcra_lqg: d.flags?.includes('EPA_RCRA_HAZARDOUS_WASTE_GENERATOR') || false,\n  tsca_reporter: d.flags?.includes('TSCA_CHEMICAL_MANUFACTURER_IMPORTER') || false,\n  npdes_holder: d.flags?.includes('CWA_NPDES_PERMIT_HOLDER') || false,\n  cercla_applicable: d.flags?.includes('CERCLA_REPORTABLE_QUANTITY_APPLICABLE') || false,\n  epcra_tier2: d.flags?.includes('EPCRA_TIER_II_REPORTER') || false,\n  caa_title5: d.flags?.includes('CAA_TITLE_V_MAJOR_SOURCE') || false,\n  dot_hazmat: d.flags?.includes('DOT_HAZMAT_TRANSPORTER') || false\n};\nreturn [{ json: { ...d, tier_config: tier, flags, onboard_ts: new Date().toISOString() } }];\n"
      },
      "name": "Code \u2014 Inject Tier + Flags",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "fromEmail": "compliance@your-company.com",
        "toEmail": "={{ $json.customer_email }}",
        "subject": "Welcome to [Your Platform] \u2014 Your Environmental Compliance Calendar is Live",
        "html": "<p>Hi {{ $json.customer_name }},</p><p>Welcome to [Your Platform]. Your compliance calendar is configured for: <b>{{ $json.tier_config.day0_focus }}</b>.</p><p><b>Activated compliance clocks:</b></p><ul>{% if $json.flags.cercla_applicable %}<li>CERCLA \u00a7103(a) \u2014 NRC immediate notification on RQ release (call log preserved in your Postgres instance)</li>{% endif %}{% if $json.flags.rcra_lqg %}<li>RCRA 40 CFR \u00a7264.56 \u2014 24h unauthorized release report + 15-day written follow-up</li>{% endif %}{% if $json.flags.npdes_holder %}<li>CWA NPDES Discharge Monitoring Report \u2014 monthly submission clock</li>{% endif %}{% if $json.flags.tsca_reporter %}<li>TSCA \u00a78(e) \u2014 30-day substantial risk notification; CBI submissions routed through your self-hosted instance</li>{% endif %}{% if $json.flags.epcra_tier2 %}<li>EPCRA \u00a7312 Tier II \u2014 March 1 annual + \u00a7304 emergency release immediate notification</li>{% endif %}{% if $json.flags.caa_title5 %}<li>CAA Title V \u2014 annual compliance certification + 24h exceedance notification</li>{% endif %}{% if $json.flags.dot_hazmat %}<li>DOT HMR \u2014 \u00a7171.15 immediate phone report + \u00a7171.16 Form 5800.1 within 30 days</li>{% endif %}</ul><p>Your n8n instance is self-hosted \u2014 CERCLA NRC call logs, RCRA manifests, and NPDES monitoring data stay within your network boundary. No third-party SaaS processor in your government reporting chain.</p><p>Setup guide attached. Reply with questions.</p>"
      },
      "name": "Gmail \u2014 Day 0 Welcome",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "unit": "days",
        "amount": 3
      },
      "name": "Wait \u2014 3 Days",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "fromEmail": "compliance@your-company.com",
        "toEmail": "={{ $json.customer_email }}",
        "subject": "Day 3: Connect Your EPA Monitoring Endpoints",
        "html": "<p>Hi {{ $json.customer_name }},</p><p>Day 3 check-in: Time to connect your monitoring data sources.</p><ul><li><b>RCRA waste tracking API</b> \u2014 manifest chain endpoint</li><li><b>NPDES monitoring API</b> \u2014 discharge flow meter data</li><li><b>CERCLA NRC portal</b> \u2014 RQ substance inventory feed</li><li><b>Air emissions API</b> \u2014 CEMS real-time feed for Title V</li></ul><p>All endpoints monitored every 5 minutes. Downtime alert fires to your #environmental-incident-bridge Slack channel before your permit authority notices.</p>"
      },
      "name": "Gmail \u2014 Day 3 Integration",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1120,
        300
      ]
    },
    {
      "parameters": {
        "unit": "days",
        "amount": 4
      },
      "name": "Wait \u2014 4 Days",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [
        1340,
        300
      ]
    },
    {
      "parameters": {
        "fromEmail": "compliance@your-company.com",
        "toEmail": "={{ $json.customer_email }}",
        "subject": "Day 7: Your Environmental Incident Pipeline is Armed",
        "html": "<p>Hi {{ $json.customer_name }},</p><p>Your incident response pipeline is now active. Test it with a POST to your webhook:</p><pre>{\"incident_type\": \"CERCLA_REPORTABLE_QUANTITY_RELEASE\", \"substance\": \"benzene\", \"quantity_lbs\": 10, \"facility_id\": \"your-facility\"}</pre><p>The workflow will: route to correct NRC/LEPC/SERC notification template \u2192 set deadline clock \u2192 log to incident_log table \u2192 alert your #environmental-incident-bridge Slack channel \u2192 send escalation email within seconds.</p><p><b>Remember:</b> CERCLA \u00a7103(a) and CWA \u00a7311 require <i>immediate</i> NRC notification. Your self-hosted n8n keeps the call log chain-of-custody intact \u2014 no third-party SaaS processor between your facility data and the government record.</p><p>Questions? Reply here.</p>"
      },
      "name": "Gmail \u2014 Day 7 Incident Activation",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1560,
        300
      ]
    }
  ],
  "connections": {},
  "active": false,
  "settings": {}
}
Enter fullscreen mode Exit fullscreen mode

Workflow 2: EPA/NRC Environmental API Health Monitor

Trigger: Every 5 minutes
Logic: Poll 5 endpoints → deduplicate with $getWorkflowStaticData (only alert on UP→DOWN transitions) → Slack #environmental-incident-bridge + Google Sheets log

5 endpoints monitored:

  • rcra_waste_tracking_api — 40 CFR §264.56 cradle-to-grave chain
  • tsca_chemical_reporting_api — TSCA §8(e) 30-day clock
  • cwta_npdes_monitoring_api — NPDES DMR monthly
  • cercla_nrc_portal_api — §103(a) RQ release immediate notification
  • air_emissions_monitoring_api — CAA Title V CEMS deviation
{
  "name": "2. EPA/NRC Environmental API Health Monitor",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 5
            }
          ]
        }
      },
      "name": "Schedule \u2014 Every 5 Minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "url": "https://api.your-platform.com/rcra/status",
        "authentication": "genericCredentialType"
      },
      "name": "HTTP \u2014 RCRA Waste Tracking API",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        460,
        200
      ]
    },
    {
      "parameters": {
        "url": "https://api.your-platform.com/tsca/status",
        "authentication": "genericCredentialType"
      },
      "name": "HTTP \u2014 TSCA Chemical Reporting API",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "url": "https://api.your-platform.com/npdes/status",
        "authentication": "genericCredentialType"
      },
      "name": "HTTP \u2014 CWA NPDES Monitoring API",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        460,
        400
      ]
    },
    {
      "parameters": {
        "url": "https://api.your-platform.com/nrc/status",
        "authentication": "genericCredentialType"
      },
      "name": "HTTP \u2014 CERCLA NRC Portal API",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        460,
        500
      ]
    },
    {
      "parameters": {
        "url": "https://api.your-platform.com/air-emissions/status",
        "authentication": "genericCredentialType"
      },
      "name": "HTTP \u2014 Air Emissions Monitoring API",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        460,
        600
      ]
    },
    {
      "parameters": {
        "jsCode": "const endpoints = [\n  { name: 'rcra_waste_tracking_api', regulatory: '40 CFR \u00a7264.56 \u2014 24h unauthorized release report, RCRA cradle-to-grave manifest chain', data: $(\"HTTP \u2014 RCRA Waste Tracking API\").first().json },\n  { name: 'tsca_chemical_reporting_api', regulatory: '15 USC \u00a72607 / TSCA \u00a78(e) 30-day substantial risk \u2014 cloud routing = additional CBI subprocessor', data: $(\"HTTP \u2014 TSCA Chemical Reporting API\").first().json },\n  { name: 'cwta_npdes_monitoring_api', regulatory: '40 CFR \u00a7122.41(j) \u2014 NPDES DMR monthly, stormwater discharge data facility-bound', data: $(\"HTTP \u2014 CWA NPDES Monitoring API\").first().json },\n  { name: 'cercla_nrc_portal_api', regulatory: 'CERCLA \u00a7103(a) \u2014 immediate NRC notification on RQ release; call log is government evidence in enforcement', data: $(\"HTTP \u2014 CERCLA NRC Portal API\").first().json },\n  { name: 'air_emissions_monitoring_api', regulatory: '40 CFR Part 70 \u2014 CAA Title V annual compliance certification; CEMS data must be defensible in EPA inspection', data: $(\"HTTP \u2014 Air Emissions Monitoring API\").first().json }\n];\nconst prev = $getWorkflowStaticData('global');\nconst now = new Date().toISOString();\nconst alerts = [];\nfor (const ep of endpoints) {\n  const status = ep.data?.status === 'ok' ? 'UP' : 'DOWN';\n  const key = ep.name;\n  if (status === 'DOWN' && prev[key] !== 'DOWN') {\n    alerts.push({ endpoint: ep.name, status, regulatory: ep.regulatory, detected_at: now });\n  }\n  prev[key] = status;\n}\n$setWorkflowStaticData('global', prev);\nreturn alerts.length > 0 ? alerts.map(a => ({ json: a })) : [{ json: { all_ok: true } }];\n"
      },
      "name": "Code \u2014 Deduplicate Alerts",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        680,
        400
      ]
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.all_ok }}",
              "value2": true
            }
          ]
        }
      },
      "name": "IF \u2014 Any Alerts",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        900,
        400
      ]
    },
    {
      "parameters": {
        "channel": "#environmental-incident-bridge",
        "text": "={{ '\ud83d\udea8 ENVSAAS API DOWN: ' + $json.endpoint + ' | Regulatory: ' + $json.regulatory + ' | Detected: ' + $json.detected_at }}"
      },
      "name": "Slack \u2014 Alert #environmental-incident-bridge",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 1,
      "position": [
        1120,
        300
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": "your-google-sheet-id",
        "sheetName": "api_health_log",
        "columns": {
          "mappingMode": "autoMapInputData"
        }
      },
      "name": "Google Sheets \u2014 Log API Status",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        1120,
        500
      ]
    }
  ],
  "connections": {},
  "active": false,
  "settings": {}
}
Enter fullscreen mode Exit fullscreen mode

Workflow 3: EPA/CERCLA/EPCRA Compliance Deadline Tracker

Trigger: Weekdays at 8AM
Logic: Pull deadlines from Google Sheets → classify urgency (OVERDUE / CRITICAL ≤14d / URGENT ≤30d / WARNING ≤60d / NOTICE ≤90d) → Slack + Gmail to owner

12 deadline types tracked:

  1. EPCRA_TIER_II_ANNUAL_MARCH_1 — §312 Tier II Chemical Inventory
  2. RCRA_BIENNIAL_REPORT_MARCH_1 — RCRA §3002 biennial report (even years)
  3. TSCA_CDR_QUADRENNIAL_SUBMISSION — TSCA §8(a) Chemical Data Reporting
  4. CWA_NPDES_DMR_MONTHLY — NPDES Discharge Monitoring Report
  5. CAA_TITLE_V_ANNUAL_COMPLIANCE_CERT — 40 CFR §70.6(c)(5)
  6. EPA_RMP_EMERGENCY_RESPONSE_EXERCISE — 40 CFR Part 68 (every 3 years)
  7. DOT_HAZMAT_TRAINING_TRIENNIAL — 49 CFR §172.704 (every 3 years)
  8. RCRA_WASTE_MINIMIZATION_ANNUAL — 40 CFR §262.27 manifest certification
  9. EPCRA_TRI_ANNUAL_JULY_1 — §313 Toxic Release Inventory Form R
  10. CWA_STORMWATER_SWPPP_ANNUAL — NPDES Multi-Sector General Permit
  11. SOC2_TYPE2_RENEWAL — Customer procurement requirement
  12. ISO_14001_SURVEILLANCE_AUDIT — Environmental management certification
{
  "name": "3. EPA/CERCLA/EPCRA Compliance Deadline Tracker",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1-5"
            }
          ]
        }
      },
      "name": "Schedule \u2014 Weekdays 8AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "documentId": "your-google-sheet-id",
        "sheetName": "compliance_deadlines",
        "options": {}
      },
      "name": "Google Sheets \u2014 Get Deadlines",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const DEADLINE_TYPES = {\n  EPCRA_TIER_II_ANNUAL_MARCH_1: { label: 'EPCRA \u00a7312 Tier II Chemical Inventory Report (March 1)', clock_days: 365, penalty: 'EPA EPCRA \u00a7325(c) \u2014 $25k-$75k/day civil penalty; state enforcement separately' },\n  RCRA_BIENNIAL_REPORT_MARCH_1: { label: 'EPA RCRA Biennial Hazardous Waste Report (March 1, even years)', clock_days: 730, penalty: '40 CFR \u00a7262.41 \u2014 RCRA \u00a73008 $37,500/day civil penalty; criminal referral for willful' },\n  TSCA_CDR_QUADRENNIAL_SUBMISSION: { label: 'TSCA \u00a78(a) Chemical Data Reporting \u2014 Quadrennial (2024, 2028\u2026)', clock_days: 1461, penalty: 'TSCA \u00a716 \u2014 $37,500/day per violation; CBI waiver risk if filed late via cloud platform' },\n  CWA_NPDES_DMR_MONTHLY: { label: 'CWA NPDES Discharge Monitoring Report \u2014 Monthly', clock_days: 30, penalty: '40 CFR \u00a7122.41(l) \u2014 CWA \u00a7309(c) criminal $10k-$25k/day, \u00a7309(d) civil $25k/day per day of violation' },\n  CAA_TITLE_V_ANNUAL_COMPLIANCE_CERT: { label: 'CAA Title V Major Source Annual Compliance Certification', clock_days: 365, penalty: '40 CFR \u00a770.6(c)(5) \u2014 CAA \u00a7113(c) criminal $25k-$100k/day + 2-5yr imprisonment' },\n  EPA_RMP_EMERGENCY_RESPONSE_EXERCISE: { label: 'EPA RMP 40 CFR Part 68 Emergency Response Exercise (3yr)', clock_days: 1095, penalty: 'CAA \u00a7112(r) $37,500/day per RMP deficiency; post-accident review triggers immediate DOJ referral' },\n  DOT_HAZMAT_TRAINING_TRIENNIAL: { label: 'DOT HMR 49 CFR \u00a7172.704 Hazmat Employee Training (3yr)', clock_days: 1095, penalty: 'DOT PHMSA \u00a7179 civil penalty $83,353 per violation per day for untrained hazmat employee' },\n  RCRA_WASTE_MINIMIZATION_ANNUAL: { label: 'RCRA Waste Minimization Certification \u2014 Annual', clock_days: 365, penalty: '40 CFR \u00a7262.27 \u2014 required on each hazardous waste manifest; omission = invalid manifest' },\n  EPCRA_TRI_ANNUAL_JULY_1: { label: 'EPCRA \u00a7313 Toxic Release Inventory (TRI) Form R \u2014 July 1', clock_days: 365, penalty: 'EPA TRI $37,500/day per violation; Form R data publicly disclosed on EPA TRI Explorer \u2014 reputational risk' },\n  CWA_STORMWATER_SWPPP_ANNUAL: { label: 'CWA SWPPP Annual Review (NPDES Multi-Sector General Permit)', clock_days: 365, penalty: '40 CFR \u00a7122.26 \u2014 CWA \u00a7309 civil/criminal enforcement; permit termination for failure' },\n  SOC2_TYPE2_RENEWAL: { label: 'SOC 2 Type II Annual Renewal', clock_days: 365, penalty: 'Enterprise customer procurement requirement \u2014 loss of contract for missing SOC 2' },\n  ISO_14001_SURVEILLANCE_AUDIT: { label: 'ISO 14001 Environmental Management Surveillance Audit', clock_days: 365, penalty: 'Certification suspension \u2014 public procurement and ESG supplier chain disqualification' }\n};\nconst today = new Date();\nconst results = [];\nfor (const row of $input.all()) {\n  const d = row.json;\n  const due = new Date(d.due_date);\n  const daysLeft = Math.round((due - today) / 86400000);\n  const dtype = DEADLINE_TYPES[d.deadline_type] || { label: d.deadline_type, penalty: 'Review required' };\n  let urgency = 'OK';\n  if (daysLeft < 0) urgency = 'OVERDUE';\n  else if (daysLeft <= 14) urgency = 'CRITICAL';\n  else if (daysLeft <= 30) urgency = 'URGENT';\n  else if (daysLeft <= 60) urgency = 'WARNING';\n  else if (daysLeft <= 90) urgency = 'NOTICE';\n  if (urgency !== 'OK') results.push({ json: { ...d, ...dtype, days_left: daysLeft, urgency } });\n}\nreturn results.length > 0 ? results : [{ json: { all_clear: true } }];\n"
      },
      "name": "Code \u2014 Classify Urgency",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.all_clear }}",
              "value2": true
            }
          ]
        }
      },
      "name": "IF \u2014 Any Due",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "channel": "#compliance-critical",
        "text": "={{ '\u26a0\ufe0f ' + $json.urgency + ': ' + $json.label + ' | Due: ' + $json.due_date + ' (' + $json.days_left + ' days) | Penalty: ' + $json.penalty }}"
      },
      "name": "Slack \u2014 Alert #compliance-critical",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 1,
      "position": [
        1120,
        200
      ]
    },
    {
      "parameters": {
        "fromEmail": "compliance@your-company.com",
        "toEmail": "cco@your-company.com",
        "subject": "={{ $json.urgency + ': ' + $json.label + ' due in ' + $json.days_left + ' days' }}",
        "text": "={{ $json.label + '\\nDue: ' + $json.due_date + '\\nPenalty: ' + $json.penalty }}"
      },
      "name": "Gmail \u2014 Notify Owner",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1120,
        400
      ]
    }
  ],
  "connections": {},
  "active": false,
  "settings": {}
}
Enter fullscreen mode Exit fullscreen mode

Workflow 4: Environmental Release & Emergency Incident Pipeline

Trigger: Webhook (your platform detects release event)
Logic: Route by incident type → set deadline clock → Slack alert + Gmail to CEO/CCO BCC CISO/Legal → Postgres INSERT

8 incident types — fastest clock: CERCLA/CWA IMMEDIATE (15 minutes):

Incident Clock Regulatory
CERCLA_REPORTABLE_QUANTITY_RELEASE IMMEDIATE CERCLA §103(a) — criminal misdemeanor for failure
CWA_OIL_HAZARDOUS_SUBSTANCE_SPILL IMMEDIATE CWA §311(b)(5) — $37,500/day civil
EPCRA_EMERGENCY_RELEASE_NOTIFICATION 30 minutes EPCRA §304 — LEPC/SERC notification
RCRA_UNAUTHORIZED_HAZARDOUS_WASTE_RELEASE 24 hours 40 CFR §264.56 — $37,500/day + criminal
TSCA_SUBSTANTIAL_RISK_INFORMATION 30 days TSCA §8(e) — CBI routing risk via cloud
CAA_AIR_QUALITY_EXCEEDANCE 24 hours CAA §113(c) — criminal $25k/day
DOT_HAZMAT_INCIDENT_IMMEDIATE 12 hours 49 CFR §171.15 — PHMSA $83,353/day
WATER_TREATMENT_SYSTEM_FAILURE 4 hours CWA §301 — unpermitted discharge $25k/day
{
  "name": "4. Environmental Release & Emergency Incident Pipeline",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "envirosass-incident",
        "responseMode": "responseNode"
      },
      "name": "Webhook \u2014 Incident Report",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const incidentMap = {\n  CERCLA_REPORTABLE_QUANTITY_RELEASE: {\n    label: 'CERCLA \u00a7103(a) Reportable Quantity Release \u2014 NRC Immediate Notification',\n    deadline_hours: 0.25,\n    recipients: ['ceo@your-company.com','vp_environmental@your-company.com'],\n    bcc: ['ciso@your-company.com','legal@your-company.com'],\n    slack: '#environmental-incident-bridge',\n    action: 'Call NRC (1-800-424-8802) immediately. Document caller, time, RQ substance and quantity. Preserve call log \u2014 it is government evidence in any CERCLA enforcement proceeding.',\n    regulatory: 'CERCLA \u00a7103(a) 42 USC \u00a79603 \u2014 failure to report is criminal misdemeanor; call log chain-of-custody critical for \u00a7107 cost recovery defense'\n  },\n  CWA_OIL_HAZARDOUS_SUBSTANCE_SPILL: {\n    label: 'CWA \u00a7311(b)(5) Oil / Hazardous Substance Discharge \u2014 NRC Immediate Notification',\n    deadline_hours: 0.25,\n    recipients: ['ceo@your-company.com','vp_environmental@your-company.com'],\n    bcc: ['ciso@your-company.com','legal@your-company.com'],\n    slack: '#environmental-incident-bridge',\n    action: 'Call NRC immediately. Initiate spill containment. Notify state agency per NPDES permit. Document notification log for EPA On-Scene Coordinator.',\n    regulatory: 'CWA \u00a7311(b)(5) \u2014 $37,500/day civil penalty; criminal: \u00a7309(c)(1) $2,500-$25,000/day 1yr imprisonment'\n  },\n  EPCRA_EMERGENCY_RELEASE_NOTIFICATION: {\n    label: 'EPCRA \u00a7304 Emergency Release Notification \u2014 LEPC / SERC Immediate',\n    deadline_hours: 0.5,\n    recipients: ['ceo@your-company.com','vp_environmental@your-company.com'],\n    bcc: ['legal@your-company.com'],\n    slack: '#environmental-incident-bridge',\n    action: 'Notify Local Emergency Planning Committee (LEPC) and State Emergency Response Commission (SERC) immediately. Written follow-up within 30 days EPCRA \u00a7304(c).',\n    regulatory: 'EPCRA \u00a7304 / 40 CFR \u00a7355.40 \u2014 $25k/day civil penalty; state EPCRA enforcement additional'\n  },\n  RCRA_UNAUTHORIZED_HAZARDOUS_WASTE_RELEASE: {\n    label: 'RCRA \u00a73008 Unauthorized Hazardous Waste Release \u2014 24h Report',\n    deadline_hours: 24,\n    recipients: ['ceo@your-company.com','vp_environmental@your-company.com'],\n    bcc: ['ciso@your-company.com','legal@your-company.com'],\n    slack: '#environmental-incident-bridge',\n    action: 'Notify EPA Regional Administrator within 24h per 40 CFR \u00a7264.56. Initiate emergency cleanup. Submit 15-day written follow-up.',\n    regulatory: '40 CFR \u00a7264.56 \u2014 RCRA \u00a73008 $37,500/day civil; criminal for knowing endangerment: $250k/50k per day + up to 15yr imprisonment'\n  },\n  TSCA_SUBSTANTIAL_RISK_INFORMATION: {\n    label: 'TSCA \u00a78(e) Substantial Risk Information \u2014 30-Day Report',\n    deadline_hours: 720,\n    recipients: ['ceo@your-company.com','vp_regulatory@your-company.com'],\n    bcc: ['legal@your-company.com'],\n    slack: '#regulatory-alerts',\n    action: 'Submit TSCA \u00a78(e) notice to EPA within 30 calendar days. Flag chemical identity as CBI if applicable. Route submission through self-hosted n8n \u2014 cloud iPaaS routing = unauthorized CBI disclosure risk.',\n    regulatory: 'TSCA \u00a716 \u2014 $37,500/day per violation; CBI classification at risk if submitted via cloud automation platform without EPA-approved data handling'\n  },\n  CAA_AIR_QUALITY_EXCEEDANCE: {\n    label: 'CAA Title V Air Quality Standard Exceedance \u2014 24h Notification',\n    deadline_hours: 24,\n    recipients: ['vp_environmental@your-company.com','cco@your-company.com'],\n    bcc: ['legal@your-company.com'],\n    slack: '#environmental-incident-bridge',\n    action: 'Notify state air agency within 24h per Title V permit. File deviation report within 30 days. CEMS data must be preserved \u2014 do not alter records.',\n    regulatory: '40 CFR \u00a770.6(a)(3)(iii) \u2014 CAA \u00a7113(c) criminal: $25k/day up to 5yr; permit deviation triggers state enforcement'\n  },\n  DOT_HAZMAT_INCIDENT_IMMEDIATE: {\n    label: 'DOT HMR 49 CFR \u00a7171.15 Hazmat Transportation Incident \u2014 Immediate Phone Report',\n    deadline_hours: 12,\n    recipients: ['vp_operations@your-company.com','cco@your-company.com'],\n    bcc: ['legal@your-company.com'],\n    slack: '#environmental-incident-bridge',\n    action: 'Call DOT PHMSA (1-800-424-8802) immediately for incidents involving death, serious injury, or evacuation. Submit Form 5800.1 within 30 days per \u00a7171.16.',\n    regulatory: '49 CFR \u00a7171.15 \u2014 PHMSA civil penalty up to $83,353/day per violation; criminal for knowing violations \u00a7171.2(l)'\n  },\n  WATER_TREATMENT_SYSTEM_FAILURE: {\n    label: 'Industrial Water Treatment System Failure \u2014 4h Internal Escalation',\n    deadline_hours: 4,\n    recipients: ['vp_operations@your-company.com'],\n    bcc: ['ciso@your-company.com'],\n    slack: '#environmental-incident-bridge',\n    action: 'Activate emergency treatment bypass protocol. Assess discharge permit compliance. Notify NPDES permit authority if discharge limit will be exceeded.',\n    regulatory: 'CWA \u00a7301 \u2014 unpermitted discharge: $25k/day per day; NPDES permit upset provision requires immediate documentation'\n  }\n};\nconst inc = $input.first().json;\nconst type = inc.incident_type || 'UNKNOWN';\nconst config = incidentMap[type] || {\n  label: type, deadline_hours: 24,\n  recipients: ['cco@your-company.com'],\n  bcc: ['legal@your-company.com'],\n  slack: '#environmental-alerts',\n  action: 'Review incident and assess regulatory notification obligations.',\n  regulatory: 'Determine applicable EPA/state regulation for this incident type'\n};\nconst deadline = new Date(Date.now() + config.deadline_hours * 3600000).toISOString();\nreturn [{ json: { ...inc, ...config, deadline_iso: deadline, triggered_at: new Date().toISOString() } }];\n"
      },
      "name": "Code \u2014 Route Incident",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "channel": "={{ $json.slack }}",
        "text": "={{ '\ud83d\udea8 INCIDENT: ' + $json.label + ' | Action: ' + $json.action + ' | Deadline: ' + $json.deadline_iso + ' | Regulatory: ' + $json.regulatory }}"
      },
      "name": "Slack \u2014 Alert Channel",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 1,
      "position": [
        680,
        200
      ]
    },
    {
      "parameters": {
        "fromEmail": "compliance@your-company.com",
        "toEmail": "={{ $json.recipients.join(',') }}",
        "bcc": "={{ $json.bcc.join(',') }}",
        "subject": "={{ '\ud83d\udea8 INCIDENT: ' + $json.label }}",
        "html": "<p><b>Incident:</b> {{ $json.label }}</p><p><b>Required Action:</b> {{ $json.action }}</p><p><b>Deadline:</b> {{ $json.deadline_iso }}</p><p><b>Regulatory basis:</b> {{ $json.regulatory }}</p><p>This notification was generated by your self-hosted n8n instance. Call log and incident data stored in your Postgres database only.</p>"
      },
      "name": "Gmail \u2014 Notify CEO + CCO",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO incident_log (incident_type, label, deadline_iso, triggered_at, regulatory) VALUES ('{{ $json.incident_type }}', '{{ $json.label }}', '{{ $json.deadline_iso }}', '{{ $json.triggered_at }}', '{{ $json.regulatory }}')"
      },
      "name": "Postgres \u2014 Log Incident",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        680,
        400
      ]
    }
  ],
  "connections": {},
  "active": false,
  "settings": {}
}
Enter fullscreen mode Exit fullscreen mode

Workflow 5: Weekly EnviroTech SaaS KPI Dashboard

Trigger: Monday 8AM
Logic: Postgres KPI query → build HTML table → Gmail to CEO BCC CSO/CISO/Legal

KPIs tracked: MRR WoW%, total facilities, RCRA LQG customer count, open/overdue compliance deadlines, active incidents, CERCLA NRC reports YTD, TSCA §8(e) open reports, CWA exceedances MTD

{
  "name": "5. Weekly EnviroTech SaaS KPI Dashboard",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1"
            }
          ]
        }
      },
      "name": "Schedule \u2014 Monday 8AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT metric, value FROM envirosass_kpis WHERE week = date_trunc('week', NOW())"
      },
      "name": "Postgres \u2014 Get KPIs",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const metrics = $input.all().map(r => r.json);\nconst kpis = {\n  total_facilities: metrics.filter(m => m.metric === 'total_facilities')[0]?.value || 0,\n  rcra_lqg_count: metrics.filter(m => m.metric === 'rcra_lqg_count')[0]?.value || 0,\n  open_compliance_deadlines: metrics.filter(m => m.metric === 'open_compliance_deadlines')[0]?.value || 0,\n  overdue_deadlines: metrics.filter(m => m.metric === 'overdue_deadlines')[0]?.value || 0,\n  active_incidents: metrics.filter(m => m.metric === 'active_incidents')[0]?.value || 0,\n  cercla_nrc_reports_ytd: metrics.filter(m => m.metric === 'cercla_nrc_reports_ytd')[0]?.value || 0,\n  tsca_8e_reports_open: metrics.filter(m => m.metric === 'tsca_8e_reports_open')[0]?.value || 0,\n  cwa_exceedances_mtd: metrics.filter(m => m.metric === 'cwa_exceedances_mtd')[0]?.value || 0,\n  mrr: metrics.filter(m => m.metric === 'mrr')[0]?.value || 0\n};\nconst prev = $getWorkflowStaticData('global');\nconst mrrChange = prev.mrr ? ((kpis.mrr - prev.mrr) / prev.mrr * 100).toFixed(1) : 'N/A';\n$setWorkflowStaticData('global', { mrr: kpis.mrr });\nconst html = `<h2>Weekly EnviroTech SaaS KPI \u2014 ${new Date().toDateString()}</h2>\n<table border='1' cellpadding='6'>\n<tr><th>Metric</th><th>Value</th></tr>\n<tr><td>MRR</td><td>$${kpis.mrr.toLocaleString()} (WoW: ${mrrChange}%)</td></tr>\n<tr><td>Total Facilities Managed</td><td>${kpis.total_facilities}</td></tr>\n<tr><td>RCRA LQG Customers</td><td>${kpis.rcra_lqg_count}</td></tr>\n<tr><td>Open Compliance Deadlines</td><td>${kpis.open_compliance_deadlines}</td></tr>\n<tr><td>OVERDUE Deadlines</td><td><b>${kpis.overdue_deadlines}</b></td></tr>\n<tr><td>Active Incidents</td><td>${kpis.active_incidents}</td></tr>\n<tr><td>CERCLA NRC Reports YTD</td><td>${kpis.cercla_nrc_reports_ytd}</td></tr>\n<tr><td>TSCA \u00a78(e) Open Reports</td><td>${kpis.tsca_8e_reports_open}</td></tr>\n<tr><td>CWA Exceedances MTD</td><td>${kpis.cwa_exceedances_mtd}</td></tr>\n</table>`;\nreturn [{ json: { html, kpis } }];\n"
      },
      "name": "Code \u2014 Build KPI Report",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "fromEmail": "compliance@your-company.com",
        "toEmail": "ceo@your-company.com",
        "bcc": "cso@your-company.com,ciso@your-company.com,legal@your-company.com",
        "subject": "Weekly EnviroTech SaaS KPI Dashboard",
        "html": "={{ $json.html }}"
      },
      "name": "Gmail \u2014 Weekly KPI to CEO",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        900,
        300
      ]
    }
  ],
  "connections": {},
  "active": false,
  "settings": {}
}
Enter fullscreen mode Exit fullscreen mode

Get the Complete Set

All 5 workflows above are import-ready JSON. If you want pre-built, production-tested versions with enhanced error handling and documentation, check out the FlowKit n8n Template Store.

The store has 15 automation templates for common business workflows — each a single-file JSON import.


Tags: n8n, automation, environmental compliance, waste management, EPA RCRA, CERCLA, CWA, TSCA, EPCRA

Top comments (0)