Fit Scoring
Fit scoring measures how well an account matches your Ideal Customer Profile (ICP). It evaluates firmographic data across four configurable dimensions, each returning a score of 0--100, then produces a weighted composite score.
The Four Dimensions
| Dimension | What It Checks | Perfect Score (100) | Zero Score (0) |
|---|---|---|---|
| Industry | Account industry vs. ICP target industries | Exact match found | No match or missing data |
| Company Size | Employee count vs. ICP range | Within [min, max] | At 0 or 2x outside range boundary |
| Revenue | Annual revenue vs. ICP range | Within [min, max] | At 0 or 2x outside range boundary |
| Custom Attributes | Arbitrary fields vs. ICP criteria | All attributes match | No matches or missing data |
Default Weights
The default dimension weights are:
| Dimension | Default Weight |
|---|---|
| Industry | 30 |
| Company Size | 25 |
| Revenue | 25 |
| Custom Attributes | 20 |
Weights do not need to sum to 100. The engine normalizes by dividing the weighted sum by the total weight. Setting Industry to 60 and Company Size to 40 (with others at 0) would score purely on those two dimensions.
How Each Dimension Works
Industry Match
function industryScore(
accountIndustry: string | null,
icpIndustries: string[],
): number
- Case-insensitive exact match: If the account's industry matches any entry in your ICP industries list, the score is 100. Otherwise, 0.
- Returns 0 if the account industry is
nullor if the ICP industries list is empty.
Example: ICP industries = ["SaaS", "FinTech"]. An account with industry "saas" scores 100. An account with industry "Healthcare" scores 0.
Company Size (Employee Count)
function companySizeScore(
employeeCount: number | null,
min: number,
max: number,
): number
- Returns 100 if the employee count falls within
[min, max] - Uses linear falloff outside the range:
- Below min:
score = max(0, 100 - ((min - value) / min) * 100) - Above max:
score = max(0, 100 - ((value - max) / max) * 100)
- Below min:
- Score reaches 0 when the value hits 0 (below) or 2x the boundary (above)
Example (min=50, max=1000):
| Employee Count | Score | Reasoning |
|---|---|---|
| 500 | 100 | Within range |
| 25 | 50 | Halfway between 0 and min |
| 1500 | 50 | Halfway between max and 2x max |
| 0 | 0 | At or below 0 |
| 2000 | 0 | At 2x max boundary |
Revenue
function revenueScore(
revenue: number | null,
min: number,
max: number,
): number
Uses the same linear falloff algorithm as company size. The default ICP range is $1,000,000 to $100,000,000.
Revenue is stored as a string in the database (Drizzle numeric column). The fit engine parses it to a number using parseFloat. Invalid or empty values result in a score of 0.
Custom Attributes
function customAttributeScore(
sourceData: Record<string, unknown> | null,
customAttributes: Array<{ field: string; values: string[]; weight: number }>,
): number
- Each custom attribute is scored independently: 100 if the account's value matches any allowed value (case-insensitive), 0 otherwise
- Results are combined as a weighted average across all custom attributes
- Returns 100 if the
customAttributesarray is empty (no penalty for unconfigured attributes) - Returns 0 if
sourceDatais null
Example configuration:
{
"customAttributes": [
{ "field": "tech_stack", "values": ["Salesforce", "HubSpot"], "weight": 2 },
{ "field": "funding_stage", "values": ["Series B", "Series C"], "weight": 1 }
]
}
If an account has tech_stack = "Salesforce" (match, score 100) and funding_stage = "Seed" (no match, score 0), the custom attribute score is:
(100 * 2 + 0 * 1) / (2 + 1) = 66.7
Composite Fit Score Formula
The final fit score is a weighted average of all four dimension scores:
fitScore = round(
(industry * w_industry + size * w_size + revenue * w_revenue + custom * w_custom)
/ (w_industry + w_size + w_revenue + w_custom),
1 decimal place
)
Configuration Reference
The full FitConfig type:
interface FitConfig {
icpProfile: {
industries: string[];
minEmployees: number; // default: 50
maxEmployees: number; // default: 1000
minRevenue: number; // default: 1_000_000
maxRevenue: number; // default: 100_000_000
customAttributes: Array<{
field: string;
values: string[];
weight: number;
}>;
};
weights: {
industry: number; // default: 30
companySize: number; // default: 25
revenue: number; // default: 25
customAttributes: number; // default: 20
};
}
Setting a dimension weight to 0 effectively disables that dimension. If all weights are 0, the fit score is 0.
Related Pages
- Scoring Overview --- How fit scoring fits into the larger pipeline
- Engagement Scoring --- The other axis
- Account Scoring --- How person fit scores roll up to accounts
- Tier Assignment --- How fit + engagement determine tiers