DEV Community

Alex Kane
Alex Kane

Posted on

n8n for HRTech/WorkforceTech SaaS Vendors: 5 Automations for FCRA, I-9, EEOC Disparate Impact, and FLSA Worker Classification

n8n for HRTech/WorkforceTech SaaS Vendors: 5 Automations for FCRA, I-9, EEOC Disparate Impact, and FLSA Worker Classification

Self-hosting angle: FCRA adverse action report data, I-9 immigration records, and EEOC hiring analytics routed through cloud iPaaS nodes are accessible to your cloud automation vendor under their standard ToS. A CRA audit or EEOC investigation subpoena can reach vendor records outside your litigation privilege boundary. Self-hosted n8n keeps all sensitive employment data inside your compliance perimeter.

This article covers 5 production-ready n8n workflows for HRTech SaaS vendors, background screening platforms, workforce management software, employer of record (EOR) companies, and immigration compliance tools — focused on the employment compliance clocks most likely to generate class action exposure: FCRA adverse action timing, I-9 re-verification windows, EEOC 80% rule monitoring, FLSA worker classification, and ADA/NLRA protective logging.


The Compliance Clocks That Define Employment Litigation Risk

Clock Regulation Window Miss =
FCRA Pre-Adverse Action 15 U.S.C. §615 5 business days Class action exposure — Spokeo, TransUnion LLC v. Ramirez
I-9 Re-Verification 8 CFR §274a.2(b)(1)(vii) On or before work auth expiry ICE civil fine $272–$2,701/employee, criminal if pattern
EEOC 80% Rule 29 CFR §1607.4(D) Ongoing — hire-cycle analysis Disparate impact litigation, OFCCP audit trigger
FLSA §13 Classification 29 CFR §541 Before next pay period 2-3yr back pay + liquidated damages + DOL WHD
ADA Interactive Process 42 U.S.C. §12111 Immediate — 10-day initial response ADA Title I failure-to-accommodate + litigation
NLRA §7 Protected Activity 29 U.S.C. §157 6-month SOL to NLRB charge ULP charge, reinstatement + back pay

Customer Tier Matrix

Tier Primary Compliance Exposure
BACKGROUND_SCREENING_SAAS_VENDOR FCRA/EEOC/I-9/FLSA/ADA/NLRA
APPLICANT_TRACKING_SAAS_VENDOR FCRA/EEOC/I-9/FLSA/ADA/NLRA
WORKFORCE_MANAGEMENT_SAAS FCRA/EEOC/I-9/FLSA/ADA/NLRA
EMPLOYER_OF_RECORD_SAAS FCRA/EEOC/I-9/FLSA/ADA/NLRA
IMMIGRATION_COMPLIANCE_SAAS FCRA/EEOC/I-9/FLSA/ADA/NLRA
COMPENSATION_ANALYTICS_SAAS FCRA/EEOC/I-9/FLSA/ADA/NLRA
HRTECH_STARTUP FCRA/EEOC/I-9/FLSA/ADA/NLRA

Workflow 1: FCRA Adverse Action Notification Pipeline

Compliance target: FCRA §604 (permissible purpose) + §615 (adverse action notice) + §607 (reasonable procedures)

The clock: 5 business days between pre-adverse action notice and final adverse action decision. Miss it and every affected applicant has individual statutory damages of $100–$1,000 plus punitive damages — class actions settle at $2M–$15M+.

