Last month I was talking to a founder who had just raised a Series A. Twelve people on the team, 400 paying customers, PostHog and HubSpot set up. I asked him how his team decides which customers to reach out to each week.
"Every Monday morning," he said, "our CS lead runs a SQL query against the production database."
The query joined four tables, filtered for accounts with zero logins in 14 days, and exported a CSV to Google Sheets. Someone manually cross-referenced it against the CRM to remove accounts already in active conversations. The whole process took two hours. They'd been doing it for eight months.
This is not a PostHog problem or a HubSpot problem. It's a category problem. The tools we use to understand customers were built for a different job.
No tool answers the question "where is this customer right now?"
Every SaaS runs on three categories of customer data tools, and each one dodges the fundamental question:
Analytics tools tell you what happened in aggregate. Mixpanel, Amplitude, PostHog. Great at funnels, retention curves, dashboards. But they compute statistics over populations. Ask "is Acme Corp activated?" and you get a blank stare. Ask "which individual accounts churned this week?" and you're writing a custom report.
CRMs record who you talked to. Salesforce, HubSpot. They're databases of human interactions. But they have no idea whether a customer is healthy or stuck unless someone manually updates a field. The health score you set up six months ago is probably stale because nobody reviewed the formula since then.
CDPs move data between systems. Segment, RudderStack. They collect from your SDKs, stitch identities, and route to destinations. They're pipes. The pipe doesn't know what the events mean. It just delivers them.
So the person who needs to answer "which customers need help now?" is left with three options: write SQL, check dashboards that show aggregates instead of individuals, or maintain a spreadsheet that's outdated by Wednesday.
What "Customer Success as Code" actually means
The idea is simple: your CS logic is product logic. It should live in the same repo as your application code, go through the same review process, and produce deterministic answers — not guesses.
Here's the model we built:
Events are immutable facts. report.exported at a specific time, by a specific customer. They describe what happened. You're already sending these to analytics.
Definitions are TypeScript files in your repo. They describe what events mean. A lifecycle definition says "a customer enters the activated state when they complete the onboarding journey." A health formula says "score = 40% activation weight + 35% engagement weight + 25% expansion signals."
The state engine evaluates your definitions against every customer's events and produces derived state: lifecycle position, journey progress, segment membership, health score, active signals. All inspectable. All recomputable. All tied to a specific version of your configuration.
This is not a new idea. It's how compilers work. Source code in, parse tree out. Events in, customer state out. It's how databases work. Rows in, query out. The difference is that nobody applied this model to Customer Success.
The TypeScript part matters more than you think
The reason we made Kite code-first instead of building a visual workflow editor is straightforward: when a customer churns and your CEO asks "why didn't we catch this?", you want an answer that's traceable to a specific version of a specific rule reviewed by a specific person at a specific time.
With a visual builder, the answer is "someone changed a slider in a settings panel nobody reviewed." With code, it's "the churn_risk lifecycle transition was updated in PR #247 by Sarah on March 12th, and the condition changed from 14 inactive days to 21. Here's the diff."
That's the bar. Your activation definition determines which customers get onboarding support. Your health score determines which accounts your CS team ignores. These are product decisions. They deserve pull requests and CI validation.
I'm not saying visual tools are bad. Zapier and Retool are visual and I use them. But they solve different problems. If I'm wiring up a Slack notification, drag and drop is fine. If I'm defining what "at risk" means for a customer paying us $2,000 a month, I want it in code.
What we got wrong (and what Kite doesn't do)
We spent two months early on building an AI layer that would automatically suggest health score adjustments based on churn patterns.
It was bad.
The suggestions were plausible but wrong often enough to be dangerous. It would flag seasonal businesses as at-risk because their usage dipped in December. It would miss accounts that were quietly escalating support tickets while maintaining normal product usage.
We killed it. The AI in Kite today is an assistant — it explains why a customer isn't activated, surfaces event gaps in your instrumentation, and helps you debug state changes. But it never makes decisions for you. State computation stays deterministic.
Some things Kite intentionally won't do:
- Send emails or Slack messages. That's what your existing tools do.
- Replace your analytics. Dashboards are still useful for trends.
- Auto-assign health scores with machine learning. Determinism matters more than precision.
- Ingest events from 50 sources. You already have an event stream. We interpret it.
We're a state engine. Not a CDP, not a CRM, not analytics. Just the part that answers "where is this customer right now?" with a number you can trace back to a specific commit.
How to try it (5 minutes, no setup call)
You can run Kite locally, define a model, and see customer state change live without deploying anything to production.
npm install -g @kitesdk/cli
kite login
mkdir my-cs-model && cd my-cs-model
kite init
kite validate
kite dev
kite init generates a working project with lifecycle, journeys, health, segments, and signals already wired up. Send two events and watch the state engine compute. The docs are at docs.usekite.cloud.
If you're the person running SQL queries on Monday mornings to figure out which customers need help, I'd like to hear from you. The whole reason we're building this is that you shouldn't have to.
Have feedback? Find a bug? Reach me at mateus@asklynx.com or open an issue on GitHub.