Skip to content
Follow Up Ace logo
AI Platform · ML · FlagshipLive in production

Follow Up Ace

An AI operating layer for real estate teams, on top of Follow Up Boss

followupace.com

Follow Up Ace is the largest system in this portfolio and the one we own end to end. It attaches to Follow Up Boss — the CRM most real estate teams already run — and adds the layer that CRM doesn't have: it scores every contact in the book on likelihood to buy, sell, respond or go cold, writes those scores back into fields the agent already looks at, and gives the team a chat and voice assistant that can actually act on the CRM rather than just describe it. Underneath sits a real data warehouse, real models, and a loop that checks the predictions against what actually happened.

528K
Lines of production code
242
MCP tools for AI agents
5
ML models serving in production
5,487
Automated test cases

The problem

A real estate team's CRM knows everything and tells them nothing. Every call, text, email open, property view and note is captured — and the agent still opens the app to a list sorted by whoever happened to email most recently. The person most likely to transact this month is somewhere in a book of forty thousand contacts, and nobody has time to find them.

The obvious answer — bolt a chatbot on the side — solves the wrong problem. A chatbot answers questions about the CRM. What a team needs is something that reads the whole book, ranks it, keeps the ranking current as new activity lands, and puts the answer where the agent is already looking.

Doing that honestly is hard in ways that are easy to skip. Scores have to be calibrated or the numbers are decoration. Predictions have to be checked against outcomes or nobody can tell whether the model works. Contacts that aren't prospects at all — lenders, vendors, recruiters, do-not-call records — have to be excluded before they poison every aggregate. And a multi-tenant billing system has to be incapable of charging one team's seat twice.

What we built

Capture the whole book, not just what happens next

Thirty-four Follow Up Boss webhook event types stream in — people, calls, texts, emails, notes, appointments, tasks, deals, email-marketing engagement, IDX property-view events, and stage and pipeline configuration changes. For teams joining with an existing book, a checkpointed seed walks the entire contact history through the API and resumes from the last completed page after a crash, writing identity fields only so it can never clobber what the live pipeline owns.

A real warehouse, not a reporting screen

Twenty Firestore-to-BigQuery change-data-capture streams mirror the operational database into an append-only raw layer, transformed by 67 SQL models across dimensions, facts, features and views. Physical compaction tables cut one dimension query from 387 GiB scanned to 74 MiB. Every warehouse read carries a byte cap and a job timeout, and a single environment variable disables all of them.

Five models, with a guard that can refuse a release

Boosted-tree classifiers predict conversion, response, churn, appointment propensity and a large-corpus conversion variant. Retraining is triggered by corpus growth, model age, feature-set change or measured AUC drift — and the promotion guard holds the incumbent if the challenger regresses beyond a threshold, raising a critical alert instead of shipping. A corpus-size guard refused a swap in production when a staging build came in at 56% of the prior row count.

Calibration, so a percentage means something

Raw model output from a class-weighted classifier is not a probability. A two-stage calibrator applies isotonic regression on a held-out slice, then optionally shifts the corpus base rate to the account's true base rate. Whether that shift was applied is tracked on a flag, and every consumer respects it: only prior-shifted scores may be shown as an absolute percentage, and everything else keeps its estimate label.

Tools, so the assistant can act

242 tool definitions on the stdio MCP server expose real CRM operations to Claude, ChatGPT and any other compatible agent, with a separate 123-tool HTTP surface for hosted connectors and a desktop extension that proxies through server-side OAuth. The assistant creates contacts, logs activity, drafts follow-ups and books tasks — it operates the CRM rather than describing it.

Voice that holds a conversation and doesn't leak keys

ElevenLabs owns the audio loop over WebRTC; our server owns exactly two things — minting the session token so the API key never reaches a browser, and serving the completions endpoint that carries live CRM context. Turn latency is protected by routing to the fastest provider and restricting voice turns to a read-only tool set, so no slow write can stall a sentence.

Engineering highlights

The parts that were genuinely hard — and how they were solved.

The calibration ladder that proves the models work

Every meaningful score change logs a prediction; an hourly job stamps the actual outcome at 24 hours and 7 days. For a long time the whole view was empty — timestamps were written as objects and parsed to NULL on all 555,000 rows. After the fix, the ladder came out monotone on the latest week: contacts scored Hot responded within 24 hours 47.5% of the time, Warm 15.2%, Cool 9.4%, Cold 1.0%, Dormant 0.6%. That ordering, measured against outcomes rather than asserted, is the single most defensible artefact in the system.