{
  "name": "FCRA Adverse Action Notification Pipeline",
  "nodes": [
    {
      "id": "n1",
      "name": "Background Check Result Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        250,
        300
      ],
      "parameters": {
        "path": "bgcheck-result",
        "responseMode": "onReceived"
      }
    },
    {
      "id": "n2",
      "name": "Validate Permissible Purpose \u00a7604",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        450,
        300
      ],
      "parameters": {
        "jsCode": "const d = $input.first().json.body;\nconst permissiblePurposes = ['employment','credit','insurance','housing','licensing'];\nconst purpose = d.permissible_purpose || 'employment';\nconst isValid = permissiblePurposes.includes(purpose);\nconst isAdverseAction = d.status === 'adverse_action_pending';\nreturn [{ json: { ...d, permissiblePurposeValid: isValid, isAdverseAction, adverseActionDeadlineTs: new Date(Date.now() + 5*24*60*60*1000).toISOString(), purpose } }];"
      }
    },
    {
      "id": "n3",
      "name": "IF Adverse Action Pending",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        650,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true
          },
          "conditions": [
            {
              "leftValue": "={{ $json.isAdverseAction }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ]
        }
      }
    },
    {
      "id": "n4",
      "name": "Send Pre-Adverse Action Notice + CFPB Summary of Rights",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        850,
        200
      ],
      "parameters": {
        "sendTo": "={{ $json.applicant_email }}",
        "subject": "Important Notice Regarding Your Background Check \u2014 Action Required",
        "message": "={{ 'Dear ' + $json.applicant_name + ',\\n\\nPursuant to the Fair Credit Reporting Act (FCRA) 15 U.S.C. \u00a7615, we are providing you with a pre-adverse action notice.\\n\\nAttached: (1) A copy of your consumer report, (2) CFPB Summary of Rights Under the FCRA.\\n\\nYou have 5 business days to review this report and dispute any inaccuracies with the Consumer Reporting Agency before a final decision is made.\\n\\nReport provided by: ' + ($json.cra_name || 'the Consumer Reporting Agency') + '\\nJob Position: ' + $json.job_title + '\\nApplication ID: ' + $json.application_id }}",
        "options": {}
      }
    },
    {
      "id": "n5",
      "name": "Log to Adverse Action Tracker",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        1050,
        200
      ],
      "parameters": {
        "operation": "append",
        "documentId": "={{ $vars.ADVERSE_ACTION_SHEET_ID }}",
        "sheetName": "adverse_action_log",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "applicant_id": "={{ $json.applicant_id }}",
            "applicant_name": "={{ $json.applicant_name }}",
            "job_title": "={{ $json.job_title }}",
            "pre_notice_sent_at": "={{ $now.toISO() }}",
            "five_day_deadline": "={{ $json.adverseActionDeadlineTs }}",
            "cra_name": "={{ $json.cra_name }}",
            "status": "PRE_ADVERSE_NOTICE_SENT"
          }
        }
      }
    },
    {
      "id": "n6",
      "name": "Wait 5 Business Days",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [
        1250,
        200
      ],
      "parameters": {
        "amount": 5,
        "unit": "days"
      }
    },
    {
      "id": "n7",
      "name": "Check Dispute Filed",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        1450,
        200
      ],
      "parameters": {
        "operation": "read",
        "documentId": "={{ $vars.ADVERSE_ACTION_SHEET_ID }}",
        "sheetName": "adverse_action_log",
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "applicant_id",
              "lookupValue": "={{ $json.applicant_id }}"
            }
          ]
        }
      }
    },
    {
      "id": "n8",
      "name": "IF Dispute Filed",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1650,
        200
      ],
      "parameters": {
        "conditions": {
          "options": {},
          "conditions": [
            {
              "leftValue": "={{ $json.dispute_filed }}",
              "rightValue": "true",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ]
        }
      }
    },
    {
      "id": "n9",
      "name": "Route to Legal Review Queue \u2014 Dispute Active",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        1850,
        100
      ],
      "parameters": {
        "channel": "#fcra-disputes",
        "text": "={{ '\u26a0\ufe0f FCRA DISPUTE FILED \u2014 ' + $json.applicant_name + ' | Application: ' + $json.application_id + ' | Dispute must be resolved before final adverse action. \u00a7611 reinvestigation within 30 days.' }}"
      }
    },
    {
      "id": "n10",
      "name": "Send Final Adverse Action Notice",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1850,
        300
      ],
      "parameters": {
        "sendTo": "={{ $json.applicant_email }}",
        "subject": "Final Employment Decision Notice \u2014 FCRA \u00a7615",
        "message": "={{ 'Dear ' + $json.applicant_name + ',\\n\\nAfter the 5-business-day review period, we have made a final employment decision regarding your application for ' + $json.job_title + '.\\n\\nThis decision was based in part on information obtained from a consumer reporting agency under the FCRA (15 U.S.C. \u00a7615).\\n\\nConsumer Reporting Agency: ' + ($json.cra_name || 'N/A') + '\\n\\nYou have the right to obtain a free copy of your report and dispute inaccurate information with the CRA within 60 days.' }}",
        "options": {}
      }
    },
    {
      "id": "n11",
      "name": "Log Final Decision",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        2050,
        300
      ],
      "parameters": {
        "operation": "update",
        "documentId": "={{ $vars.ADVERSE_ACTION_SHEET_ID }}",
        "sheetName": "adverse_action_log",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "status": "FINAL_ADVERSE_ACTION_SENT",
            "final_decision_at": "={{ $now.toISO() }}",
            "applicant_id": "={{ $json.applicant_id }}"
          }
        }
      }
    }
  ],
  "connections": {
    "Background Check Result Webhook": {
      "main": [
        [
          {
            "node": "Validate Permissible Purpose \u00a7604",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Permissible Purpose \u00a7604": {
      "main": [
        [
          {
            "node": "IF Adverse Action Pending",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF Adverse Action Pending": {
      "main": [
        [
          {
            "node": "Send Pre-Adverse Action Notice + CFPB Summary of Rights",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Send Pre-Adverse Action Notice + CFPB Summary of Rights": {
      "main": [
        [
          {
            "node": "Log to Adverse Action Tracker",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Adverse Action Tracker": {
      "main": [
        [
          {
            "node": "Wait 5 Business Days",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 5 Business Days": {
      "main": [
        [
          {
            "node": "Check Dispute Filed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Dispute Filed": {
      "main": [
        [
          {
            "node": "IF Dispute Filed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF Dispute Filed": {
      "main": [
        [
          {
            "node": "Route to Legal Review Queue \u2014 Dispute Active",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Final Adverse Action Notice",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Final Adverse Action Notice": {
      "main": [
        [
          {
            "node": "Log Final Decision",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}
Enter fullscreen mode Exit fullscreen mode

What it does: Webhook receives background check result → validates FCRA §604 permissible purpose → sends Pre-Adverse Action Notice + CFPB Summary of Rights to applicant → logs to Sheets with 5-business-day countdown → waits → checks for dispute → routes to legal review queue if dispute filed, else sends Final Adverse Action Notice → logs outcome.

Why this matters for HRTech SaaS: Every background screening SaaS vendor whose customers miss FCRA adverse action timing is one class-action plaintiff away from reputational damage that follows them into every enterprise sales cycle. Building this into your platform as an automated safeguard converts a liability into a selling point.


Workflow 2: I-9 E-Verify Tracking & Re-Verification Pipeline

Compliance target: Immigration Reform and Control Act 8 U.S.C. §1324a + 8 CFR §274a.2

The clock: Section 3 re-verification must occur on or before the work authorization document expiry date — not after. ICE audits can reach 3 years of I-9 records. Civil penalties: $272–$2,701 per unauthorized employee (first offense); $542–$5,404 (second).

{
  "name": "I-9 E-Verify Tracking & Re-Verification Pipeline",
  "nodes": [
    {
      "id": "n1",
      "name": "Daily Weekday 8AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        250,
        300
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1-5"
            }
          ]
        }
      }
    },
    {
      "id": "n2",
      "name": "Read Employee I-9 Records",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        450,
        300
      ],
      "parameters": {
        "operation": "readAllRows",
        "documentId": "={{ $vars.I9_SHEET_ID }}",
        "sheetName": "i9_records"
      }
    },
    {
      "id": "n3",
      "name": "Classify I-9 Status & Retention",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        650,
        300
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst today = new Date();\nconst results = [];\nfor (const item of items) {\n  const d = item.json;\n  const hireDate = new Date(d.hire_date);\n  const termDate = d.termination_date ? new Date(d.termination_date) : null;\n  // I-9 retention: 3yr from hire OR 1yr post-termination, whichever is later\n  const retentionDate = termDate\n    ? new Date(Math.max(new Date(hireDate.getTime() + 3*365*24*3600*1000), new Date(termDate.getTime() + 1*365*24*3600*1000)))\n    : new Date(hireDate.getTime() + 3*365*24*3600*1000);\n  const daysToRetention = Math.floor((retentionDate - today) / (24*3600*1000));\n  // Work authorization re-verification\n  const workAuthExpiry = d.work_auth_expiry ? new Date(d.work_auth_expiry) : null;\n  const daysToWorkAuthExpiry = workAuthExpiry ? Math.floor((workAuthExpiry - today) / (24*3600*1000)) : null;\n  let status = 'OK';\n  let urgency = 'NONE';\n  if (daysToRetention < 0) { status = 'RETENTION_EXPIRED'; urgency = 'CRITICAL'; }\n  else if (daysToRetention <= 30) { status = 'RETENTION_EXPIRING'; urgency = 'WARNING'; }\n  if (workAuthExpiry) {\n    if (daysToWorkAuthExpiry <= 0) { status = 'WORK_AUTH_EXPIRED'; urgency = 'CRITICAL'; }\n    else if (daysToWorkAuthExpiry <= 14) { urgency = 'CRITICAL'; status = 'REVERIFY_REQUIRED_14D'; }\n    else if (daysToWorkAuthExpiry <= 30) { urgency = 'WARNING'; status = 'REVERIFY_DUE_30D'; }\n    else if (daysToWorkAuthExpiry <= 90) { urgency = 'NOTICE'; status = 'REVERIFY_DUE_90D'; }\n  }\n  if (urgency !== 'NONE') results.push({ json: { ...d, status, urgency, daysToRetention, daysToWorkAuthExpiry, retentionDate: retentionDate.toISOString() } });\n}\nreturn results;"
      }
    },
    {
      "id": "n4",
      "name": "IF Critical I-9 Action Required",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        850,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {},
          "conditions": [
            {
              "leftValue": "={{ $json.urgency }}",
              "rightValue": "CRITICAL",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ]
        }
      }
    },
    {
      "id": "n5",
      "name": "Slack #i9-compliance \u2014 CRITICAL",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        1050,
        200
      ],
      "parameters": {
        "channel": "#i9-compliance",
        "text": "={{ '\ud83d\udea8 I-9 CRITICAL \u2014 ' + $json.employee_name + ' | Status: ' + $json.status + ' | Work Auth Expiry: ' + ($json.work_auth_expiry || 'N/A') + ' | Days remaining: ' + ($json.daysToWorkAuthExpiry || 'N/A') + ' | ICE audit exposure if not resolved. Re-verify using List A or C on or before expiry date per 8 CFR \u00a7274a.2(b)(1)(vii).' }}"
      }
    },
    {
      "id": "n6",
      "name": "Gmail HR Compliance Owner",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1050,
        400
      ],
      "parameters": {
        "sendTo": "={{ $vars.HR_COMPLIANCE_EMAIL }}",
        "subject": "={{ '[I-9 ' + $json.urgency + '] ' + $json.employee_name + ' \u2014 ' + $json.status }}",
        "message": "={{ 'Employee: ' + $json.employee_name + '\\nEmployee ID: ' + $json.employee_id + '\\nStatus: ' + $json.status + '\\nWork Authorization Expiry: ' + ($json.work_auth_expiry || 'N/A') + '\\nDays Remaining: ' + ($json.daysToWorkAuthExpiry || 'N/A') + '\\nI-9 Retention Deadline: ' + $json.retentionDate + '\\n\\nAction Required: Complete Section 3 re-verification using an unexpired List A or List C document. Do NOT re-verify List B documents. Per 8 CFR \u00a7274a.2(b)(1)(vii), re-verification must occur on or before the document expiry date.' }}",
        "options": {}
      }
    },
    {
      "id": "n7",
      "name": "Log I-9 Action to Compliance Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        1250,
        300
      ],
      "parameters": {
        "operation": "append",
        "documentId": "={{ $vars.I9_SHEET_ID }}",
        "sheetName": "i9_action_log",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "employee_id": "={{ $json.employee_id }}",
            "employee_name": "={{ $json.employee_name }}",
            "status": "={{ $json.status }}",
            "urgency": "={{ $json.urgency }}",
            "action_date": "={{ $now.toISO() }}",
            "work_auth_expiry": "={{ $json.work_auth_expiry }}",
            "days_to_expiry": "={{ $json.daysToWorkAuthExpiry }}"
          }
        }
      }
    }
  ],
  "connections": {
    "Daily Weekday 8AM": {
      "main": [
        [
          {
            "node": "Read Employee I-9 Records",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Employee I-9 Records": {
      "main": [
        [
          {
            "node": "Classify I-9 Status & Retention",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify I-9 Status & Retention": {
      "main": [
        [
          {
            "node": "IF Critical I-9 Action Required",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF Critical I-9 Action Required": {
      "main": [
        [
          {
            "node": "Slack #i9-compliance \u2014 CRITICAL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Gmail HR Compliance Owner",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack #i9-compliance \u2014 CRITICAL": {
      "main": [
        [
          {
            "node": "Log I-9 Action to Compliance Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail HR Compliance Owner": {
      "main": [
        [
          {
            "node": "Log I-9 Action to Compliance Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}
Enter fullscreen mode Exit fullscreen mode

What it does: Runs daily on weekdays → reads all employee I-9 records → calculates IRCA retention periods (3yr from hire OR 1yr post-termination, whichever is later) → flags work authorization expiries at 90/30/14-day thresholds → alerts Slack #i9-compliance for CRITICAL cases → emails HR compliance owner with specific CFR citation and action instructions → logs all actions.

Key nuance: The workflow correctly handles the List B re-verification prohibition — IRCA prohibits re-verifying List B documents (state ID, driver's license); only List A (passport, EAD) or List C (Social Security card, birth certificate) can be used for Section 3. This is the most common I-9 audit finding.


Workflow 3: EEOC Disparate Impact Monitor — 80% Rule

Compliance target: EEOC Title VII §2000e + Uniform Guidelines on Employee Selection Procedures 29 CFR §1607.4(D)

The test: Adverse impact ratio = (selection rate of protected class) / (selection rate of highest-selected group). Below 0.80 = potential disparate impact. EEOC and OFCCP both use this trigger. Federal contractors face AAP obligations under Executive Order 11246.

{
  "name": "EEOC Disparate Impact Monitor \u2014 80% Rule",
  "nodes": [
    {
      "id": "n1",
      "name": "Weekly Monday 7AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        250,
        300
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 7 * * 1"
            }
          ]
        }
      }
    },
    {
      "id": "n2",
      "name": "Query Hiring Decisions by Job Category",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        450,
        300
      ],
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT job_category, protected_class, COUNT(*) as applicants, SUM(CASE WHEN selected THEN 1 ELSE 0 END) as selected FROM hiring_decisions WHERE decision_date >= NOW() - INTERVAL '90 days' GROUP BY job_category, protected_class ORDER BY job_category, protected_class"
      }
    },
    {
      "id": "n3",
      "name": "Compute 80% Adverse Impact Ratio",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        650,
        300
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\n// Group by job_category\nconst byCategory = {};\nfor (const item of items) {\n  const { job_category, protected_class, applicants, selected } = item.json;\n  if (!byCategory[job_category]) byCategory[job_category] = {};\n  byCategory[job_category][protected_class] = { applicants: parseInt(applicants), selected: parseInt(selected) };\n}\nconst alerts = [];\nfor (const [job_category, classes] of Object.entries(byCategory)) {\n  // Find highest selection rate (comparison group)\n  let maxRate = 0;\n  let maxClass = '';\n  for (const [cls, data] of Object.entries(classes)) {\n    const rate = data.applicants > 0 ? data.selected / data.applicants : 0;\n    if (rate > maxRate) { maxRate = rate; maxClass = cls; }\n  }\n  // Check 80% rule for all other groups\n  for (const [cls, data] of Object.entries(classes)) {\n    if (cls === maxClass) continue;\n    const rate = data.applicants > 0 ? data.selected / data.applicants : 0;\n    const adverseImpactRatio = maxRate > 0 ? rate / maxRate : 1;\n    const adverseImpact = adverseImpactRatio < 0.8 && data.applicants >= 30; // min sample\n    if (adverseImpact) {\n      alerts.push({ json: { job_category, protected_class: cls, applicants: data.applicants, selected: data.selected, selectionRate: rate.toFixed(4), maxClass, maxRate: maxRate.toFixed(4), adverseImpactRatio: adverseImpactRatio.toFixed(4), severity: adverseImpactRatio < 0.5 ? 'CRITICAL' : 'WARNING' } });\n    }\n  }\n}\nreturn alerts.length > 0 ? alerts : [{ json: { noAlerts: true, message: 'No disparate impact detected this week.' } }];"
      }
    },
    {
      "id": "n4",
      "name": "IF Disparate Impact Detected",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        850,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {},
          "conditions": [
            {
              "leftValue": "={{ $json.noAlerts }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "notEquals"
              }
            }
          ]
        }
      }
    },
    {
      "id": "n5",
      "name": "Slack #eeoc-compliance \u2014 Disparate Impact Alert",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        1050,
        200
      ],
      "parameters": {
        "channel": "#eeoc-compliance",
        "text": "={{ '\u26a0\ufe0f EEOC TITLE VII DISPARATE IMPACT \u2014 ' + $json.job_category + ' | Protected Class: ' + $json.protected_class + ' | Selection Rate: ' + ($json.selectionRate*100).toFixed(1) + '% vs ' + $json.maxClass + ': ' + ($json.maxRate*100).toFixed(1) + '% | Adverse Impact Ratio: ' + $json.adverseImpactRatio + ' (< 0.80 = EEOC 80% rule violation) | Applicants analyzed: ' + $json.applicants + ' | Severity: ' + $json.severity + '. Review selection criteria under EEOC Uniform Guidelines 29 CFR \u00a71607 before next hiring cycle.' }}"
      }
    },
    {
      "id": "n6",
      "name": "Gmail EEO Officer + Legal Counsel",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1050,
        400
      ],
      "parameters": {
        "sendTo": "={{ $vars.EEO_OFFICER_EMAIL }}",
        "subject": "={{ '[EEOC ' + $json.severity + '] Disparate Impact Detected \u2014 ' + $json.job_category }}",
        "message": "={{ 'EEOC Title VII \u00a72000e Disparate Impact Monitor Report\\n\\nJob Category: ' + $json.job_category + '\\nProtected Class with Lower Selection Rate: ' + $json.protected_class + '\\nSelection Rate: ' + ($json.selectionRate*100).toFixed(1) + '%\\nHighest Selection Rate (comparison group ' + $json.maxClass + '): ' + ($json.maxRate*100).toFixed(1) + '%\\nAdverse Impact Ratio: ' + $json.adverseImpactRatio + '\\n\\nEEOC 80% Rule: An adverse impact ratio below 0.80 indicates potential disparate impact under EEOC Uniform Guidelines 29 CFR \u00a71607.4(D).\\n\\nRecommended Action:\\n1. Review selection criteria for job-relatedness and business necessity\\n2. Document legitimate, non-discriminatory reasons for selection decisions\\n3. Consult legal counsel before next hiring cycle\\n4. Consider validation study if criteria continue to produce disparate impact\\n\\nNote: This is a statistical indicator, not a legal determination. Patterns with fewer than 30 applicants per class are excluded from this report.' }}",
        "options": {}
      }
    },
    {
      "id": "n7",
      "name": "Log to EEOC Audit Trail",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        1250,
        300
      ],
      "parameters": {
        "operation": "append",
        "documentId": "={{ $vars.EEOC_SHEET_ID }}",
        "sheetName": "disparate_impact_log",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "detected_at": "={{ $now.toISO() }}",
            "job_category": "={{ $json.job_category }}",
            "protected_class": "={{ $json.protected_class }}",
            "adverse_impact_ratio": "={{ $json.adverseImpactRatio }}",
            "applicants": "={{ $json.applicants }}",
            "severity": "={{ $json.severity }}"
          }
        }
      }
    }
  ],
  "connections": {
    "Weekly Monday 7AM": {
      "main": [
        [
          {
            "node": "Query Hiring Decisions by Job Category",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Query Hiring Decisions by Job Category": {
      "main": [
        [
          {
            "node": "Compute 80% Adverse Impact Ratio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compute 80% Adverse Impact Ratio": {
      "main": [
        [
          {
            "node": "IF Disparate Impact Detected",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF Disparate Impact Detected": {
      "main": [
        [
          {
            "node": "Slack #eeoc-compliance \u2014 Disparate Impact Alert",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Slack #eeoc-compliance \u2014 Disparate Impact Alert": {
      "main": [
        [
          {
            "node": "Gmail EEO Officer + Legal Counsel",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail EEO Officer + Legal Counsel": {
      "main": [
        [
          {
            "node": "Log to EEOC Audit Trail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}
Enter fullscreen mode Exit fullscreen mode

What it does: Runs weekly Monday 7AM → queries hiring decisions by job category and protected class → computes 80% adverse impact ratio using EEOC Uniform Guidelines formula → filters for statistical significance (minimum 30 applicants) → alerts Slack #eeoc-compliance + emails EEO Officer with specific §1607 citation → logs to EEOC audit trail for annual EEO-1 reporting.

Enterprise selling point: Federal contractors ($50K+ government contracts) face OFCCP Scheduling Letter audits that request 2 years of applicant flow data. Having a real-time disparate impact monitor that produces audit-ready logs is a procurement requirement for HR platforms serving federal contractors — not a nice-to-have.


Workflow 4: FLSA Worker Classification Alert — §13 Exemption Checker

Compliance target: Fair Labor Standards Act §13 white-collar exemptions + 29 CFR §541 + state-specific salary thresholds

The exposure: FLSA misclassification: 2-year back pay + equal liquidated damages. Willful misclassification: 3-year SOL. DOL WHD collected $274M in back wages in FY2023 — misclassification is the #1 finding.

{
  "name": "FLSA Worker Classification Alert \u2014 \u00a713 Exemption Checker",
  "nodes": [
    {
      "id": "n1",
      "name": "New Hire or Role Change Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        250,
        300
      ],
      "parameters": {
        "path": "worker-classification",
        "responseMode": "onReceived"
      }
    },
    {
      "id": "n2",
      "name": "FLSA \u00a713 Exemption Analysis",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        450,
        300
      ],
      "parameters": {
        "jsCode": "const d = $input.first().json.body;\n// FLSA \u00a713 white-collar exemptions require ALL criteria\nconst weeklyPay = parseFloat(d.weekly_salary || 0);\nconst annualPay = weeklyPay * 52;\n// Federal salary threshold: $684/wk ($35,568/yr) DOL 29 CFR \u00a7541\nconst fedThreshold = 684;\n// State-specific overrides (higher wins)\nconst stateThresholds = { 'CA': 1040, 'NY': 1125, 'WA': 975, 'CO': 961, 'AK': 867 };\nconst stateThreshold = stateThresholds[d.state] || fedThreshold;\nconst effectiveThreshold = Math.max(fedThreshold, stateThreshold);\nconst meetsSalaryBasis = weeklyPay >= effectiveThreshold;\n// Duty tests (simplified \u2014 must be documented)\nconst duties = d.job_duties || [];\nconst isExecutive = meetsSalaryBasis && duties.includes('management') && duties.includes('hires_fires') && (d.direct_reports || 0) >= 2;\nconst isAdministrative = meetsSalaryBasis && duties.includes('office_nonmanual') && duties.includes('discretion_judgment');\nconst isProfessional = meetsSalaryBasis && (duties.includes('advanced_knowledge') || duties.includes('creative_professional'));\nconst isHCE = annualPay >= 107432 && duties.includes('office_nonmanual');\nconst isExempt = isExecutive || isAdministrative || isProfessional || isHCE;\nconst exemptionType = isExempt ? (isExecutive ? 'EXECUTIVE' : isAdministrative ? 'ADMINISTRATIVE' : isProfessional ? 'PROFESSIONAL' : 'HIGHLY_COMPENSATED') : 'NONEXEMPT';\nconst riskLevel = isExempt ? 'LOW' : (weeklyPay < effectiveThreshold * 0.9 ? 'HIGH' : 'MEDIUM');\nreturn [{ json: { ...d, weeklyPay, annualPay, effectiveThreshold, meetsSalaryBasis, isExempt, exemptionType, riskLevel, stateApplied: d.state, stateThreshold, misclassificationRisk: !isExempt && d.current_classification === 'exempt' } }];"
      }
    },
    {
      "id": "n3",
      "name": "IF Misclassification Risk",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        650,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {},
          "conditions": [
            {
              "leftValue": "={{ $json.misclassificationRisk }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ]
        }
      }
    },
    {
      "id": "n4",
      "name": "Slack #hr-compliance \u2014 FLSA Risk",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        850,
        200
      ],
      "parameters": {
        "channel": "#hr-compliance",
        "text": "={{ '\ud83d\udea8 FLSA MISCLASSIFICATION RISK \u2014 ' + $json.employee_name + ' | Current Classification: ' + $json.current_classification + ' | FLSA Analysis: NONEXEMPT | Weekly Pay: $' + $json.weeklyPay + ' (threshold: $' + $json.effectiveThreshold + '/wk ' + ($json.stateApplied || 'Federal') + ') | Risk: ' + $json.riskLevel + '. FLSA \u00a7207 overtime liability applies. Review before next pay period.' }}"
      }
    },
    {
      "id": "n5",
      "name": "Gmail Payroll Manager",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        850,
        400
      ],
      "parameters": {
        "sendTo": "={{ $vars.PAYROLL_MANAGER_EMAIL }}",
        "subject": "={{ '[FLSA RISK] Potential Misclassification \u2014 ' + $json.employee_name }}",
        "message": "={{ 'FLSA Worker Classification Alert\\n\\nEmployee: ' + $json.employee_name + '\\nPosition: ' + $json.job_title + '\\nCurrent Classification: ' + $json.current_classification + '\\nFLSA Analysis Result: ' + $json.exemptionType + '\\n\\nSalary Basis Test:\\n- Weekly Pay: $' + $json.weeklyPay + '\\n- Required Threshold: $' + $json.effectiveThreshold + '/week (' + ($json.stateApplied || 'Federal') + ' FLSA)\\n- Meets Salary Basis: ' + $json.meetsSalaryBasis + '\\n\\nDuties Test: See HR file\\n\\nRisk Level: ' + $json.riskLevel + '\\n\\nAction Required: Review classification against 29 CFR \u00a7541 before next pay period. Misclassification exposes the company to FLSA \u00a7207 overtime back-pay, liquidated damages (equal to back pay), and DOL WHD enforcement. Statute of limitations: 2 years (willful: 3 years).' }}",
        "options": {}
      }
    },
    {
      "id": "n6",
      "name": "Log to Classification Audit",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        1050,
        300
      ],
      "parameters": {
        "operation": "append",
        "documentId": "={{ $vars.FLSA_AUDIT_SHEET_ID }}",
        "sheetName": "classification_audit",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "employee_id": "={{ $json.employee_id }}",
            "employee_name": "={{ $json.employee_name }}",
            "current_classification": "={{ $json.current_classification }}",
            "flsa_analysis": "={{ $json.exemptionType }}",
            "risk_level": "={{ $json.riskLevel }}",
            "weekly_pay": "={{ $json.weeklyPay }}",
            "effective_threshold": "={{ $json.effectiveThreshold }}",
            "flagged_at": "={{ $now.toISO() }}"
          }
        }
      }
    }
  ],
  "connections": {
    "New Hire or Role Change Webhook": {
      "main": [
        [
          {
            "node": "FLSA \u00a713 Exemption Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FLSA \u00a713 Exemption Analysis": {
      "main": [
        [
          {
            "node": "IF Misclassification Risk",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF Misclassification Risk": {
      "main": [
        [
          {
            "node": "Slack #hr-compliance \u2014 FLSA Risk",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Slack #hr-compliance \u2014 FLSA Risk": {
      "main": [
        [
          {
            "node": "Gmail Payroll Manager",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail Payroll Manager": {
      "main": [
        [
          {
            "node": "Log to Classification Audit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}
Enter fullscreen mode Exit fullscreen mode

What it does: Webhook fires on new hire or role change → FLSA §13 analysis checks salary basis test against federal ($684/wk) AND state-specific thresholds (CA $1,040/wk, NY $1,125/wk, WA $975/wk, CO $961/wk) → applies duties test for executive/administrative/professional/HCE exemptions → flags misclassification risk → alerts #hr-compliance + emails payroll manager with specific CFR citations and 29 CFR §541 duties criteria → logs to classification audit trail.

Differentiation for workforce management SaaS: Scheduling platforms that manage exempt/non-exempt shift assignments carry indirect FLSA risk if their scheduling logic treats misclassified workers as exempt from overtime caps. This workflow adds a classification gate before schedule generation.


Workflow 5: ADA Accommodation & NLRA Protected Activity Pipeline

Compliance target: Americans with Disabilities Act §12111 + NLRA §7 protected concerted activity

Two distinct legal obligations: ADA requires good-faith interactive process (IMMEDIATE trigger, 10-day best practice for initial response). NLRA §7 protects wage discussions, working condition complaints, and union organizing — any adverse action within 6 months of protected activity creates NLRB charge exposure.

{
  "name": "ADA Accommodation & NLRA Protected Activity Pipeline",
  "nodes": [
    {
      "id": "n1",
      "name": "Accommodation or Activity Report Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        250,
        300
      ],
      "parameters": {
        "path": "hr-activity-report",
        "responseMode": "onReceived"
      }
    },
    {
      "id": "n2",
      "name": "Classify ADA vs NLRA Event",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        450,
        300
      ],
      "parameters": {
        "jsCode": "const d = $input.first().json.body;\nconst isADA = d.event_type === 'accommodation_request' || d.event_type === 'disability_disclosure' || d.event_type === 'medical_leave_ada';\nconst isNLRA = d.event_type === 'protected_concerted_activity' || d.event_type === 'union_organizing' || d.event_type === 'wage_discussion' || d.event_type === 'working_condition_complaint';\n// ADA interactive process must begin immediately \u00a712111(9)\nconst adaDeadline = new Date(Date.now() + 10*24*3600*1000).toISOString(); // 10-day initial response best practice\n// NLRA \u00a77: employees have right to discuss wages/conditions; adverse action = ULP \u00a78(a)(1)\nconst nlraWarning = isNLRA ? 'CAUTION: NLRA \u00a77 protects this activity. Any adverse action against this employee (termination, discipline, reduced hours) could constitute an Unfair Labor Practice under \u00a78(a)(1). SOL: 6 months from ULP to NLRB charge.' : null;\nreturn [{ json: { ...d, isADA, isNLRA, adaDeadline, nlraWarning, interactiveProcessRequired: isADA } }];"
      }
    },
    {
      "id": "n3",
      "name": "Switch: ADA or NLRA",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        650,
        300
      ],
      "parameters": {
        "mode": "rules",
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {},
                "conditions": [
                  {
                    "leftValue": "={{ $json.isADA }}",
                    "rightValue": true,
                    "operator": {
                      "type": "boolean",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "renameOutput": true,
              "outputKey": "ADA"
            },
            {
              "conditions": {
                "options": {},
                "conditions": [
                  {
                    "leftValue": "={{ $json.isNLRA }}",
                    "rightValue": true,
                    "operator": {
                      "type": "boolean",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "renameOutput": true,
              "outputKey": "NLRA"
            }
          ]
        }
      }
    },
    {
      "id": "n4",
      "name": "Gmail HR \u2014 ADA Interactive Process Required",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        850,
        150
      ],
      "parameters": {
        "sendTo": "={{ $vars.HR_MANAGER_EMAIL }}",
        "subject": "=[ADA \u00a712111] Accommodation Request \u2014 Interactive Process Required",
        "message": "={{ 'ADA Accommodation Request Received\\n\\nEmployee: ' + $json.employee_name + '\\nEvent Type: ' + $json.event_type + '\\nDate Received: ' + $now.toISO() + '\\nInitial Response Deadline: ' + $json.adaDeadline + '\\n\\nRequired Actions (ADA \u00a712111 Interactive Process):\\n1. Acknowledge receipt within 5 business days\\n2. Schedule interactive process meeting with employee\\n3. Request medical documentation if needed (\u00a71630.2(o)(3))\\n4. Identify potential reasonable accommodations\\n5. Implement accommodation or document undue hardship analysis\\n\\nCaution: Failure to engage in good-faith interactive process exposes the company to ADA Title I liability even if no reasonable accommodation exists. Document ALL steps.' }}",
        "options": {}
      }
    },
    {
      "id": "n5",
      "name": "Schedule ADA Follow-Up",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [
        1050,
        150
      ],
      "parameters": {
        "amount": 10,
        "unit": "days"
      }
    },
    {
      "id": "n6",
      "name": "ADA Follow-Up Check",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        1250,
        150
      ],
      "parameters": {
        "channel": "#hr-ada",
        "text": "={{ '\u23f0 ADA FOLLOW-UP \u2014 ' + $json.employee_name + ' | Accommodation request received 10 days ago. Confirm: (1) Interactive process meeting completed, (2) Accommodation decision made and documented, (3) Response sent to employee. ADA \u00a712111 requires good-faith engagement.' }}"
      }
    },
    {
      "id": "n7",
      "name": "Slack #hr-legal \u2014 NLRA \u00a77 Protected Activity",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        850,
        450
      ],
      "parameters": {
        "channel": "#hr-legal",
        "text": "={{ '\u26a0\ufe0f NLRA \u00a77 PROTECTED ACTIVITY LOGGED \u2014 ' + $json.employee_name + ' | Event: ' + $json.event_type + ' | ' + $json.nlraWarning }}"
      }
    },
    {
      "id": "n8",
      "name": "Log All Events to HR Compliance Audit",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        1050,
        400
      ],
      "parameters": {
        "operation": "append",
        "documentId": "={{ $vars.HR_COMPLIANCE_SHEET_ID }}",
        "sheetName": "accommodation_activity_log",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "employee_id": "={{ $json.employee_id }}",
            "employee_name": "={{ $json.employee_name }}",
            "event_type": "={{ $json.event_type }}",
            "event_category": "={{ $json.isADA ? 'ADA' : $json.isNLRA ? 'NLRA' : 'OTHER' }}",
            "logged_at": "={{ $now.toISO() }}",
            "ada_deadline": "={{ $json.adaDeadline }}",
            "nlra_warning": "={{ $json.nlraWarning }}"
          }
        }
      }
    }
  ],
  "connections": {
    "Accommodation or Activity Report Webhook": {
      "main": [
        [
          {
            "node": "Classify ADA vs NLRA Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify ADA vs NLRA Event": {
      "main": [
        [
          {
            "node": "Switch: ADA or NLRA",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch: ADA or NLRA": {
      "main": [
        [
          {
            "node": "Gmail HR \u2014 ADA Interactive Process Required",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack #hr-legal \u2014 NLRA \u00a77 Protected Activity",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail HR \u2014 ADA Interactive Process Required": {
      "main": [
        [
          {
            "node": "Schedule ADA Follow-Up",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule ADA Follow-Up": {
      "main": [
        [
          {
            "node": "ADA Follow-Up Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack #hr-legal \u2014 NLRA \u00a77 Protected Activity": {
      "main": [
        [
          {
            "node": "Log All Events to HR Compliance Audit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}
Enter fullscreen mode Exit fullscreen mode

What it does: Webhook receives event (accommodation request, disability disclosure, protected concerted activity report) → classifies as ADA or NLRA event → ADA path: emails HR manager with specific §12111 interactive process checklist + schedules 10-day follow-up Slack reminder → NLRA path: logs to #hr-legal with §7 protection warning and 6-month SOL clock → all events logged to Google Sheets compliance audit trail.

Why log NLRA activity: The most common NLRB charge pattern is discipline/termination within weeks of protected activity (wage discussion, group complaint). Having a timestamped log that pre-dates any adverse action decision creates evidence of good-faith process — and forces management to see the legal context before taking action.


Self-Hosting vs Cloud iPaaS: The HRTech Compliance Argument

Data Type Cloud iPaaS Risk Self-Hosted n8n
FCRA consumer reports Vendor = third-party data recipient under §604 permissible purpose Data never leaves your AWS/Azure/GCP environment
I-9 immigration records DHS/ICE subpoena can reach cloud vendor records I-9 data stays inside your compliance perimeter
EEOC hiring analytics OFCCP AAP data in vendor logs = undocumented data sharing Audit-ready logs stay inside privilege boundary
FLSA payroll records DOL WHD subpoena reaches cloud vendor Pay records in self-hosted DB outside vendor reach
ADA medical documentation ADA §503/HIPAA crossover — third-party access prohibited PHI-adjacent data never transits external system
NLRA organizing communications NLRB discovery can subpoena vendor records Organizing activity logs stay inside legal boundary

Five Buyer Questions to Address in Your Sales Cycle

Q: Does your platform maintain FCRA §607 'reasonable procedures' for adverse action timing?
A: Yes — the FCRA pipeline enforces the 5-business-day pre-adverse-action window and logs every step for audit.

Q: Can your I-9 module handle List B re-verification prohibition?
A: Yes — the I-9 workflow explicitly alerts HR that only List A or C can be used for Section 3; re-verifying List B is a common ICE audit finding.

Q: Do you produce OFCCP-ready adverse impact analysis?
A: The EEOC monitor generates 80-rule analysis logs in the format OFCCP requests in Scheduling Letters — 2 years of job-category applicant flow with adverse impact ratios.

Q: Does your platform cover state FLSA threshold variations?
A: Yes — the FLSA classifier covers California ($1,040/wk), New York ($1,125/wk), Washington, Colorado, and Alaska salary basis thresholds automatically.

Q: How do you handle the NLRA protected activity logging requirement?
A: All §7 activity is logged with timestamps before any adverse action is taken — creating the good-faith evidence trail that negates most NLRB §8(a)(1) charges.


Get the Complete FlowKit n8n Template Pack

These 5 workflows are import-ready JSON. The full FlowKit template library includes 15 production-ready n8n workflows across compliance, ops, and revenue automation:

stripeai.gumroad.com — individual templates $12–$29 | complete bundle $97

Tags: n8n, hrtech, automation, compliance

Top comments (0)