Jacob Perry
← All projects

Built for the Customer Success team at beehiiv

CSM Mission Control

What on my book needs me today, and why?

An operational dashboard for a customer success team. It replaced a weekly ritual of stitching together Metabase tabs, HubSpot views and Zendesk searches with one screen per account.

Open the live demo

Runs on fictional data. No sign-in or account needed, and nothing you change is saved.

The Workspace view of CSM Mission Control showing one account's live blocks
122k
lines of TypeScript
133
API routes
26
engines
18
scheduled workflows

The problem

A CSM covering around 40 enterprise accounts had the data but not the picture. Utilization lived in Metabase, renewal dates and risk notes in HubSpot, support load in Zendesk, deliverability in ClickHouse, feature requests in Linear, and the actual conversations in Gmail.

Answering “which accounts are quietly going sideways?” meant half a morning of tab-switching, done inconsistently, and usually only once something had already gone wrong. The dashboard's job is to make that question cheap enough to ask every day.

What it does

The Workspace shows one account at a time: only the blocks that are true today, with every absent check named underneath, so a short screen means “nothing else is wrong” rather than “something didn't load.”

Behind it, an at-risk engine scores a book of business against eight weighted flags and explains each result. Renewal sweeps fire at 90, 60, 30 and 7 days with drafted outreach, deliverability alerts come off a ClickHouse rollup, and a request loop tracks feature requests from intake through to “live in app” and back to the customer.

A committed snapshot, not live queries

Every page reads from an AES-256-GCM encrypted snapshot committed to the repo and refreshed twice a day. The source query takes 20–40 seconds; a dashboard that runs it per page view is a dashboard nobody opens twice.

Committing the data makes the read path a file decrypt: single-digit milliseconds, identical in dev and prod. The tradeoff is data up to twelve hours old, which the header states plainly instead of hiding.

One mapping choke point

Raw rows become typed Customer objects in exactly one function. When an upstream schema change silently broke every Stripe and HubSpot deep link, a single column fallback in that mapper restored them for 592 of 598 accounts on the next sync.

Engines as pure functions

An engine takes customers plus options and returns a plain report. It doesn't know whether it's being rendered, posted to Slack by a cron, or dumped to stdout — which is why the same at-risk engine backs a dashboard tab, a CLI and a nightly sweep without a branch anywhere in it.

Mission Control portfolio overview tiles
Mission Control — the portfolio at a glance.
The at-risk sweep table with flagged accounts and scored reasons
The at-risk sweep. Each flag letter is one rule; the recommended action is generated from whichever flags fired.