A control arm inside the prediction log

The prediction logger is change-gated, which biases the sample toward contacts that moved. So it also writes a small unbiased random arm every run, tagged as a separate source. That lets the calibration view segment on prediction source and measure the main stream's selection bias directly, rather than hoping it's small.

Billing predicates that close the undefined-flag trap

The convention that an unset ownership flag means legacy team-pay meant every naive equality check let legacy seats reach Stripe and buy a seat their team already paid for. Two pure predicates now gate every mutating billing route on both the modern and legacy stacks. The second-order bug was worse than double billing: the resulting webhook would have silently converted a team seat to self-managed. 142 tests across five files hold the line.

Attribution that records ambiguity instead of guessing

Every activity is attributed not just to a person and an agent but to the deal it happened under, by checking which deals were open at that moment. One candidate is a clean attribution; none means it was lead work. When several open deals overlap, the system records that the attribution was ambiguous along with the candidate deal IDs rather than silently picking one — because a confident wrong number is worse than a flagged uncertain one.

A relevance classifier born from one bad score

Teams run side businesses through their CRM. A contact belonging to an entirely different business, sitting in a Do Not Call stage, was carrying a Win Score of 99 and the label Active Buyer. The classifier that fixed it is deliberately tuned for precision over recall — wrongly excluding a real lead removes them from every AI surface, which is far worse than wasting analysis on a lender — and its single verdict is enforced at six separate pipeline gates.

Design consistency enforced by the test suite

The same metric was rendering on five admin screens with two definitions and three different values. The fix was a frozen metric catalogue and one shared data fetch — but the durable part is the contract test that walks every admin component file and fails the build on hardcoded metric labels, so the drift cannot come back. A companion test pins deleted routes, redirects and the navigation row count.

Reverse-engineering a DSL that fails silently

The CRM's Smart List filter language returns HTTP 200 with zero results for a wrong operator, a scalar value, a camel-cased operation or a raw custom-field handle — four ways to be silently wrong. The condition builder converts all four into loud exceptions at construction time, including a banned numeric operator with a documented arithmetic workaround. Thresholds are per-account percentiles, because a fixed cutoff selects nobody on a team whose median score is single digits.

Priority lanes so new customers never wait behind a backfill

Extraction work runs in four explicit lanes — day-one connect, continuation, coverage backfill, and fleet re-pass — and a higher-urgency enqueue supersedes a queued lower-priority job for the same account. A team that connects today jumps the queue ahead of the fleet-wide sweep, so their consolidated scores land hours after signup rather than after the next weekly rebuild.

Where it stands

  • Live at followupace.com on Railway, with 854 registered HTTP endpoints, 42 scheduled in-process jobs, 26 background workers and 34 deployed cloud functions.
  • Twenty change-data-capture streams keep the warehouse fresh to within minutes, feeding five production models plus 67 SQL transformation models.
  • 419 test files carrying 5,487 test cases and 13,947 assertions, including contract tests that fail the build on design-system drift.
  • A 374-page public content surface, an admin dashboard of 107 components, a companion app, a browser extension, and a desktop extension for Claude.
  • Cross-tenant benchmarking enforces k-anonymity of 10 at three code anchors plus independent floors in SQL, so no aggregate can expose a single team.
  • iOS and Android companion builds are in App Store review — not yet released.

The stack

Backend
Node 20Express 4Socket.io 4BullMQ 4RedisOpossum
Frontend
Vue 3.5Vuetify 3VitePiniaEChartsCapacitor 8
Data
FirestoreBigQueryRedisPostgresTypesense
ML
BQML boosted treesPAVA/isotonic calibrationDrift promotion guard
AI
Anthropic ClaudeOpenAIGeminiElevenLabs ConvAIMCPDXT
Platform
RailwayDockerFirebase FunctionsCloudflare Workers
Quality
Jest 30VitestESLint 9SentryNew Relic
Third-party services wired in
Follow Up Boss API + OAuthStripeTwilioGmail OAuthResendSendGridMailchimpElevenLabsAnthropicOpenAIGoogle GeminiGoogle BigQueryFirebaseZillow lead ingestFreshdeskSentryNew RelicFirebase Cloud Messaging35 public data sources