Quick Nav
- A Closed-Won Date Cannot Help You Score a New Lead
- Choose the Conversion Event Before Choosing an Algorithm
- Build CRM Features That Existed When Each Lead Was Scored
- Start With Logistic Regression, Then Test Boosted Trees
- Measure Conversion Where the Sales Queue Actually Ends
- Write Scores Back to the CRM Without Losing the Audit Trail
- Expand Lead Scoring Only When the Sales Workflow Benefits
A Closed-Won Date Cannot Help You Score a New Lead
Every CRM record tells its story in order. A lead is created, it enters an outreach queue, a representative makes contact, and weeks later, if things go well, someone marks the opportunity closed-won. When that prospect first lands in the queue, the closed-won date does not exist yet.
That sounds obvious until you open a training export. The closed-won timestamp sits in the same row as the lead's company size and acquisition channel, looking like any other column. Feed it to a model and the model learns to read the answer off the page. Historical performance looks superb, and the model fails the moment it meets a lead whose future has not happened.
This guide covers implementation. It walks from timestamped CRM records to a Python scoring pipeline, then to evaluation built around how many leads your team can actually call, and finally to a costed decision about deployment. No measured model results or customer revenue studies sit behind it, so every number below is a design example and is labelled as one.
Choose the Conversion Event Before Choosing an Algorithm
Write the scoring specification first. It needs four parts: the scoring moment, the eligible population, the conversion event, and the observation window.
I define the scoring moment as scoring_time: the timestamp when an eligible lead first enters the outreach queue. Every predictive input must have been available by then. A workable design example scores new business prospects at first queue entry and predicts a closed-won event occurring after scoring_time and no later than scoring_time plus 90 days. That horizon is a design choice. It does not estimate anyone's sales cycle.
Booked meetings and closed deals answer different questions
A meeting-booking model tells an SDR team whom to call this morning. A closed-won model tells sales leadership which prospects tend to become revenue. Both are reasonable targets, yet they support different decisions, so each needs its own explicit event definition and its own horizon. Calling both of them "conversion" in a shared dashboard invites someone to compare numbers that measure separate things.
Keep the related quantities apart as well. Conversion probability, expected deal value, contribution margin, sales effort and the incremental effect of outreach are distinct, and the specification should state which decision each one informs.
Unfinished windows are unknown outcomes
A lead scored last week has had seven days to convert. Labelling it a non-converter corrupts the training data with false negatives. At extraction, retain window_end, outcome_timestamp and current_outcome_status, then exclude immature records from binary evaluation.
Open Is Not Lost
After a complete 90-day window, an opportunity that remains open can be labelled "no win within horizon" while keeping its open status. That label does not record a confirmed loss, and reports should never merge the two.
Build CRM Features That Existed When Each Lead Was Scored
The practical method is an event-based export that reconstructs one feature snapshot per scoring moment. A compact field specification looks like this:
| Field | Purpose | Availability rule |
|---|---|---|
| lead_created_at | Record origin | Known at creation |
| scoring_time | First queue entry | Defines the snapshot cutoff |
| account_id, company attributes | Firmographic inputs | Values as recorded by scoring_time |
| acquisition channel | Source of the lead | Known at creation |
| pre-score engagement counts | Behavioural inputs | Events available by scoring_time |
| outcome_timestamp, outcome_status | Label only | Never used as an input |
An example engagement feature counts recorded interactions during the 30 days ending at scoring_time. Use event timestamps and, where your CRM keeps them, availability timestamps. An email open backfilled into the record two weeks later was not visible to the original scoring job, even though it now appears on the exported row. A closed-won timestamp or a backfilled engagement event contaminates the initial score in exactly the same way.
Later sales-stage changes, eventual contract value and any activity after scoring_time belong outside the initial feature set.
Duplicates, gaps and splits
Resolve contact and account identities before splitting the data. Fuzzy matching on company names, using something like Levenshtein distance, catches the "Acme Ltd" and "Acme Limited" pairs that would otherwise leak one account across training and test sets.
Missing activity needs care. Zero recorded emails and an engagement feed that was switched off that month look identical as a zero, and treating the second as disinterest teaches the model something false. Preserve the distinction with an explicit flag.
For chronology, a proposed design uses January to June 2024 for training, October to December 2024 for validation and April to June 2025 for testing. With a 90-day horizon, those cohorts mature by 28 September 2024, 31 March 2025 and 28 September 2025. For evaluation on unseen accounts, exclude validation accounts that appear in training and test accounts that appear in either earlier cohort, then report the resulting sizes and exclusions. Document your entitlement to process each field, remove sensitive personal attributes, and review retention and role-based access, because CRM schemas differ and field reliability has to be recorded rather than assumed.
Start With Logistic Regression, Then Test Boosted Trees
Freeze the existing rules-based ranking as the baseline. Then fit two candidates on the same training cohort.
| Approach | Preparation | Interpretability | Operational complexity |
|---|---|---|---|
| Existing rules | Already in place | Directly inspectable | Lowest |
| Logistic regression | Encoded, scaled inputs | Coefficients can be inspected | Moderate |
| Gradient-boosted trees | Encoding plus tuning | Needs extra explanation work | Highest |
Logistic regression takes a weighted sum of a lead's features and passes it through a logistic function, which squeezes any value into a probability between zero and one. A large positive coefficient on a channel means leads from that channel tend to carry higher predicted probability, holding the other inputs constant. Those coefficients describe conditional predictive associations. They say nothing about what would happen if you pushed more leads through that channel.
Boosted trees can capture interactions the linear model misses, such as a channel that works only for larger companies. The cost is more tuning and harder explanations for the sales floor.
A minimal Python pipeline
pre = ColumnTransformer([ ("num", make_pipeline(SimpleImputer(), StandardScaler()), num_cols), ("cat", OneHotEncoder(handle_unknown="ignore"), cat_cols), ]) model = make_pipeline(pre, LogisticRegression(max_iter=1000)) model.fit(X_train, y_train) scores = model.predict_proba(X_eligible)[:, 1]The inputs are a training frame of point-in-time features with mature labels, and later a frame of eligible leads. The output is one probability per eligible lead. Because the imputer, scaler and category vocabulary sit inside the pipeline, fit learns them from training records only, and every later call applies them unchanged. The handle_unknown="ignore" setting is the explicit policy for a channel that first appears in production.
Weighting Can Bend Probabilities
Class weighting or resampling may improve ranking while distorting predicted probabilities. Test calibration separately, select settings on the validation cohort, freeze the pipeline, and leave test outcomes untouched until final evaluation. Record feature definitions, cohort boundaries, exclusion rules, package versions, random seeds and model version.
Measure Conversion Where the Sales Queue Actually Ends
Start with a fact about your team. On each queue date, let k be the number of eligible leads representatives can actually contact. That number comes from the outreach workflow, and every metric hangs off it.
- Precision at capacity = converters among the top k leads ÷ k
- Recall at capacity = converters among the top k ÷ all converters in that eligible cohort
- Precision-recall AUC summarises ranking quality across every possible cutoff
- Calibration compares predicted probabilities with observed outcome frequencies
- Ranking lift = precision at capacity ÷ the eligible cohort's conversion rate, undefined when the cohort has no conversions
Precision at capacity is the centrepiece because it matches the decision: of the leads we will call, how many convert? Recall shows how much of the cohort's conversion you leave beyond the cutoff. PR AUC helps when capacity shifts. Calibration matters if anyone reads the score as a literal probability.
Accuracy gets left out deliberately. When conversions are uncommon, a model that predicts non-conversion for almost everyone scores high accuracy and helps nobody.
Evaluate the rules baseline and both models on identical scoring dates, eligible records and mature outcomes. Historical conversions also reflect the outreach policy that produced them. Stronger retrospective precision shows better ranking against those recorded outcomes. It does not prove incremental sales, which is why a controlled rollout follows: comparable account groups assigned to model-ranked and existing-rule queues, matched capacity, contact attempts and overrides recorded separately, and a predeclared revenue or contribution-margin outcome. One illustrative plan observes 90-day conversions and reconciles refunds, credits and direct delivery costs through day 120.
Write Scores Back to the CRM Without Losing the Audit Trail
Deploy the frozen pipeline as a scheduled batch. An example schedule extracts at 05:00 and publishes by 06:00 in a documented business timezone; set yours around queue preparation and source-data refresh times.
- Extract eligible records.
- Validate fields against the specification.
- Reconstruct point-in-time features.
- Score with the frozen model.
- Write results back to the CRM.
The write-back carries score, scored_at, feature_snapshot_at, model_version and model_based_explanation, the last labelled as a predictive association. Representative overrides go into separate fields so nobody mistakes a human judgement for model output.
Use lead_id plus scoring timestamp and model version as an idempotency key. Log attempted and successful writes, retry transient failures and route persistent ones for investigation. Stale records should fail validation rather than receive fresh-looking scores, and write permissions should belong to the service account alone.
When The Batch Breaks
Retain the previous score version for rollback. If the job fails, expose the existing rules-based queue with a visible freshness status. Silently showing yesterday's scores as current is the failure mode that erodes trust fastest.
Monitor missing-field rates, score distributions, job failures, delayed outcomes and representative adoption, and investigate distribution shifts before retraining. Cost data cleanup, engineering, hosting, CRM integration, monitoring, maintenance and sales training as separate line items using real labour estimates and provider quotations.
Expand Lead Scoring Only When the Sales Workflow Benefits
The go-or-revise decision checks five things:
- A defined conversion target with a stated horizon
- Timestamp-safe features
- Improvement over the existing queue at contact capacity
- Manageable integration costs
- A measurable downstream outcome
Next actions follow the same order: document the scoring specification, audit a CRM export, build the baseline, evaluate chronologically, and agree on the rollout measurement plan. Name an owner for each before implementation starts.
If the audit shows unreliable source data or representatives following up inconsistently, fix those first. A boosted tree cannot rescue a queue that nobody works the same way twice. The decision record should separate ranking improvement from revenue improvement and set commercial value as incremental contribution margin less implementation and operating costs.
One date from the example design makes the stakes concrete. A model trained on leads from January 2024 cannot be honestly judged on its test cohort until 28 September 2025, more than eighteen months after its first training record was created.








