Part 3 of 7 · Lead intake bot series ~5 min read

How the bot reads a lead

A lead reaches the qualifier as one clean object: a contact, a company, a free-text message, and a source tag. Reading it well is the difference between a hot ping that lands a meeting and a hot ping that wakes a rep at 2am for a tire-kicker. Three small extractors run in parallel against the message. Each looks at one thing only. Each returns a confidence score the move-picker reads before it decides anything.

Key takeaways

  • Three small extractors run in parallel against the lead message.
  • Intent (demo, quote, info, partnership, support, other), urgency (deadlines, switch language), fit (industry, size, role, budget).
  • Plus a free passive enrichment from the email domain — free-mail vs business, partner allowlist, competitor list.
  • Each extractor returns a 0–1 confidence score the move-picker reads before it decides anything.
  • Splitting into narrow extractors makes failures small and easy to fix.

Three extractors, one consolidated read

Three parallel extractors plus passive enrichment, consolidated into one structured lead A diagram with one input box at the top labelled "Lead from intake — contact, company, message, source". Below it, three extractor boxes run side by side in parallel — Extractor A reads the message for intent (what they're asking for: demo, quote, info, partnership, support, other), Extractor B reads for urgency cues (deadlines mentioned, words like ASAP, dates, named events, current-vendor mentions), Extractor C reads for fit signals (industry, company size hints, role hints, budget mentions, named tools or platforms). Each runs the message through Bedrock Claude Haiku 4.5 with a small focused tool definition that returns a typed object plus a 0-to-1 confidence score. To the right sits a fourth box, "Passive enrichment", which doesn't call the model at all — it does a free DNS lookup on the email domain to detect free-mail providers (gmail.com, yahoo.com, etc.) versus business domains, checks the domain against a small allowlist of partner companies, and notes whether a phone number was provided. Below all four, a single consolidation box reads "Structured lead — intent, urgency, fit signals, enrichment, per-field confidences". An output arrow on the right points to "to move-picker, with confidences attached". A note at the bottom: each extractor knows one thing only — easier to tune, easier to debug, easier to swap. Lead from intake contact · company · message · source extractor A Intent • demo / quote / info / partnership / support / other • Bedrock Haiku tool returns label + score extractor B Urgency • deadlines, dates, “ASAP,” named events • current-vendor / switch language • level: hot / soon / none extractor C Fit signals • industry, size hints, role, budget mentions • named tools or platforms in use • competitor mentioned passive enrichment (no model call) • Email domain: free-mail vs. business; partner-allowlist hit; competitor-list hit • Phone present? · Source campaign tag · Country from form / IP (if available) Structured lead intent · urgency · fit · enrichment · per-field confidences → to move-picker, with confidences attached
Fig 3. Three small extractors plus a free enrichment, run in parallel. Each one knows one thing only — easier to tune, easier to debug, easier to swap. The move-picker reads the consolidated record next.

Why three extractors instead of one big prompt

You could ask one large prompt to read a lead and return everything — intent, urgency, fit, summary, suggested move — and it would mostly work. But “mostly” is the problem. When the consolidated answer is wrong, you don’t know which sub-decision went wrong. The model entangles them and you end up rewriting a 600-line prompt to fix one symptom. Splitting into three narrow extractors gives you three small failures instead of one big one. When intent is wrong, you tune the intent prompt without breaking urgency. When urgency starts overweighting words like “today” (which often means “today I had a thought,” not “I need this today”), you fix it with a one-line clarification in the urgency tool description. Fit and intent stay exactly the same.

The three extractors run in parallel against Bedrock Haiku. Each one has a tightly scoped tool definition that forces the model to return a typed object plus a 0-to-1 confidence score. That’s three model calls per lead instead of one. But each call is small and fast, and the parallelism means wall-clock time is the slowest of the three, not their sum. Per-lead spend is still measured in tenths of a cent.

Extractor A — intent

What is the lead actually asking for? The intent extractor returns one of a small fixed set: demo, quote, info, partnership, support, other. The label matters because the four moves downstream branch heavily on it. A support intent is almost never a hot route — it belongs in your help system, not a sales rep’s phone. A partnership intent goes to a different owner than a quote. A clear demo request from the right ICP is the easiest hot ping you’ll ever send. The confidence score lets the move-picker downgrade an uncertain “is this a quote or just info?” lead from hot to warm, even if everything else lines up.

Extractor B — urgency

Hot leads have time pressure. The urgency extractor reads the message for explicit deadlines (“by end of June”), implicit ones (“our current contract expires next month”), named events (“before our board meeting”), and switch language (“we’re replacing X,” “we’re evaluating alternatives to Y”). It returns a level — hot, soon, or none — with a confidence score and the specific phrases it picked up. The phrases matter. When a rep gets the hot ping, the message reads “urgency: hot — mentioned ‘contract expires June 30’,” not just “urgency: hot.” They can check the AI read the message correctly before they pick up the phone.

This extractor is the one most prone to false positives. Words like “quickly” and “soon” show up in friendly throwaway sentences as often as in real urgency. The tool definition includes explicit examples of the difference, and the confidence score reflects when the model is unsure. The move-picker is calibrated to require either a high-confidence hot urgency or a clear deadline phrase before it pings the team. An enthusiastic but vague “hoping to hear back soon” doesn’t qualify.

Extractor C — fit signals

The third extractor reads the message for everything that helps decide whether the lead matches the ICP. Industry mentions. Company-size hints (“our team of 30,” “we’re a small agency”). Role mentions (“I’m the head of operations,” “I run procurement”). Budget signals (“we have around $5K/month allocated”). Named tools or platforms in use (“we’re currently on HubSpot”). Competitor mentions. It also flags the absence of those signals. A one-line message with no fit signals at all is a different kind of lead than one with three.

The fit extractor doesn’t score the lead against your ICP. That’s the move-picker’s job in the next post, with the ICP doc as input. The extractor just pulls out the raw signals so the scoring step has something concrete to compare against.

Passive enrichment, free of charge

One more piece of context arrives without a model call. The email domain is checked against three small lists. A free-mail providers list (gmail.com, yahoo.com, outlook.com, etc.) versus business domains. A partner allowlist (companies you have an existing relationship with, where their leads should always route hot to a specific owner). A competitor list (companies whose leads should be archived with a reason). A phone-present flag, the source campaign tag, and a country code (when the form or ad platform supplied one) round out the enrichment. None of this needs Bedrock. All of it costs essentially nothing.

The free-mail-versus-business signal is surprisingly load-bearing for B2B leads. A lead from jane@somecompany.com is a different lead than the same words from jane@gmail.com. Not always, but often enough that the move-picker should know.

What the move-picker sees next

By the end of this step, the qualifier has built a structured lead: { contact, company, message, source, intent: { label, confidence }, urgency: { level, confidence, phrases }, fit: { signals[], absent[] }, enrichment: { domain_class, partner, competitor, phone_present, country } }. The next post is about how the move-picker reads this object, scores it against your ICP file, and picks one of four moves. Two override gates handle the cases that bypass scoring entirely.

All posts