02 · SDK Lead generation

Lead Generation SDK

Which of these people is worth the call?

Four thousand names, six people on the phones, two days before the promo closes. Rank the list so the team opens with the calls most likely to land and stops where the next call is not worth the minute — and, if you want it, get the version of the offer each name is likeliest to say yes to.

The twin orders a redeemer above a non-redeemer 76 times in 100 across 12,480 held-out offers with known outcomes. The study is on the research pages.

Quick start

from eggai import LeadGenerator

customers = {
    "c_8813": "fortnightly, coupon-led, small baskets",
    "c_2291": "weekly family shop, rarely uses vouchers",
}

leads  = LeadGenerator(api_key)
ranked = leads.rank(
    customers,
    offer        = "Fresh Picks Weekend — 15% off produce",
    guidelines   = "hold the 15%, the aisle may change",
    tailor_offer = True,   # False = score and rank only
)

ranked[0].score   # 0.71
ranked[0].reason  # "buys dairy most weeks, produce rarely"
ranked[0].offer   # "Fresh Picks Weekend — 15% off dairy"
ranked.above(0.5) # the slice worth calling

Pass {id: profile} as above, or a list of rows ({id, profile, ...}) when contacts carry extra traits. The offer and guidelines are set once for the run, not per row.

How a run works

Ranking needs only the judge. The twin runs once per contact, reading that person's profile and history before it is shown the offer. Contacts never see each other's answers, so a ranking is many independent judgements rather than one pass down a list.

Tailoring brings in the proposer — which is exactly what the tailor_offer / tailorOffer switch decides. When it is on, Offerwright drafts one variant per contact inside your guidelines — moving the aisle, holding the discount — and the twin scores that variant instead of the generic offer. The twin is the half that can be checked against held-out outcomes; the proposer cannot be checked that way, which is why it never gets the last word.

Tuning the list

ParameterMeaning
thresholdThe score at or above which a contact is qualified (default 0.5, range 0.05–0.95). Below it, the name is still scored and ranked — it is just explicitly not worth the call.
max_leads / maxLeadsCap the deliverable list. Overflow contacts keep their score and rank but are demoted — qualified is false. Pick how many your team can actually call.
tailor_offer / tailorOfferFalse: score and rank the offer as written. True: also draft a per-contact variant inside the guidelines.
constraintsNumeric backstops on tailored terms — same shape as Offer Optimization.

The result object

FieldMeaning
rankPosition in the calling order — qualified first, by score.
scoreThe twin's acceptance probability for this contact (of the tailored variant, when tailoring is on).
reasonThe twin's words — why this name lands where it does.
offerThe tailored offer for this contact, when tailoring is on.
qualifiedAbove threshold and inside max_leads.
rawThe untouched server row, ladder included.

ranked.above(0.5) slices by score; ranked.qualified is the deliverable list; iteration order is rank order.