# 09. Attom Data Solutions API for Property Record Snapshots meta: id: core-services-09 feature: core-services-implementation priority: P2 depends_on: [core-services-01] tags: [hometitle, attom, property-records, api-integration, real-estate] objective: - Replace the `fetchCountyRecords()` stub that returns `{ ownerName: "Unknown Owner" }` with a real property data API integration using Attom Data Solutions, enabling actual property snapshot and change detection. deliverables: - Attom API client for property search, owner info, and tax/assessment data - Property snapshot creation and storage in database - Change detection pipeline wired to real data (your detector logic already works) - Alert generation for ownership changes, liens, and tax status changes steps: 1. Sign up for Attom Data API at https://attomdata.com (pricing: ~$0.05–$0.10/record, enterprise plans available) 2. Add `ATTOM_API_KEY` to `.env.example` and validate in `env.ts` 3. Create `hometitle/attom.client.ts`: - `searchProperty(address)` — find property by address, return parcel ID and metadata - `getPropertyProfile(parcelId)` — full property record: owner, deed date, tax info, liens - `getPropertyHistory(parcelId)` — historical ownership and transaction records - `getTaxInfo(parcelId)` — tax amount, delinquency status, exemptions 4. Replace `fetchCountyRecords()` in `scanner.ts` with Attom API call: - Use geocoding result (Google Maps API, already works) to get normalized address - Query Attom by address → get parcel ID → fetch full property profile - Parse response into `CountyRecord` / `SnapshotData` schema 5. Implement snapshot storage: - Store initial snapshot in `propertySnapshots` table - On re-scan, fetch new snapshot → compare with last → detect changes 6. Wire change detection (your `change.detector.ts` is already implemented): - `ownership_transfer`: owner name changed → critical alert - `lien_filing`: lien count increased → warning/critical alert - `tax_change`: tax amount changed → info alert - `deed_change`: deed date changed → critical alert 7. Implement tier limits: - Guard: 1 property monitored - Fortress: 3 properties monitored - Family: 5 properties monitored 8. Add cost tracking: ~$0.05–$0.10 per property lookup, track per-user usage tests: - Unit: Mock Attom API responses, verify parsing and snapshot creation - Integration: Test with real Attom API using known property address - E2E: Add property to watchlist → trigger scan → verify snapshot created → simulate change → verify alert acceptance_criteria: - [ ] `fetchCountyRecords()` makes real HTTP request to Attom API (not returning mock data) - [ ] Property snapshots contain real owner name, deed date, tax amount, lien count - [ ] Change detection compares real snapshots and identifies actual changes - [ ] Ownership transfer creates critical alert with property address in message - [ ] Lien filing creates warning or critical alert depending on lien amount - [ ] Alert severity matches existing `severityForChange()` logic - [ ] Geocoding → Attom search → snapshot pipeline works end-to-end - [ ] Cost tracking records each Attom API call for billing analytics - [ ] Tier limits enforced: Guard = 1 property, Fortress = 3, Family = 5 - [ ] Graceful fallback: if Attom API fails, retry once, then alert user of monitoring gap validation: - Run `vitest run hometitle.test.ts` — all tests pass with real Attom mock - Manual: Add real property address, trigger scan, verify snapshot in database - Simulate change: Update snapshot in database with different owner, trigger detector, verify alert - Check cost: Database shows Attom API usage per user per month notes: - Attom covers ~150M US properties but not all counties equally — some rural areas may have gaps - For counties not covered by Attom, Phase 3 (task 10) implements county recorder web scrapers - Property fraud is a real and growing problem: FTC reports $1B+ in losses annually - This is a unique differentiator — no major identity protection competitor offers property monitoring - Consider partnership with title insurance companies for added credibility - The existing Google Maps geocoding already works — verify `GEOCODING_API_KEY` is set