Scenarios
Lead qualification
Your ideal customer profile lives in one place, as prose the revenue team can edit. Every lead is judged against it in one request.
Source: examples/launch/02-lead-qualification.ts
The problem#
An inbound form arrives with a company, a role and a free-text message. Someone has to decide whether an account executive calls today, a solutions engineer follows up, or the lead gets a self-serve email. The criteria change every quarter and they belong to sales, not engineering.
The schema#
The ideal customer profile and the disqualifiers are context. Changing them is a config edit, not a deploy.
import { boolean, defineSchema, enumOf, object, score } from "jevascript"
export const qualify = defineSchema({
name: "lead-qualification",
version: "3",
instructions: `
Qualify an inbound lead for Northwind, a B2B payments API sold to engineering
teams. Judge the lead only on what the form and enrichment actually say.
Absence of evidence is not evidence of a poor fit — score it low, not negative.
`,
context: {
idealCustomer: `
Series A to Series C software companies, 50-500 employees, processing
payments as a core part of their product rather than to collect their own
subscription fees. Marketplaces and vertical SaaS are the strongest fits.
`,
disqualifiers: `
Agencies and consultancies reselling to clients. Companies in regulated
lending. Students and personal projects.
`,
competitors: ["Stripe Connect", "Adyen for Platforms", "Moov"],
},
output: object({
icpFit: score(0, 100, "This company matches the ideal customer profile."),
buyingIntent: score(0, 100, "This person is actively evaluating a solution right now."),
technicalDepth: score(0, 100, "The writer understands the technical problem they describe."),
switching: boolean({
describe: "The lead is moving away from a named competitor.",
falseWhen: "Merely mentioning a competitor as a reference point.",
}),
disqualified: boolean({
describe: "This lead matches one of the disqualifiers.",
trueWhen: "An agency, a reseller, regulated lending, a student, or a personal project.",
}),
segment: enumOf(
{
marketplace: "Connects buyers and sellers and needs to split or route funds",
vertical_saas: "Software for one industry that embeds payments for its customers",
platform: "Lets other businesses build on top of them and pay out to them",
internal: "Only wants to collect payment for their own product",
unclear: "Not enough information to tell",
},
"What kind of business is this",
),
}),
})Note the unclear option. A choice always picks something; giving it an honest "not enough information" option is how you keep it from guessing.
The decision#
Routing is arithmetic on values you can explain to a sales leader:
const q = await qualify({ company, employees, funding, role, message })
const priority = q.icpFit * 0.5 + q.buyingIntent * 0.35 + q.technicalDepth * 0.15
const route = q.disqualified
? "nurture — disqualified"
: priority > 70
? "book AE call today"
: priority > 45
? "solutions engineer follow-up"
: "self-serve onboarding email"The weights are a line of code. Changing them is a pull request with a diff, and a test can pin the routing for a known lead.
Why three scores instead of one#
"How good is this lead?" would give one opaque number. Three independent propositions give three numbers you can weigh, log, and chart separately. When the sales team asks why a lead was routed somewhere, the answer is icpFit 82, intent 40, not "the model said so".
What to test#
- A marketplace on Series B, switching from a competitor →
book AE call todayandswitching === true. - An agency asking for a reseller programme →
disqualified === true, routed to nurture regardless of the scores. - Change a weight → a snapshot test on
routefor a fixed set of stubbed answers catches the effect.