DEV Community

Alex Kane
Alex Kane

Posted on

n8n for PropTech SaaS Vendors: 5 Automations for Fair Housing, RESPA, TRID, HMDA, and ADA Compliance (Free)

If you sell software to lenders, brokers, property managers, or real estate operators, your platform sits inside a compliance stack your customers can't afford to break.

Fair Housing Act (FHA) violations cost $22,000–$107,000 per finding. RESPA referral fee violations trigger treble damages under 12 USC §2607. A single missed TRID Loan Estimate timing window (12 CFR §1026.19(e)) can unwind a loan closing. HMDA LAR errors trigger CRA ratings impacts and civil money penalties.

Your customers are under regulatory scrutiny. If your platform misses a disclosure deadline or routes applicant PII through an unsecured pipeline, you're the vendor they name in the enforcement action.

n8n — self-hosted, JSON-native, and git-versioned — lets you build the compliance automation layer your customers require without sending fair housing audit trails through a cloud workflow platform your general counsel has never reviewed.

Here are five production-ready n8n workflows for PropTech SaaS vendors.


1. PropTech Customer Onboarding Drip with Compliance-Tier Segmentation

Real estate platforms serve radically different buyers. An enterprise mortgage lender under HMDA + TRID + RESPA has a completely different Day 0 checklist than a regional property manager under Fair Housing + ADA. Generic onboarding misses both.

This workflow segments by customer tier and compliance profile on intake:

What it does: Creates a compliance-segmented Day 0 → Day 3 → Day 7 email drip based on customer tier and regulatory flags. An ENTERPRISE_LENDER with TRID_COVERED_LOAN=true gets a TRID-specific Day 0 checklist; a REGIONAL_PROP_MANAGER with FAIR_HOUSING_REQUIRED=true gets Fair Housing audit trail setup instructions.

