I built the system that tells our sales team which customers to call, why, and what to say.

Screenshots use anonymized sample data. All company names, people, and figures are fictional.
Nobody knew which customers to worry about
A growing book of business with no visibility into growth, churn risk, or expansion opportunities. Sales teams manually queried the data warehouse across 20+ tables. A legacy internal tool existed but was broken.
Product usage, credit consumption, support tickets, meeting cadence, webinar attendance, community activity. All siloed and unintegrated.
Six things, one system
Signal Detection
30+ signal types evaluated against every account, each assigned severity (P0/P1/P2) and category (expansion, churn, or mixed). Usage growth week-over-week, consumption rate changes, support spikes, deployment changes, contract thresholds, meeting cadence, webinar engagement, community activity.
LLM Expansion Scoring
An LLM classifies accounts into expansion motions with predicted ARR delta and confidence scores. The whole thing costs about $0.08 for the entire book of business because I built a token budget system that caps spending per run.
Health Scores
Multiple factors across product usage, technical health, strategic alignment, engagement, and expansion path. Some rules-based, others LLM-evaluated. Hard overrides for domain-specific edge cases like acquisitions or critical usage thresholds.
Persona Analysis
Views into the buying committee: who's the champion, who's the decision maker, where are the coverage gaps, which contacts have gone stale. Built from CRM and community platform data.
Slack Delivery
Monday morning DMs to each AE with P0/P1 signal accounts. Includes signal context, recommended plays, predicted ARR delta. P2 signals go to shared channels.
One-Click QBRs
Quarterly Business Reviews with health scores, contact roster, usage metrics, top signals. Generates shareable links. PDF export via server-side rendering. No customer login required.

Signals view — 30+ signal types, filterable by source and time window.

Account detail — health score, churn risk, score trend over 90 days.
Everything expensive happens at build time
The core design: a multi-cadence bake pipeline — hourly data refresh, periodic LLM scoring and persona analysis — produces all expensive computation and writes the output to object storage. Runtime reads cached results. Pages load fast. Cost is fixed and predictable.
50+ warehouse queries across 30 data modules pull from 20+ warehouse tables. Signal evaluation, LLM scoring, health computation, and persona aggregation run across hourly, daily, and weekly cadences. Output is a single data.json pushed to object storage. The Next.js app reads it with ETag-based caching. PostgreSQL handles writes (QBRs, user state). If storage is down, the app serves stale disk data.
Full Stack
Five problems that didn't have obvious answers
Keeping LLM costs from exploding
The naive version would send every account's full context to an LLM. That would cost hundreds per run. I built a token budget system with a hard cap per run. Shared context (pricing tiers, product catalog) goes in a cached prompt block. Per-account input is restricted to variable data only. If the budget runs out mid-run, it stops cleanly and reports which accounts got scored and which didn't. Primary API is the LLM provider directly, with cloud provider fallback.
Signals that fire too much are useless
Early versions had a problem: if an account's usage was growing 60% WoW for three weeks straight, the AE got three identical alerts. That's noise, not signal. I added firing granularity controls. Some signals fire daily, some weekly. If the trend continues, it doesn't re-alert. Definitions are versioned with supersede chains, so changing a threshold from 50% to 25% doesn't break historical data. Minimum 2-signal threshold before an account becomes an opportunity.
Context-blind health scores
Rules-based and LLM factors sometimes rated accounts green despite imminent churn risk. I added hard override circuit breakers: domain-specific rules that force Red ratings for special contexts. Near-ceiling usage with a short renewal window. Acquisition mid-flight. These override whatever the computed score says.
Pre-rendering hundreds of pages
Every account gets its own detail page, pre-rendered at build time with charts, timelines, signal histories, persona analysis. Default Node heap couldn't handle it. Had to significantly increase it for the SSG pass. Multi-stage Docker build: fat heap for compilation, minimal image with headless browser for PDF export. Explicit build artifact exclusions to prevent image bloat.
Signal utility vs. accuracy
Raw signals like "usage grew 60% WoW" are accurate but not actionable. Every signal now carries a "why" payload with specific context: job changes, new assets, credit burn shifts. Slack delivery provides concrete talking points, not dashboard links that require interpretation.
Shipped to the org in 4 weeks
Before: AEs manually queried the data warehouse, decided whether to act, often skipped the process entirely.
After: Every AE receives Monday Slack DMs with priority accounts, ranked by severity, with specific talking points. The dashboard is there for deeper analysis, but Slack delivery is the primary product.
QBR generation went from a multi-hour manual process to one click with shareable customer-facing links. LLM costs held at $0.08 per scoring run for full account scoring. 580+ tests cover signal logic edge cases.
Scope Touched
Interested in a similar project?
Let's Talk