How Kova works
The short version: things that happen at a customer come in as events, Kova stores every event, rebuilds the account from them, scores it, and tells the CSM when it slips. Everything below is running on this site right now. You can try it or call the API.
Overview
Solid arrows are live on this site. The dashed one is how a real Kova customer would feed it; here, the buttons and the API Explorer stand in for those systems.
One event, end to end
What happens when you click “Customer at risk” and the first event, “weekly usage falls to 17”, is sent:
- Request. The browser sends
POST /api/v1/eventswith the sandbox key as a bearer token. - Checks. Kova checks the key, confirms the sandbox hasn't expired, applies the rate limit, and validates the event type and payload. Anything wrong gets a clear error: 400, 401, 410, 413, 422 or 429.
- Store. The event is written to Postgres. Events are never edited or deleted, except by a full reset.
- Rebuild. Kova replays every event for the account, in order, to rebuild its current state: seats, active users, features, tickets, contacts, NPS, invoices.
- Score. Five components (usage, feature adoption, support, relationship, commercial) are scored with written reasons, and the rules engine picks a next step.
- Compare. If the account has dropped into a worse band (healthy to at risk, or at risk to critical), an alert is written, by the AI model if one is connected or by a template if not.
- Deliver. If the visitor gave an email and hasn't used their 3 emails, Kova posts the alert to a Make webhook. Make sends it through Gmail.
- Log. Every outbound call is recorded with its status and timing, and shown in the dashboard's integration log.
- Show. The response carries the new score. The dashboard updates straight away; anything sent from outside (cURL, Postman) appears within 3 seconds because the dashboard polls.
Design decisions
Store events, not scores
Kova keeps the history of what happened and works out the score from it each time. Every score can be traced back to the events that caused it, which is what a CSM needs when a customer asks “why are we red?”. It also means a change to the scoring model rescores the whole history, with no migration.
Trade-off: replaying gets slower as history grows. Fine at this scale; in production I'd add a snapshot every few hundred events.
Rules first, not machine learning
The score is a transparent weighted model. A new customer can check it against their own instinct in the first week, and it works from day one without months of churn data.
Trade-off: it won't spot patterns nobody thought to write a rule for. Once a customer has a year of renewals and losses, a predictive model can be tested against the rules, not instead of them.
Alerts on band changes only
Alerting on every score movement trains people to ignore alerts. Kova only alerts when an account crosses into a worse band, with a cooldown and a hard cap on emails per sandbox.
A private sandbox for every visitor
Each visitor gets their own account and key, so one person's experiments never spoil another's demo. Sandboxes and any email addresses expire after 7 days.
Hand off to a workflow tool for delivery
Kova doesn't send email itself. It posts to a webhook and Make does the delivery. Swapping Gmail for Slack, Teams or a CRM task becomes a change in Make, not in Kova's code, which is how most customers want to work.
Secrets stay on the server
The database key, AI key and webhook address live in the hosting environment, never in the code or the browser. The only key a visitor sees is their own sandbox key, which can only touch their own sandbox.
Stack
| Part | Tool | Why |
|---|---|---|
| Web app and API | Next.js, TypeScript | One codebase for pages and API routes |
| Hosting | Vercel | Deploys on every GitHub commit, secrets kept in environment settings |
| Database | Postgres on Supabase | Reached through its REST API, row-level security on |
| Alert workflow | Make, Gmail | Webhook trigger, email step, run history for debugging |
| AI writing | Anthropic API | Optional, with a template fallback so nothing breaks without it |
What changes in production
This is a demo, and some things are deliberately simpler than they would be for a paying customer. What I'd add:
- Signed webhooks in both directions (HMAC signatures), so each side can prove a request is genuine.
- Idempotency keys on events, so a customer system that retries doesn't count the same ticket twice.
- A proper queue with retries for alerts, instead of sending during the request.
- Rate limits in a shared store such as Redis. The demo keeps them in memory per server.
- Scoped API keys (read-only, write-only), key rotation, and OAuth for the native integrations.
- Customer-editable scoring weights, because every customer weighs usage and support differently.
- Monitoring and alerting on error rates and webhook failures, plus an audit log customers can export.
- Data handling: EU data residency options, a DPA, and retention settings per customer.