← back to Commercialrealestate
CRCP: /api/property/:ain + /api/property/by-address enrichment endpoints (TK-10313 Cycle 2)
03cae430e0e2c24232c2ccd82eb664e3ffe1f28d · 2026-08-06 17:00:22 -0700 · Steve Abrams
Wire scripts/enrich-property.js into serve.js as read-only public-record endpoints:
GET /api/property/:ain(\d+) and /api/property/by-address?q= return the per-property
dossier (assessor parcel facts + ULA price candidates + gated-deed declaration).
10-min in-memory cache; AIN digit-constrained so /by-address isn't shadowed. $0,
public gov APIs. Deploy + paid deed lookups stay Steve-gated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 03cae430e0e2c24232c2ccd82eb664e3ffe1f28d
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Aug 6 17:00:22 2026 -0700
CRCP: /api/property/:ain + /api/property/by-address enrichment endpoints (TK-10313 Cycle 2)
Wire scripts/enrich-property.js into serve.js as read-only public-record endpoints:
GET /api/property/:ain(\d+) and /api/property/by-address?q= return the per-property
dossier (assessor parcel facts + ULA price candidates + gated-deed declaration).
10-min in-memory cache; AIN digit-constrained so /by-address isn't shadowed. $0,
public gov APIs. Deploy + paid deed lookups stay Steve-gated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
scripts/serve.js | 32 ++++++++++++++++++++++++++++----
1 file changed, 28 insertions(+), 4 deletions(-)
diff --git a/scripts/serve.js b/scripts/serve.js
index 56bb4b6..423a6c5 100644
--- a/scripts/serve.js
+++ b/scripts/serve.js
@@ -806,10 +806,34 @@ function overlayBroker(row) {
return o;
}
-// ── Per-listing scratch NOTES — now PER-USER (Steve 2026-08-06, TK-10301). The store + the
-// /api/condo-notes routes moved to scripts/crcp-notes.js (mounted above via require('./crcp-notes')),
-// which scopes each user's notes to their own account (hybrid CRM: private notes + shared contacts)
-// and auto-migrates the legacy global condo-notes.json under Frank. Left intentionally blank here.
+// ── Per-property PUBLIC-RECORD enrichment (TK-10313) — "figure each property out". GET
+// /api/property/:ain returns the free dossier (LA Assessor parcel facts + ULA price candidates +
+// an explicit gated-deed declaration). Also /api/property/by-address?q=... . Read-only against
+// public gov APIs, $0. 10-min in-memory cache so repeat drills don't re-hit the assessor. The paid
+// deed-parties layer (grantor/grantee) is a Steve-gated source decision (pending-approval memo).
+const { enrichByAin: _enrichAin, enrichByAddress: _enrichAddr } = require('./enrich-property');
+const _propCache = new Map(); // ain|addr -> { at, data }
+const _propTTL = 10 * 60 * 1000;
+async function _cachedEnrich(key, fn) {
+ const hit = _propCache.get(key);
+ if (hit && (Date.now() - hit.at) < _propTTL) return hit.data;
+ const data = await fn();
+ _propCache.set(key, { at: Date.now(), data });
+ if (_propCache.size > 2000) _propCache.clear(); // bounded
+ return data;
+}
+app.get('/api/property/:ain(\\d+)', async (req, res) => { // digit-constrained so /by-address isn't captured
+ const ain = String(req.params.ain || '').replace(/\D/g, '').slice(0, 20);
+ if (!ain) return res.status(400).json({ error: 'numeric AIN required' });
+ try { res.json(await _cachedEnrich('ain:' + ain, () => _enrichAin(ain))); }
+ catch (e) { res.status(502).json({ error: 'assessor lookup failed', detail: String(e.message).split('\n')[0] }); }
+});
+app.get('/api/property/by-address', async (req, res) => {
+ const q = String(req.query.q || '').slice(0, 120);
+ if (!q.trim()) return res.status(400).json({ error: 'q (street address) required' });
+ try { res.json(await _cachedEnrich('addr:' + q.toLowerCase(), () => _enrichAddr(q))); }
+ catch (e) { res.status(502).json({ error: 'assessor lookup failed', detail: String(e.message).split('\n')[0] }); }
+});
const CONDO_LABEL = 'FHA/VA-approval-based proxy, NOT lender-verified Fannie/Freddie warrantability.';
app.get('/api/condos', async (req, res) => {
← bc720b1 CRCP mls: make 'First seen' visible per admin-card rule — un
·
back to Commercialrealestate
·
DSCR Qualifier: add vacancy % + interest-only underwriting l 8e33148 →