← back to Commercialrealestate
scripts/crcp-leads.js
45 lines
// crcp-leads.js — P2 assumable-loan LEAD ENGINE (docs/TOOL-SPEC.md). The Pro-tier money feature for a
// loan officer: rank FHA-financed multifamily by ASSUMABLE ADVANTAGE (how far the loan's rate sits below
// the market rate at origination — a buyer can assume that below-market note). 100% HUD public data, $0.
// Mount from serve.js AFTER accounts: require('./crcp-leads')(app, ROOT, acct.userOf);
'use strict';
const fs = require('fs');
const path = require('path');
module.exports = function mountLeads(app, ROOT, userOf) {
const FHA = path.join(ROOT, 'data', 'fha-loans.json');
const load = () => { try { return JSON.parse(fs.readFileSync(FHA, 'utf8')).rows || []; } catch { return []; } };
// GET /api/leads/assumable?state=CA&city=&limit=100&minSpread=1
// Ranked assumable-loan targets. Gated to signed-in users (it's the Pro feature); free tier gets a taste.
app.get('/api/leads/assumable', (req, res) => {
const u = userOf && userOf(req);
const rows = load();
const state = (req.query.state || 'CA').toUpperCase();
const city = (req.query.city || '').toUpperCase();
const minSpread = req.query.minSpread != null ? +req.query.minSpread : 0.5;
let leads = rows
.filter(r => r.rate != null && r.pmmsBenchmark != null && (!state || r.state === state) && (!city || String(r.city || '').toUpperCase() === city))
.map(r => ({
property: r.property, city: r.city, state: r.state, zip: r.zip, units: r.units,
loan_amount: r.originalAmount, rate: r.rate, market_at_origination: r.pmmsBenchmark,
assumable_spread: +(r.pmmsBenchmark - r.rate).toFixed(2), // pts below market = the pitch
below_market: !!r.belowMarket, originated: r.originationDate, holder: r.holder,
source: 'HUD/FHA Multifamily Insured Mortgages (public, resellable)',
}))
.filter(l => l.assumable_spread >= minSpread)
.sort((a, b) => b.assumable_spread - a.assumable_spread);
const total = leads.length;
// Free/anon get a 5-row taste; signed-in get the full ranked list (capped by limit).
const paid = u && u.email;
const limit = paid ? Math.min(+req.query.limit || 200, 1000) : 5;
res.json({
total, returned: Math.min(total, limit), tier: paid ? (u.tier || 'free') : 'anon',
teaser: !paid, leads: leads.slice(0, limit),
note: paid ? undefined : 'Sign in for the full ranked assumable-lead list.',
});
});
console.log('[crcp-leads] assumable-loan lead engine mounted (/api/leads/assumable)');
};