Occupancy Fraud Signal Detection Failures
Issue: AI System Fails to Detect False Owner-Occupancy Claims
Frequency: Common
Symptoms
- Borrower claims primary residence but indicators suggest investment
- Address history shows pattern of property flipping
- Existing primary residence not being sold
- Property location inconsistent with employment
- Borrower already owns multiple properties
- Rental listings appearing shortly after closing
- Insurance type doesn’t match occupancy claim
Root Cause Owner-occupied loans receive better rates and terms than investment properties. Fraudsters claim primary residence intent when planning to rent or flip. AI systems verify occupancy claims on paper but miss behavioral and contextual signals that indicate investment intent. Post-closing, properties are often listed for rent within months.
Example
Scenario 1: Existing primary not being sold
Application claims:
- Property: Primary residence
- Current residence: "Will be sold"
AI verification:
- Occupancy field: "Primary" ✓
- Property type: Single family ✓
- Intent to occupy letter: Signed ✓
Reality check (not performed):
- Current primary residence: No listing found
- Current mortgage: No payoff planned
- Two primary residences not possible
← Will rent new property as investment
← Current home kept as actual residence
← AI didn't verify sale plan
---
Scenario 2: Geographic impossibility
Application data:
- Employer: Downtown Chicago
- Work type: Full-time, on-site
- Subject property: Miami, FL (1,300 miles)
- Occupancy: Primary residence
AI verification:
- Employment: Verified ✓
- Property: Appraised ✓
- Occupancy claim: Documented ✓
Occupancy signals missed:
- 1,300-mile commute impossible
- No job transfer documentation
- No remote work arrangement
- No logical occupancy path
← Investment property at primary rates
← Geographic analysis not performed
---
Scenario 3: Property flipping pattern
Borrower history:
- Property 1 (2023): Purchased as "primary," sold 8 months later
- Property 2 (2024): Purchased as "primary," sold 6 months later
- Property 3 (2025): Current application, "primary residence"
AI verification:
- Current application: Verified ✓
- Credit report: Reviewed ✓
- Property: Appraised ✓
Pattern missed:
- Serial "primary residence" claims
- Short hold periods indicate flipping
- No actual occupancy
← Third fraudulent occupancy claim
← Pattern visible in credit history
← No historical occupancy analysis
---
Scenario 4: Post-closing rental listing
Application:
- Occupancy: Primary residence
- Closed: March 2026
Post-closing discovery (AI not monitoring):
- Zillow rental listing: April 2026
- Listed rent: $2,400/month
- Property photos match closing docs
← Never intended to occupy
← AI only verified at application
← No post-closing monitoring
---
Occupancy fraud indicators:
Pre-closing signals:
Multiple owned properties: 30%
Geographic mismatch: 25%
Existing primary not sold: 20%
Property type mismatch: 15%
Income/property ratio: 10%
Post-closing signals:
Rental listing within 12 months: 40%
Insurance change: 30%
Address change: 20%
Utility transfer: 10%
Detection rates:
Application-only review: 15-25%
With behavioral analysis: 40-55%
With post-closing monitoring: 70-85%
Key Statistics From Occupancy Fraud Research (2025-2026):
- Occupancy misrepresentation: 10-15% of loans
- Rate difference (primary vs. investment): 0.5-0.75%
- Average fraud savings to borrower: $50-$150/month
- Investor losses when discovered: Varies (rep & warrant claims)
Contributing Factors
- Self-certification of occupancy
- No post-closing verification
- Geographic analysis not standard
- Historical pattern review limited
- Rental listings not monitored
- Rate incentive for fraud
Mitigation Strategies
Prevention
- Geographic plausibility: Check property/employment distance
- Historical pattern review: Analyze prior occupancy claims
- Existing property analysis: Verify sale/disposition plans
- Post-closing monitoring: Check rental listings, address changes
- Insurance verification: Confirm owner-occupied coverage
- Utility monitoring: Verify borrower-named utilities
Implementation
class OccupancyFraudDetector:
"""Detect occupancy fraud indicators"""
MAX_COMMUTE_MILES = 75
MIN_HOLD_PERIOD_MONTHS = 12
def analyze_occupancy(self, application: dict) -> dict:
"""Analyze application for occupancy fraud indicators"""
indicators = []
risk_score = 0
# Geographic plausibility
geo_result = self.check_geographic_plausibility(application)
if geo_result["implausible"]:
indicators.append(geo_result)
risk_score += 0.35
# Existing property disposition
existing_result = self.check_existing_properties(application)
if existing_result["suspicious"]:
indicators.append(existing_result)
risk_score += 0.30
# Historical pattern
history_result = self.check_occupancy_history(application)
if history_result["pattern_detected"]:
indicators.append(history_result)
risk_score += 0.40
# Property/income fit
fit_result = self.check_property_income_fit(application)
if fit_result["mismatch"]:
indicators.append(fit_result)
risk_score += 0.20
return {
"occupancy_fraud_risk": min(risk_score, 1.0),
"claimed_occupancy": application.get("occupancy_type"),
"indicators": indicators,
"recommendation": self.get_recommendation(risk_score)
}
def check_geographic_plausibility(self, application: dict) -> dict:
"""Check if occupancy is geographically plausible"""
if application.get("occupancy_type") != "primary_residence":
return {"implausible": False}
property_loc = application.get("property_address")
employer_loc = application.get("employer_address")
work_type = application.get("work_type", "on_site")
if not property_loc or not employer_loc:
return {"implausible": False, "note": "Missing location data"}
distance = self.calculate_distance(property_loc, employer_loc)
if distance > self.MAX_COMMUTE_MILES and work_type == "on_site":
return {
"indicator": "geographic_implausibility",
"implausible": True,
"distance_miles": distance,
"work_type": work_type,
"issue": f"Property {distance} miles from on-site job",
"risk": "high"
}
return {"implausible": False}
def check_existing_properties(self, application: dict) -> dict:
"""Check disposition plan for existing primary residence"""
# Get borrower's existing properties
credit_report = application.get("credit_report", {})
existing_mortgages = credit_report.get("mortgages", [])
current_primary = None
for mortgage in existing_mortgages:
if mortgage.get("property_type") == "primary_residence":
current_primary = mortgage
break
if not current_primary:
return {"suspicious": False}
# Check if there's a disposition plan
disposition = application.get("existing_property_disposition")
if not disposition or disposition == "retain":
return {
"indicator": "existing_primary_retention",
"suspicious": True,
"current_property": current_primary.get("address"),
"issue": "Retaining existing primary while claiming new primary",
"risk": "high"
}
if disposition == "sell":
# Verify listing exists
listing = self.check_for_listing(current_primary.get("address"))
if not listing:
return {
"indicator": "no_sale_listing",
"suspicious": True,
"issue": "Claims to sell but no listing found",
"risk": "medium"
}
return {"suspicious": False}
def check_occupancy_history(self, application: dict) -> dict:
"""Check for pattern of short-term primary residence claims"""
credit_report = application.get("credit_report", {})
mortgage_history = credit_report.get("mortgage_history", [])
short_holds = []
for mortgage in mortgage_history:
if mortgage.get("original_occupancy") == "primary_residence":
hold_months = mortgage.get("months_held", 0)
if hold_months < self.MIN_HOLD_PERIOD_MONTHS:
short_holds.append({
"property": mortgage.get("address"),
"months_held": hold_months,
"disposition": mortgage.get("disposition")
})
if len(short_holds) >= 2:
return {
"indicator": "occupancy_pattern",
"pattern_detected": True,
"short_hold_count": len(short_holds),
"properties": short_holds,
"issue": "Pattern of short-term primary residence claims",
"risk": "high"
}
return {"pattern_detected": False}
def schedule_post_closing_monitoring(self, loan: dict) -> dict:
"""Schedule post-closing occupancy monitoring"""
monitoring_schedule = {
"loan_id": loan["id"],
"property_address": loan["property_address"],
"checks": [
{
"type": "rental_listing_scan",
"frequency": "monthly",
"duration_months": 12,
"sources": ["zillow", "apartments.com", "craigslist"]
},
{
"type": "address_change_monitor",
"frequency": "quarterly",
"duration_months": 24
},
{
"type": "insurance_verification",
"frequency": "annually",
"check": "owner_occupied_coverage"
}
]
}
return monitoring_schedule