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
import { LeadGenerator } from '@eggai-sdk/core'; const customers = { c_8813: 'fortnightly, coupon-led, small baskets', c_2291: 'weekly family shop, rarely uses vouchers', }; const leads = new LeadGenerator(apiKey); const ranked = await leads.rank(customers, { offer: 'Fresh Picks Weekend — 15% off produce', guidelines: 'hold the 15%, the aisle may change', tailorOffer: true, // false = score and rank only }); ranked.at(0).score; // 0.71 ranked.at(0).reason; // "buys dairy most weeks, produce rarely" ranked.at(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
| Parameter | Meaning |
|---|---|
threshold | The 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 / maxLeads | Cap 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 / tailorOffer | False: score and
rank the offer as written. True: also draft a per-contact variant inside the
guidelines. |
constraints | Numeric backstops on tailored terms — same shape as Offer Optimization. |
The result object
| Field | Meaning |
|---|---|
rank | Position in the calling order — qualified first, by score. |
score | The twin's acceptance probability for this contact (of the tailored variant, when tailoring is on). |
reason | The twin's words — why this name lands where it does. |
offer | The tailored offer for this contact, when tailoring is on. |
qualified | Above threshold and inside max_leads. |
raw | The untouched server row, ladder included. |
ranked.above(0.5) slices by score; ranked.qualified is the deliverable
list; iteration order is rank order.