This repo is a lead health score algorithm built for Intuitive Analytica's OA. It ingests a CSV (or uses sample data provided) and generates a "health score" depending on various factors (explained below).
This full-stack application is written in Next.js 16 for simplicity. This handles most of the routing and bundling, although its functionality can be expanded on with tools such as TanStack.
The UI is built using React with Tailwind CSS as the primary CSS scaffolding framework. In addition, Shadcn was chosen for common elements in the dashboard along with Phosphor for icons. The theme of the application is extracted directly from Intuitive Analytica's page (https://intuitiveanalytica.com/).
For production instances, it would make the most sense to host this on a Vercel instance or an equivalent cloud provider. For the purposes of a demo, I've bundled this app to be easiest to use on a local machine.
To get started, install required packages using npm:
npm installThen, spin up a new server using:
npm run devFrom here, open http://localhost:3000. You can choose to view sample data provided, or upload your own CSV.
This app uses an algorithm design common within many startup lead scrapers, including one I have built in the past for my own company's purposes. It is completely deterministic, meaning there is no AI used. AI tends to skew results in non-deterministic ways, which can cause confusion to end users and is less predictable than a scoring algorithm.
To begin, the parser (found in lib/csv.ts) maps each row into a special table for scoring.
| Field | Role in scoring |
|---|---|
appointmentScheduled |
Funnel progress |
quoted |
Funnel progress |
answeredCall |
Funnel + unlocks call-duration points |
lastCallDurationSeconds |
Engagement (only if answered) |
emailOpened |
Engagement |
emailClicks |
Engagement |
smsReplied |
Engagement |
createdDaysAgo |
Recency |
lastContactDaysAgo |
Recency + interaction rules |
leadSource |
Source quality |
creditRange |
Financial fitness |
annualIncome |
Financial fitness |
callAttempts |
Penalties / next-action routing |
The core formula is a very simple weighted point total that determines whether a lead is "hot", "warm" or "cold." This makes it very simple for sales reps to understand and is easily adjustable for any future columns that may be added.
- Collect all "contributors" to the score (shown in the tables below).
- Sum all of these points to get a total
- Clamp down to the 0-100 range
- Map to a "band":
- Hot >= 85
- Warm 60-84
- Cold < 60
Each category has maximum values shown to the user:
- Funnel: 35
- Engagement: 25
- Recency: 20
- Source: 10
- Financial: 10
These are numbers that I used in my own implementation of this, but they can be easily adjusted based on the user's preferences. They are only used in the UI for the user, but do not make a difference during score calculation. The purpose of this is to make the user experience more friendly, practical, and easier to understand at a glance.
| Signal | Points |
|---|---|
| Appointment scheduled | +15, or +10 if last contact is 7+ days ago |
| Quote delivered | +12 |
| Answered a call | +8 |
| Signal | Points |
|---|---|
| Call duration (answered only): 10+ min | +10 |
| Call duration (answered only): 5–10 min | +7 |
| Call duration (answered only): 2–5 min | +4 |
| Call duration (answered only): <2 min | +1 |
| SMS replied | +6 |
| Email clicks 4+ | +5 |
| Email clicks 1–3 | +3 |
| Email opened | +4 |
Duration points only apply if answeredCall is true.
| Signal | Points |
|---|---|
| Created ≤3 / ≤7 / ≤14 days | +10 / +7 / +4 |
| Last contact ≤1 / ≤3 / ≤7 days | +10 / +7 / +4 |
Anything older than these will be given a score of 0 (the staleness is scored separately).
| Source | Points |
|---|---|
| Referral | 10 |
| 8 | |
| 7 | |
| Website | 6 |
| 4 | |
| TV Ad | 3 |
| Anything else / blank | 5 |
| Credit | Points |
|---|---|
| Excellent | 6 |
| Good | 4 |
| Fair | 2 |
| Unknown | 2 |
| Annual income | Points |
|---|---|
| ≥150k | 4 |
| ≥90k | 3 |
| ≥60k | 2 |
| else | 1 |
| Rule | When | Points |
|---|---|---|
| No answer | ≥8 attempts and never answered | -18 |
| Attempt pressure | ≥6 attempts, never answered (and not already in no-answer) | -6 |
| Short calls | ≥5 attempts and last call <60 seconds | -10 |
| Stale | created ≥18 days ago and last contact ≥8 days ago | -12 |
| No engagement | no email open, 0 clicks, no SMS reply, no appointment, no quote | -12 |