Pulse CRM
The eligibility and pricing engine behind a reverse-mortgage call center.
- Role
- Sole primary engineer
- Period
- Mar 2025 – Apr 2026
- Status
- Production
- Stack
- Next.js 16, Supabase Postgres, Deno Edge Functions.
323,695
Lines in src/
429
SQL migrations
143
RLS-protected tables
17m17s
10k-property eligibility benchmark
1,642
Commits
5
Custom ESLint rules
The problem
Ennkar's loan officers manage reverse-mortgage (HECM) refinance pipelines: qualifying borrowers against FHA program rules, valuing properties, importing large third-party dial lists, and calling under telemarketing compliance constraints. Before Pulse, this was spreadsheet-and-phone work — no shared source of truth for pricing, no audit trail for who could call whom, and no way to run eligibility across a dial list faster than an agent could dial it by hand.
Constraints
- Loan math has to be exactly right — there is no acceptable rounding error in a regulated financial calculation.
- Dial lists arrive as inconsistent third-party CSVs that need fuzzy field mapping, not a fixed schema.
- Telemarketing compliance (TCPA) constrains when and how a borrower can be contacted, and that has to be enforced by the system, not by agent discipline.
- The team is small enough that there is no dedicated QA function — correctness has to be enforced by tooling, not by process.
What I built
A Next.js 16 application on Supabase Postgres with no application-server API tier: pages call Postgres directly through typed queries, Postgres RPCs, and Deno Edge Functions, with Row-Level Security enforced on every public table. Two route groups — protected and public — cover 53 page routes. There are zero 'use server' files in the codebase, a documented architectural rule rather than an oversight: the database, not a Next.js API layer, is the authority.
- A unified loan derivation engine (loan-field-engine.ts) that produces rates, PLF matches, principal limits, and balances consistently across the client form, CSV import, and nightly pipelines — replacing three implementations that used to drift.
- A batch eligibility pipeline benchmarked at 10,000 properties in 17m17s across 130 parallel windows with zero defects, with window claim caps, continuation dedup, and same-run idempotency so a rerun cannot double-process a property.
- A jurisdiction fee engine covering all 50 states with per-county research.
- TCPA-aware call-window scheduling and a reminder-delivery ledger that dedupes minute-accurate notifications.
- Integrations with FRED for interest rates, DataTree (with RentCast fallback) for property valuation, Gmail and RingCentral for communications, and a RingCentral speech-to-text webhook — the only shipped AI-adjacent surface in the system.
Architecture
The system deliberately has no general-purpose API tier. Client code queries Supabase directly under RLS, calls Postgres RPCs for anything that needs to run as one transaction, and calls Deno Edge Functions for anything that needs a runtime outside Postgres — webhook receivers, third-party API calls, and long-running batch work. As of the 2026-08-24 snapshot the database exposes 143 public tables, all under RLS, roughly 339 callable functions and procedures, and 40 Edge Functions, coordinated by 20 pg_cron jobs. An earlier internal audit tracked reducing SECURITY DEFINER functions exposed to the authenticated role from 238 to 91 across ten labelled phases — authorization hardening treated as a measured program, not a one-time review.
Decisions and tradeoffs
Decision
Write a custom ESLint plugin (eslint-plugin-internal) with five project-specific rules, two of which stop the loan engine from overwriting factual source inputs or basis fields without an explicit canEngineWrite() gate.
Because
A real production defect — Number.isFinite(Number(x)) evaluating a malformed value as finite — let a 0% ARM cap freeze rates. Code review had already missed it once.
Trade-off
Every engineer touching the loan engine now works inside a stricter set of rules than the rest of the codebase, which adds friction to changes in that area by design.
Decision
Route all data access through Supabase RPCs and Edge Functions instead of a Next.js API layer.
Because
It keeps authorization logic in one place (Postgres RLS and grants) instead of duplicated across an app-server API and the database.
Trade-off
Business logic that would be trivial in a typed server function has to be written in PL/pgSQL or Deno, which is a smaller hiring pool and a steeper local-development curve.
Note
A multi-tenancy isolation harness (Storage, Realtime, and cache isolation, tested in vitest.isolation.config.ts) exists in the codebase. It is designed and test-harnessed, not shipped — Pulse currently serves one tenant.Outcome
- RLS-protected public tables
- 143
- Callable functions / procedures
- ~339
- Edge Functions
- 40
- SQL migrations
- 429
- 10k-property benchmark
- 17m17s, 130 windows, 0 defects
- Legacy API routes removed
- ~89 → 20
- Eligibility-report storage after retention pruning
- ~21GB → ~3.5GB
As of 2026-08-24
Pulse CRM is the deepest system in this portfolio: the largest codebase, the most tables, and the only project with static analysis purpose-built to prevent a specific class of financial defect. It has no public source and no publishable screenshot — every local capture contains real borrower data — so this case study leans on measured system figures rather than imagery.
Related work
Want the full picture, or open to talking about your own project?