# lead-scorer

Intelligent lead-scoring microservice for sales & marketing: turn raw lead/contact records into points and an A–D grade using a transparent, rule-based model. Ships a proven default model blending...


Intelligent, **explainable** lead-scoring microservice for sales & marketing. Turns raw lead/contact
records into **points + an A–D grade** with a transparent, rule-based model. Ships a proven default
model that blends **BANT** (budget, authority, need, timeline), **firmographic fit** (company size,
seniority, industry, revenue) and **engagement** (visits, opens, clicks, replies, demo, meetings).

Pure deterministic heuristic scoring — there is no LLM in the scoring path on purpose (a score must be
reproducible and auditable). The model is passed in args (`buildModel` produces one; DB persistence
lands later). Missing lead fields never throw — they score 0 and are reported.

## Tools

| Tool | Args | Returns |
|---|---|---|
| `score` | `{ lead, model? }` | `points`, `percent`, `grade` (A–D), group rollup, `missingFields` |
| `buildModel` | `{ rules, options? }` | a validated, portable model object |
| `explain` | `{ lead, model? }` | top drivers, gaps (missed points), plain-English summary + full breakdown |
| `batchScore` | `{ leads, model?, options.top? }` | ranked leads + grade distribution + averages |
| `defaultModel` | `{ options.grades? }` | the shipped BANT/fit/engagement model + weights |

### Rule types (for `buildModel`)

`boolean` · `presence` · `map` (value→0..1) · `threshold` (numeric bands, `goal:'min'` to invert) ·
`scale` (linear min→max, `goal:'min'`) · `contains` (keyword/array membership). Field lookup is
case/format-insensitive (`company_size` == `companySize` == `Company Size`).

## Example

```js
import pack from './index.js';

const lead = { budget: 'high', authority: 'decision-maker', need: 'high', timeline: 'this-quarter',
  companySize: 450, seniority: 'vp', industry: 'SaaS', demoBooked: true, replies: 2, linkClicks: 6, visits: 9 };

console.log(pack.adapters.score({ lead }));          // → { points, percent, grade: 'A', ... }
console.log(pack.adapters.explain({ lead }).summary);
```

## DRY boundaries

- Not `numbers.weightedScoring` (that ranks generic metric objects by a raw weighted sum). This owns
  the **sales-lead** domain: BANT/fit/engagement semantics, A–D grades, per-field explanations, and a
  shipped default model.
- Self-contained: no cross-pack imports.


---
Source: shared/engines/adapters/domain/lead-scorer/README.md
Canonical: https://docs.leumas.tech/p/adapters/domain/lead-scorer
