Skip to navigation

Lead Scoring Optimization

View as Markdown

A marketing team can generate thousands of leads a day, and a sales team can only chase a fraction of them, so the question is which leads to call first. Lead scoring ranks the leads a rep should work by how likely each one is to convert, and a good score is the difference between a sales day spent on real prospects and one spent dialing at random. This example makes three kinds of lead score: it predicts whether a recently-engaged lead will convert in the coming weeks, it does the same conditional on recorded sales outreach in the next seven days, and it estimates how much revenue a lead is likely to bring so a team can prioritize by value and not just probability. Each one looks ahead from a lead and Kumo Engine learns the pattern from the lead’s own profile and the campaign responses and interactions connected to that lead in the graph.

Data and relationships

The example assumes a prepared Kumo Engine graph, a matching snapshot, and these tables:

TableRequired columnsPurpose
leadslead_id as the primary key, plus optional profile columns such as industry and titleThe leads, and the entity every query scores.
triggerstrigger_id as the primary key, plus lead_id and timestampThe campaign-response log. Each row is one time a lead responded to a marketing campaign, used to keep the prediction on leads that recently engaged.
eventsevent_id as the primary key, plus lead_id, timestamp, event_type, and valueThe interaction log. Each row is one action tied to a lead, and event_type names the action, for example email_open, click, meeting, sales_touch, or conversion. value carries the deal amount on a conversion row and is 0 otherwise.

Connect triggers.lead_id to leads.lead_id and events.lead_id to leads.lead_id. Mark timestamp as the time column of both the triggers table and the events table. These time columns place every campaign response and interaction in time, so each prediction is trained and scored using only the history recorded before the point it looks ahead from. The leads table has no time column of its own, so it is a static entity: each forward-looking window is measured from the scoring time, the moment you run the prediction, rather than from anything on the lead row.

Every query here predicts a future window and restricts the leads it scores. The target counts or sums a lead’s conversions over the days that follow the scoring time, and the trailing WHERE COUNT(triggers.*, -7, 0, days) > 0 keeps the prediction on leads that responded to a campaign in the previous week, because a cold lead with no recent engagement is not what a rep is deciding to call today. The forward window is half-open: it excludes actions at the exact scoring time and includes actions through the end of the window.

Predict which recently-engaged leads will convert

This query flags leads that responded to a campaign recently and are about to convert. COUNT(events.* WHERE events.event_type = 'conversion', 0, 30, days) counts only the conversion events a lead records over the 30 days that follow the scoring time, and > 0 turns “converts at least once” into a yes label. The trailing WHERE COUNT(triggers.*, -7, 0, days) > 0 keeps the prediction on leads that responded to a campaign in the previous 7 days, so the model ranks the leads a rep would actually be working right now rather than long-dormant records.

from kumo.engine import PredictiveQuery
query_text = (
"PREDICT COUNT(events.* WHERE events.event_type = 'conversion', 0, 30, days) > 0 "
"FOR EACH leads.lead_id "
"WHERE COUNT(triggers.*, -7, 0, days) > 0"
)
query = PredictiveQuery(graph, query_text)
query.validate()
print(query.get_task_type()) # binary_classification

Score conversion conditional on sales outreach

This query uses the same 30-day conversion target, conditional on a recorded sales touch in the next seven days. ASSUMING COUNT(events.* WHERE events.event_type = 'sales_touch', 0, 7, days) > 0 restricts training to historical lead and scoring-time pairs with that outreach in the following week. The recent campaign-response requirement still applies. Use this scenario to learn from conversion outcomes in that observed outreach subset. Conditioning on recorded outreach does not remove selection bias or establish how much contacting a lead changes its chance of conversion.

query_text = (
"PREDICT COUNT(events.* WHERE events.event_type = 'conversion', 0, 30, days) > 0 "
"FOR EACH leads.lead_id "
"WHERE COUNT(triggers.*, -7, 0, days) > 0 "
"ASSUMING COUNT(events.* WHERE events.event_type = 'sales_touch', 0, 7, days) > 0"
)
query = PredictiveQuery(graph, query_text)
query.validate()
print(query.get_task_type()) # binary_classification

Rank leads by the revenue they are likely to bring

A lead that is very likely to convert for a small amount is not always a better use of a rep’s time than one that is less likely to convert for a large deal. This query predicts the revenue itself instead of a yes-or-no label. SUM(events.value WHERE events.event_type = 'conversion', 0, 30, days) adds up the deal amounts on a lead’s conversion events over the next 30 days, so the model estimates the revenue a lead is likely to generate rather than only the chance it converts. A lead with no conversion in the window contributes 0, and the same trailing WHERE keeps the prediction on recently-engaged leads.

query_text = (
"PREDICT SUM(events.value WHERE events.event_type = 'conversion', 0, 30, days) "
"FOR EACH leads.lead_id "
"WHERE COUNT(triggers.*, -7, 0, days) > 0"
)
query = PredictiveQuery(graph, query_text)
query.validate()
print(query.get_task_type()) # regression

Reading the predictions

The first query returns a probability that a recently-engaged lead will convert in the coming weeks, which lets a sales team sort a day’s call list so the leads most likely to close sit at the top. The second estimates conversion conditional on recorded sales outreach, using only historical windows that satisfy the outreach condition for training. Its scores reflect that selected history; they do not measure the additional conversions caused by a call. The third query returns an expected revenue for each lead, which lets a team weigh a likely small deal against a less certain large one and prioritize by the value at stake.

All three are estimates from past behavior, not guarantees, and each is anchored at the time you run it, so the model uses only history recorded before that moment and never sees the outcome it predicts. The 30-day forward window, the 7-day recent-response window, and the 7-day outreach window are starting points; widen or narrow the number of days to match your sales cycle and how quickly reps follow up. Refresh the scores as new campaign responses and interactions arrive so each call list reflects the latest engagement.