{
  "name": "PropTech Customer Onboarding Drip",
  "nodes": [
    {
      "name": "Google Sheets Trigger",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "parameters": {
        "sheetId": "YOUR_SHEET_ID",
        "range": "Customers!A:Z",
        "event": "rowAdded"
      }
    },
    {
      "name": "Set Customer Context",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "const row = $json;\nconst tier = row.tier || 'MIDMARKET_BROKER';\nconst flags = {\n  FAIR_HOUSING_REQUIRED: row.fair_housing_required === 'true',\n  RESPA_COVERED_TRANSACTION: row.respa_covered === 'true',\n  HMDA_LAR_REQUIRED: row.hmda_lar_required === 'true',\n  TRID_COVERED_LOAN: row.trid_covered === 'true',\n  ADA_ACCESSIBLE_HOUSING: row.ada_accessible === 'true',\n  FCRA_TENANT_SCREENING: row.fcra_tenant_screening === 'true',\n  SOC2_REQUIRED: row.soc2_required === 'true'\n};\nconst tierMap = {\n  ENTERPRISE_LENDER: { priority: 1, csm: row.enterprise_csm_email || 'csm@yourco.com', sla: '2h' },\n  MIDMARKET_BROKER: { priority: 2, csm: row.midmarket_csm_email || 'cs@yourco.com', sla: '8h' },\n  REGIONAL_PROP_MANAGER: { priority: 3, csm: row.regional_csm_email || 'support@yourco.com', sla: '24h' },\n  SMALL_LANDLORD: { priority: 4, csm: null, sla: 'self-serve' }\n};\nreturn [{\n  json: {\n    customer_id: row.customer_id,\n    company: row.company_name,\n    email: row.contact_email,\n    tier,\n    ...tierMap[tier] || tierMap.MIDMARKET_BROKER,\n    flags,\n    enrolled_at: new Date().toISOString()\n  }\n}];"
      }
    },
    {
      "name": "Gmail Day 0 Welcome",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "to": "={{ $json.email }}",
        "subject": "Welcome to [YourPlatform] \u2014 your compliance setup checklist",
        "message": "={{ 'Hi ' + $json.company + ',\\n\\nWelcome aboard. Based on your profile, here is your Day 0 compliance checklist:\\n\\n' + ($json.flags.TRID_COVERED_LOAN ? '\u2713 TRID: Enable Loan Estimate 3-business-day timer in Settings > Disclosures\\n' : '') + ($json.flags.HMDA_LAR_REQUIRED ? '\u2713 HMDA: Connect your LAR export endpoint in Settings > Reporting\\n' : '') + ($json.flags.RESPA_COVERED_TRANSACTION ? '\u2713 RESPA: Review referral fee prohibition checklist in Compliance > RESPA\\n' : '') + ($json.flags.FAIR_HOUSING_REQUIRED ? '\u2713 Fair Housing: Enable protected class anonymization in Settings > Screening\\n' : '') + ($json.flags.FCRA_TENANT_SCREENING: '\u2713 FCRA: Verify adverse action notice templates in Compliance > Screening\\n' : '') + '\\nYour CSM will reach out within ' + $json.sla + '.\\n\\n\u2014 The [YourPlatform] Team' }}"
      }
    },
    {
      "name": "Slack Notify CSM",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#{{ $json.tier === 'ENTERPRISE_LENDER' ? 'cs-enterprise' : 'cs-general' }}",
        "text": "New customer: {{ $json.company }} ({{ $json.tier }}) \u2014 Flags: {{ Object.entries($json.flags).filter(([k,v])=>v).map(([k])=>k).join(', ') || 'none' }}. Contact: {{ $json.email }}. SLA: {{ $json.sla }}."
      }
    },
    {
      "name": "Wait 3 Days",
      "type": "n8n-nodes-base.wait",
      "parameters": {
        "amount": 3,
        "unit": "days"
      }
    },
    {
      "name": "Gmail Day 3 Integration Tips",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "to": "={{ $json.email }}",
        "subject": "Day 3: Connecting your data sources",
        "message": "={{ 'Hi ' + $json.company + ',\\n\\nDay 3 tip: connect your ' + ($json.flags.TRID_COVERED_LOAN ? 'loan origination system (LOS) to auto-populate TRID timestamps' : $json.flags.HMDA_LAR_REQUIRED ? 'LAR export API to automate HMDA filing' : 'property management system for Fair Housing audit trail') + '.\\n\\nGuide: help.yourco.com/integrations\\n\\n\u2014 [YourPlatform] Team' }}"
      }
    },
    {
      "name": "Wait 4 Days",
      "type": "n8n-nodes-base.wait",
      "parameters": {
        "amount": 4,
        "unit": "days"
      }
    },
    {
      "name": "Gmail Day 7 Compliance Walkthrough Invite",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "to": "={{ $json.email }}",
        "subject": "Book your 30-min compliance configuration walkthrough",
        "message": "={{ 'Hi ' + $json.company + ',\\n\\nWeek 1 done. Let us walk through your compliance configuration \u2014 especially ' + ($json.flags.TRID_COVERED_LOAN ? 'your TRID timing alerts' : $json.flags.FAIR_HOUSING_REQUIRED ? 'your Fair Housing audit trail' : 'your disclosure pipeline') + '.\\n\\nBook here: calendly.yourco.com/compliance-walkthrough\\n\\n\u2014 [YourPlatform] Team' }}"
      }
    },
    {
      "name": "Log to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "parameters": {
        "operation": "append",
        "sheetId": "YOUR_SHEET_ID",
        "range": "OnboardingLog!A:Z",
        "data": "={{ { customer_id: $json.customer_id, company: $json.company, tier: $json.tier, enrolled_at: $json.enrolled_at, day7_sent: new Date().toISOString() } }}"
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

2. PropTech API & Disclosure Health Monitor

When your fair housing screening API goes down, your customers' applicants don't get protected class anonymization — that's a HUD §3604 risk in every transaction processed during the outage. When your TRID calculation endpoint fails, the Loan Estimate 3-business-day clock keeps running whether your platform knows about it or not.

Five-minute polling, compliance annotation on every alert:

{
  "name": "PropTech API Health Monitor",
  "nodes": [
    {
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 5
            }
          ]
        }
      }
    },
    {
      "name": "Define Endpoints",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "return [\n  { json: { name: 'fair_housing_api', url: 'https://api.yourco.com/health/fair-housing', annotation: 'DOWN = HUD 24 CFR Part 100 disclosure risk \u2014 \u00a73604 protected class screening offline' } },\n  { json: { name: 'respa_disclosure_api', url: 'https://api.yourco.com/health/respa', annotation: 'DOWN = 12 CFR \u00a71024.8 GFE disclosure pipeline offline \u2014 RESPA \u00a78 treble damage exposure' } },\n  { json: { name: 'hmda_reporting_api', url: 'https://api.yourco.com/health/hmda', annotation: 'DOWN = HMDA 12 CFR Part 1003 LAR submission pipeline offline' } },\n  { json: { name: 'trid_calculation_api', url: 'https://api.yourco.com/health/trid', annotation: 'DOWN = TRID 12 CFR \u00a71026.19(e) Loan Estimate 3-business-day timer offline' } },\n  { json: { name: 'tenant_screening_api', url: 'https://api.yourco.com/health/fcra-screening', annotation: 'DOWN = FCRA 15 USC \u00a71681 adverse action notice pipeline offline' } },\n  { json: { name: 'ada_accessibility_api', url: 'https://api.yourco.com/health/ada', annotation: 'DOWN = ADA Title III 28 CFR \u00a736.302 accessible housing feature check offline' } }\n];"
      }
    },
    {
      "name": "HTTP Health Check",
      "type": "n8n-nodes-base.httpRequest",
      "parameters": {
        "url": "={{ $json.url }}",
        "method": "GET",
        "timeout": 10000,
        "continueOnFail": true
      }
    },
    {
      "name": "Evaluate Status",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "const status = $json.statusCode || $input.first().error?.cause?.code || 'UNKNOWN';\nconst endpoint = $('Define Endpoints').item.json;\nconst isDown = typeof status !== 'number' || status >= 400;\nconst isStale = status === 200 && $json.body && JSON.parse($json.body || '{}').last_updated &&\n  (Date.now() - new Date(JSON.parse($json.body).last_updated).getTime()) > 300000;\nreturn [{\n  json: {\n    name: endpoint.name,\n    url: endpoint.url,\n    annotation: endpoint.annotation,\n    status: isDown ? 'DOWN' : isStale ? 'STALE' : 'OK',\n    http_status: status,\n    checked_at: new Date().toISOString()\n  }\n}];"
      }
    },
    {
      "name": "Filter Non-OK",
      "type": "n8n-nodes-base.filter",
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.status }}",
              "operation": "notEqual",
              "value2": "OK"
            }
          ]
        }
      }
    },
    {
      "name": "Slack Alert",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#proptech-ops-alerts",
        "text": ":red_circle: PropTech API *{{ $json.status }}*: `{{ $json.name }}` (HTTP {{ $json.http_status }})\n> {{ $json.annotation }}\nChecked: {{ $json.checked_at }}"
      }
    },
    {
      "name": "Log to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "parameters": {
        "operation": "append",
        "sheetId": "YOUR_SHEET_ID",
        "range": "HealthLog!A:F",
        "data": "={{ { name: $json.name, status: $json.status, http_status: $json.http_status, annotation: $json.annotation, checked_at: $json.checked_at } }}"
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

3. PropTech Compliance Deadline Tracker

HMDA LAR submissions are due March 1. TRID Closing Disclosure windows are 3 business days — not calendar days. State rent control ordinances renew annually on different schedules by jurisdiction. Miss the HMDA LAR deadline and your customers face FFIEC examination findings that affect their CRA ratings.

Twelve deadline types, weekday morning routing, dedup on alert_sent_date:

{
  "name": "PropTech Compliance Deadline Tracker",
  "nodes": [
    {
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1-5"
            }
          ]
        }
      }
    },
    {
      "name": "Read Deadline Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "parameters": {
        "operation": "readRows",
        "sheetId": "YOUR_SHEET_ID",
        "range": "PropTechDeadlines!A:G"
      }
    },
    {
      "name": "Classify Urgency",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "const deadlineTypes = {\n  TRID_LOAN_ESTIMATE_3DAY: 'TRID 12 CFR \u00a71026.19(e) \u2014 Loan Estimate 3-business-day delivery window',\n  TRID_CLOSING_DISCLOSURE_3DAY: 'TRID 12 CFR \u00a71026.19(f) \u2014 Closing Disclosure 3-business-day window',\n  HMDA_LAR_ANNUAL: 'HMDA 12 CFR Part 1003 \u2014 Annual LAR submission to FFIEC (March 1)',\n  RESPA_GFE_REFORM: 'RESPA 12 CFR \u00a71024.8 \u2014 Annual GFE/disclosure template review',\n  FAIR_HOUSING_COMPLAINT_RESPONSE: 'FHA 42 USC \u00a73610 \u2014 HUD complaint response SLA review',\n  ADA_ACCESSIBLE_DESIGN_ANNUAL: 'ADA Title III 28 CFR \u00a736.302 \u2014 Annual accessible design audit',\n  FCRA_ADVERSE_ACTION_NOTICE: 'FCRA 15 USC \u00a71681m \u2014 Adverse action notice template annual review',\n  STATE_RENT_CONTROL_ANNUAL: 'State Rent Control \u2014 Annual rent control ordinance compliance review',\n  FAIR_CREDIT_REPORTING_ANNUAL: 'FCRA 15 USC \u00a71681 \u2014 Annual consumer reporting compliance review',\n  SOC2_TYPE2_RENEWAL: 'SOC 2 Type II \u2014 Annual audit renewal',\n  ISO27001_SURVEILLANCE: 'ISO 27001 \u2014 Annual surveillance audit',\n  CCPA_CONSUMER_PRIVACY_NOTICE: 'CCPA Cal. Civ. Code \u00a71798.100 \u2014 Annual privacy notice update'\n};\nconst results = [];\nconst today = new Date();\nfor (const row of $input.all()) {\n  const d = row.json;\n  if (!d.deadline_date || !d.type) continue;\n  const deadline = new Date(d.deadline_date);\n  const daysLeft = Math.round((deadline - today) / 86400000);\n  let urgency, channel, ccEmail;\n  if (daysLeft < 0) { urgency = 'OVERDUE'; channel = '#compliance-critical'; ccEmail = d.legal_email; }\n  else if (daysLeft <= 7) { urgency = 'CRITICAL'; channel = '#compliance-critical'; ccEmail = d.legal_email; }\n  else if (daysLeft <= 21) { urgency = 'URGENT'; channel = '#compliance-team'; ccEmail = null; }\n  else if (daysLeft <= 45) { urgency = 'WARNING'; channel = '#compliance-team'; ccEmail = null; }\n  else if (daysLeft <= 90) { urgency = 'NOTICE'; channel = '#compliance-team'; ccEmail = null; }\n  else continue;\n  const dedup_key = d.type + '_' + d.deadline_date;\n  if (d.alert_sent_date === new Date().toISOString().slice(0, 10)) continue;\n  results.push({ json: { ...d, daysLeft, urgency, channel, ccEmail, description: deadlineTypes[d.type] || d.type, dedup_key } });\n}\nreturn results;"
      }
    },
    {
      "name": "Slack Alert",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "={{ $json.channel }}",
        "text": "{{ $json.urgency === 'OVERDUE' ? ':rotating_light:' : $json.urgency === 'CRITICAL' ? ':red_circle:' : ':warning:' }} PropTech Compliance *{{ $json.urgency }}*: `{{ $json.type }}`\n> {{ $json.description }}\nDue: {{ $json.deadline_date }} ({{ $json.daysLeft < 0 ? Math.abs($json.daysLeft) + ' days OVERDUE' : $json.daysLeft + ' days left' }})\nOwner: {{ $json.owner_email }}"
      }
    },
    {
      "name": "Gmail Owner",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "to": "={{ $json.owner_email }}",
        "cc": "={{ $json.ccEmail || '' }}",
        "subject": "[{{ $json.urgency }}] PropTech Compliance Deadline: {{ $json.type }} \u2014 {{ $json.daysLeft < 0 ? Math.abs($json.daysLeft) + ' days overdue' : $json.daysLeft + ' days remaining' }}",
        "message": "={{ 'Compliance Deadline Alert\\n\\nType: ' + $json.type + '\\nRegulation: ' + $json.description + '\\nDeadline: ' + $json.deadline_date + '\\nStatus: ' + $json.urgency + ' (' + ($json.daysLeft < 0 ? Math.abs($json.daysLeft) + ' days overdue' : $json.daysLeft + ' days remaining') + ')\\n\\nAction required. Contact compliance@yourco.com with questions.' }}"
      }
    },
    {
      "name": "Mark Alert Sent",
      "type": "n8n-nodes-base.googleSheets",
      "parameters": {
        "operation": "update",
        "sheetId": "YOUR_SHEET_ID",
        "range": "PropTechDeadlines!A:G",
        "data": "={{ { row: $json.row_number, alert_sent_date: new Date().toISOString().slice(0, 10) } }}"
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

4. Fair Housing & RESPA Compliance Incident Alert Pipeline

When a fair housing discrimination complaint arrives, HUD requires a substantive response within 10 days of complaint service (42 USC §3610). When a RESPA kickback allegation is filed, your legal team needs the referral arrangement records immediately — treble damages under 12 USC §2607 make this the most expensive enforcement exposure in residential real estate.

Eight incident types with SLA clocks and Postgres audit trail:

{
  "name": "Fair Housing & RESPA Incident Alert Pipeline",
  "nodes": [
    {
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "parameters": {
        "path": "proptech-compliance-incident",
        "method": "POST"
      }
    },
    {
      "name": "Respond Immediately",
      "type": "n8n-nodes-base.respondToWebhook",
      "parameters": {
        "responseCode": 200,
        "responseData": "{\"received\": true}"
      }
    },
    {
      "name": "Classify Incident",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "const incident = $json;\nconst incidentMap = {\n  FAIR_HOUSING_DISCRIMINATION_COMPLAINT: {\n    severity: 'CRITICAL',\n    slaHours: 24,\n    regulation: 'FHA 42 USC \u00a73610 \u2014 HUD complaint response window',\n    channel: '#compliance-critical',\n    recipients: ['cco@yourco.com', 'legal@yourco.com'],\n    notes: 'HUD requires substantive response within 10 days of complaint service. Flag for outside counsel immediately.'\n  },\n  RESPA_KICKBACK_ALLEGATION: {\n    severity: 'HIGH',\n    slaHours: 72,\n    regulation: 'RESPA 12 USC \u00a72607 \u2014 referral fee prohibition, treble damages',\n    channel: '#compliance-critical',\n    recipients: ['cco@yourco.com', 'legal@yourco.com'],\n    notes: 'RESPA \u00a78 violations carry treble actual damages. Preserve all referral arrangement records.'\n  },\n  HMDA_LAR_DISCREPANCY: {\n    severity: 'HIGH',\n    slaHours: 48,\n    regulation: 'HMDA 12 CFR Part 1003 \u2014 LAR accuracy requirement',\n    channel: '#compliance-team',\n    recipients: ['compliance@yourco.com', 'cco@yourco.com'],\n    notes: 'FFIEC examination triggers CRA rating impact. Remediate before LAR submission window.'\n  },\n  TRID_TIMING_VIOLATION: {\n    severity: 'HIGH',\n    slaHours: 24,\n    regulation: 'TRID 12 CFR \u00a71026.19(e)/(f) \u2014 LE/CD 3-business-day delivery',\n    channel: '#compliance-team',\n    recipients: ['compliance@yourco.com', 'ops@yourco.com'],\n    notes: 'TRID timing violations can void loan closing. Determine cure option under \u00a71026.19(e)(3)(i).'\n  },\n  FCRA_ADVERSE_ACTION_FAILURE: {\n    severity: 'HIGH',\n    slaHours: 24,\n    regulation: 'FCRA 15 USC \u00a71681m \u2014 adverse action notice required within reasonable time',\n    channel: '#compliance-team',\n    recipients: ['compliance@yourco.com', 'legal@yourco.com'],\n    notes: 'Failure to send adverse action notice: up to $1,000 statutory damages per violation.'\n  },\n  ADA_ACCESSIBILITY_VIOLATION: {\n    severity: 'MEDIUM',\n    slaHours: 72,\n    regulation: 'ADA Title III 28 CFR \u00a736.302 \u2014 accessible housing features',\n    channel: '#compliance-team',\n    recipients: ['compliance@yourco.com', 'product@yourco.com'],\n    notes: 'Document remediation plan. ADA DOJ enforcement includes injunctive relief and civil penalties.'\n  },\n  STATE_RENT_CONTROL_VIOLATION: {\n    severity: 'MEDIUM',\n    slaHours: 48,\n    regulation: 'State Rent Control \u2014 jurisdiction-specific ordinance',\n    channel: '#compliance-team',\n    recipients: ['compliance@yourco.com', 'legal@yourco.com'],\n    notes: 'CA AB 1482, NYC Rent Guidelines Board, NJ NJSA 2A:42-84.1 \u2014 verify applicable ordinance.'\n  },\n  FAIR_HOUSING_AUDIT_INITIATED: {\n    severity: 'CRITICAL',\n    slaHours: 4,\n    regulation: 'FHA 42 USC \u00a73610 \u2014 HUD/DOJ audit \u2014 preserve all records immediately',\n    channel: '#compliance-critical',\n    recipients: ['ceo@yourco.com', 'cco@yourco.com', 'legal@yourco.com'],\n    notes: 'Litigation hold required immediately. Do not delete any communications or screening records.'\n  }\n};\nconst type = incident.incident_type || 'UNKNOWN';\nconst meta = incidentMap[type] || { severity: 'MEDIUM', slaHours: 72, regulation: 'Review required', channel: '#compliance-team', recipients: ['compliance@yourco.com'], notes: '' };\nconst sla_deadline = new Date(Date.now() + meta.slaHours * 3600000).toISOString();\nreturn [{ json: { ...incident, ...meta, type, sla_deadline, received_at: new Date().toISOString() } }];"
      }
    },
    {
      "name": "Slack Incident Alert",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "={{ $json.channel }}",
        "text": "{{ $json.severity === 'CRITICAL' ? ':rotating_light: CRITICAL' : ':warning: HIGH' }} PropTech Compliance Incident: `{{ $json.type }}`\n> Regulation: {{ $json.regulation }}\n> SLA: {{ $json.sla_deadline }}\n> Notes: {{ $json.notes }}\nCustomer: {{ $json.customer_id || 'platform-wide' }}"
      }
    },
    {
      "name": "Gmail Compliance Team",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "to": "={{ $json.recipients.join(',') }}",
        "subject": "[{{ $json.severity }}] PropTech Compliance Incident: {{ $json.type }} \u2014 SLA {{ $json.sla_deadline }}",
        "message": "={{ 'PropTech Compliance Incident Alert\\n\\nType: ' + $json.type + '\\nSeverity: ' + $json.severity + '\\nRegulation: ' + $json.regulation + '\\nSLA Deadline: ' + $json.sla_deadline + '\\nReceived: ' + $json.received_at + '\\n\\nNotes: ' + $json.notes + '\\n\\nCustomer: ' + ($json.customer_id || 'platform-wide') + '\\nDetails: ' + JSON.stringify($json.incident_details || {}) }}"
      }
    },
    {
      "name": "Postgres Audit Log",
      "type": "n8n-nodes-base.postgres",
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO proptech_compliance_incidents (type, severity, regulation, sla_deadline, customer_id, received_at, notes, raw_payload) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
        "additionalFields": {
          "queryParams": "={{ [$json.type, $json.severity, $json.regulation, $json.sla_deadline, $json.customer_id || null, $json.received_at, $json.notes, JSON.stringify($json)] }}"
        }
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

5. Weekly PropTech Platform KPI Dashboard

Your CEO needs to see MRR alongside compliance exposure — a $50K MRR week with three TRID violations open is a different risk picture than a $50K week with zero. BCC General Counsel and CISO ensures governance accountability doesn't depend on the weekly meeting.

{
  "name": "Weekly PropTech Platform KPI Dashboard",
  "nodes": [
    {
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1"
            }
          ]
        }
      }
    },
    {
      "name": "Query Platform Metrics",
      "type": "n8n-nodes-base.postgres",
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT\n  COUNT(DISTINCT c.id) AS total_active_customers,\n  SUM(c.mrr_usd) AS total_mrr_usd,\n  COUNT(DISTINCT CASE WHEN c.tier = 'ENTERPRISE_LENDER' THEN c.id END) AS enterprise_lender_count,\n  COUNT(DISTINCT CASE WHEN c.tier = 'MIDMARKET_BROKER' THEN c.id END) AS midmarket_broker_count,\n  SUM(c.trid_disclosures_sent_7d) AS trid_disclosures_7d,\n  SUM(c.hmda_lar_records_7d) AS hmda_lar_records_7d,\n  COUNT(DISTINCT ci.id) FILTER (WHERE ci.type = 'FAIR_HOUSING_DISCRIMINATION_COMPLAINT' AND ci.resolved_at IS NULL) AS fair_housing_open,\n  COUNT(DISTINCT ci.id) FILTER (WHERE ci.type = 'TRID_TIMING_VIOLATION' AND ci.resolved_at IS NULL) AS trid_violations_open,\n  COUNT(DISTINCT ci.id) FILTER (WHERE ci.type = 'HMDA_LAR_DISCREPANCY' AND ci.resolved_at IS NULL) AS hmda_errors_open,\n  COUNT(DISTINCT ci.id) FILTER (WHERE ci.type = 'RESPA_KICKBACK_ALLEGATION' AND ci.resolved_at IS NULL) AS respa_incidents_open\nFROM customers c\nLEFT JOIN proptech_compliance_incidents ci ON ci.created_at >= NOW() - INTERVAL '30 days'\nWHERE c.status = 'active'"
      }
    },
    {
      "name": "Build HTML Report",
      "type": "n8n-nodes-base.code",
      "parameters": {
        "jsCode": "const m = $input.first().json;\nconst mrrK = (m.total_mrr_usd / 1000).toFixed(1);\nconst fhFlag = m.fair_housing_open > 0 ? ' \u26a0\ufe0f OPEN' : ' \u2713';\nconst tridFlag = m.trid_violations_open > 0 ? ' \u26a0\ufe0f OPEN' : ' \u2713';\nconst html = `<h2>Weekly PropTech Platform KPI \u2014 ${new Date().toDateString()}</h2>\n<table border=\"1\" cellpadding=\"6\" style=\"border-collapse:collapse\">\n<tr><th>Metric</th><th>Value</th><th>Status</th></tr>\n<tr><td>Total MRR</td><td>$${mrrK}K</td><td>\u2014</td></tr>\n<tr><td>Active Customers</td><td>${m.total_active_customers}</td><td>\u2014</td></tr>\n<tr><td>Enterprise Lenders</td><td>${m.enterprise_lender_count}</td><td>\u2014</td></tr>\n<tr><td>Midmarket Brokers</td><td>${m.midmarket_broker_count}</td><td>\u2014</td></tr>\n<tr><td>TRID Disclosures (7d)</td><td>${m.trid_disclosures_7d}</td><td>\u2014</td></tr>\n<tr><td>HMDA LAR Records (7d)</td><td>${m.hmda_lar_records_7d}</td><td>\u2014</td></tr>\n<tr><td><b>Fair Housing Incidents Open</b></td><td>${m.fair_housing_open}</td><td>${fhFlag}</td></tr>\n<tr><td><b>TRID Violations Open</b></td><td>${m.trid_violations_open}</td><td>${tridFlag}</td></tr>\n<tr><td>HMDA Errors Open</td><td>${m.hmda_errors_open}</td><td>${m.hmda_errors_open > 0 ? '\u26a0\ufe0f' : '\u2713'}</td></tr>\n<tr><td>RESPA Incidents Open</td><td>${m.respa_incidents_open}</td><td>${m.respa_incidents_open > 0 ? '\u26a0\ufe0f' : '\u2713'}</td></tr>\n</table>`;\nreturn [{ json: { html, metrics: m, mrrK } }];"
      }
    },
    {
      "name": "Gmail KPI Report",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "to": "ceo@yourco.com",
        "cc": "cco@yourco.com",
        "bcc": "legal@yourco.com,ciso@yourco.com",
        "subject": "Weekly PropTech KPI \u2014 ${{ $json.mrrK }}K MRR | FH:{{ $json.metrics.fair_housing_open }} TRID:{{ $json.metrics.trid_violations_open }} HMDA:{{ $json.metrics.hmda_errors_open }} RESPA:{{ $json.metrics.respa_incidents_open }}",
        "message": "={{ $json.html }}",
        "additionalFields": {
          "bodyContentType": "html"
        }
      }
    },
    {
      "name": "Slack One-Liner",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#management",
        "text": "Weekly PropTech KPI: ${{ $json.mrrK }}K MRR | {{ $json.metrics.total_active_customers }} customers | [FH COMPLAINT OPEN {{ $json.metrics.fair_housing_open }}] [TRID VIOLATION OPEN {{ $json.metrics.trid_violations_open }}] [HMDA ERRORS {{ $json.metrics.hmda_errors_open }}] [RESPA OPEN {{ $json.metrics.respa_incidents_open }}]"
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Why PropTech vendors self-host n8n

The fair housing audit trail problem. When a HUD investigator issues a subpoena following a §3604 discrimination complaint, your legal team needs to produce a complete chain of custody for every applicant screening record. If your disclosure automation runs on Zapier or Make, your applicant PII — including income, credit tier, employment status, and protected class proxies like zip code — sits in Zapier's execution logs. Your general counsel cannot audit those logs, cannot guarantee their preservation, and cannot certify their integrity to a federal investigator.

Self-hosted n8n keeps applicant data inside your VPC. Your execution logs are in your Postgres database. Your legal team can run a query.

TRID timing is not approximate. The 3-business-day Loan Estimate delivery requirement under 12 CFR §1026.19(e) has no "good faith" exception for system outages. If your TRID calculation workflow is hosted on a SaaS automation platform that goes down during a closing window, that's the lender's problem — and your platform's vendor relationship problem. Self-hosted n8n running in your infrastructure under your SLA gives you control over uptime.

HMDA LAR accuracy is examiner-graded. FFIEC examiners review LAR files for accuracy during CRA examinations. If your HMDA reporting pipeline processes loan records through a cloud iPaaS with data residency outside the US, that's a data governance finding before the examiner even looks at the data. Self-hosted n8n keeps loan demographic data in your environment.

SOC2 CC9.2 subprocessor scope. Every SaaS automation platform you add to your disclosure or screening pipeline is a new subprocessor in your SOC2 report and your enterprise customers' vendor risk assessments. A lender's IT security team will ask: "Does your workflow automation platform have a SOC2 Type II?" Self-hosted n8n eliminates the question.


Get the complete FlowKit template pack

All five workflows above are production-ready starting points. For the complete library of 15 n8n workflow templates — covering onboarding drips, compliance trackers, incident pipelines, health monitors, and KPI dashboards across multiple verticals:

FlowKit on Gumroad — 15 n8n Automation Templates

Individual templates from $12. Bundle (all 15) at $97.


All workflows are import-ready JSON. Paste into your n8n instance and configure your credentials. No lock-in, no recurring fees beyond your n8n hosting.

Top comments (0)