A three-stage pipeline that filters noise before a lead reaches your CRM

Why we built it
Every enterprise client we've worked with runs some version of the same process: a sales rep opens ten browser tabs to figure out whether an inbound lead is worth a same-day call or a next-quarter follow-up. That research is genuinely valuable signal — company size, hiring activity, recent funding, tech stack — and almost none of it makes it into the CRM before the first call happens.
We'd built pieces of this — signal collection, LLM-based scoring — for two separate client engagements before we saw it clearly as a product rather than a one-off integration each time. Both of those earlier engagements ended the same way: the client wanted the scoring layer to keep running after the project wrapped, which was the signal that told us it deserved to exist independently rather than as a bespoke build we'd re-write for the next client who asked. Being honest about it: we also built LeadIntent partly because we kept re-solving the same problem for different clients and wanted to stop starting from zero on every engagement.
What it does
LeadIntent sits between a lead source — a form fill, an inbound email, a signup — and the CRM. It gathers public signal about the lead's company and the person, scores intent and fit against a profile the sales team configures, and writes a score plus a short rationale into the CRM record before a rep ever opens it.
The scoring profile itself is configurable per sales team — what counts as a good fit for an enterprise seller and a self-serve SaaS company aren't the same thing, so the weighting between firmographic signals and behavioral signals is a setting, not something hardcoded into the model prompt. Reps can also flag a score as wrong directly from the CRM record, and that feedback feeds into how we tune the stage-two filter thresholds over time, rather than treating the pipeline as fixed once it's deployed.
How we built it
The scoring itself runs as a three-stage pipeline rather than one large model call. Stage one is retrieval: pull whatever public and first-party signal exists for the lead and turn it into an embedding-searchable set of facts. Stage two is a cheap, fast model pass that filters out the obvious non-fits — wrong company size, wrong geography, an obviously personal email domain — before any real model budget is spent on the lead. Stage three is a slower, higher-quality model call that only runs on leads that survived stage two, producing the actual score and the rationale a rep reads. Splitting it this way means the expensive call only runs on the leads worth the cost, instead of on every single one that arrives.
For infrastructure, we run on Cloudflare Workers rather than a traditional always-on server. Lead volume for any given client is spiky by nature — a webinar, a product launch, or a paid campaign can multiply the inbound rate for a day and then drop straight back to baseline. A traditional server means provisioning for the spike and paying for idle capacity the rest of the time, or under-provisioning and dropping leads during exactly the moment they matter most. Workers scale with the request pattern directly, so we're not paying for capacity between spikes, and there's no server to patch or restart at 2am.
For the database, we chose Neon over a traditional managed Postgres instance mainly for branching. Every schema change to the scoring pipeline gets tested against a full branch of production data before it touches the real database, which is a meaningfully different workflow from spinning up a synthetic staging environment that never quite matches production data shape. Neon's scale-to-zero behavior also matters economically for smaller client deployments without consistent traffic — we're not paying for full-time compute on a database that sits idle outside business hours for a lead-gen tool serving a single region.
On the integration side, we treat the CRM write-back as its own reliability problem rather than an afterthought. Incoming leads land on a Cloudflare Queue rather than being scored synchronously in the request path, so a slow model call or a temporary CRM API outage doesn't drop a lead — it retries until the write-back succeeds. That queue-based design has mattered more in practice than the scoring logic itself: the failure mode nobody wants is a lead silently never reaching the CRM at all.
What went wrong
Our first version of the pipeline used a single-stage model call for everything, and it was expensive in a way we hadn't modeled properly before launch — every lead, including the obvious non-fits, ran through the same high-quality, highest-cost model. Watching the first month's API bill against actual usable output was the moment we redesigned it into the current three-stage filter. That redesign, not the original build, is the version we'd actually call the product.
We also underestimated how much the retrieval stage would matter. Early scoring quality was inconsistent not because the model's reasoning was bad, but because the facts it was reasoning over were incomplete or stale. We ended up investing more engineering time in stage one — the unglamorous retrieval and data-freshness problem — than in the model prompting itself, which wasn't what we expected going into the build.
In one early deployment, the stage-two filter was tuned too aggressively for that team's market and was quietly discarding a segment of leads that turned out to matter — smaller companies that, for this client, were disproportionately likely to convert quickly. We'd validated the filter against general fit criteria but not against their actual conversion data before turning it on. We now treat that validation step as mandatory before any new deployment goes live, rather than something to check after the fact.
What it costs to run
The three-stage filter is the direct reason AI spend stays proportionate to genuinely qualified leads rather than total inbound volume — in early testing, the majority of leads never reach the expensive stage-three call at all. Cloudflare and Neon's usage-based pricing means the infrastructure cost for a smaller deployment stays close to negligible outside of the model API spend itself.
Where it is now
LeadIntent is running in production across a small number of client deployments, each with its own scoring profile. It's early — we'd rather grow deployment count deliberately, validating the pipeline against each client's actual conversion data as described above, than scale distribution before scoring quality is consistently solid across different markets and sales motions.
What this means for your project
The three-stage cost-filtering pattern, the Cloudflare Workers deployment model, and the Neon branch-per-change workflow are the same setup we'd bring to an enterprise client needing a similar pipeline — none of it is specific to lead scoring, it applies to any workflow where an expensive AI call should only run on the inputs that earn it. The queue-based write-back is the part we'd insist on even if a client asked us to cut scope, because a silently dropped lead is a worse failure than a slightly wrong score.
Working on something similar?
Book a call. We'll listen, tell you what we'd do, and say so if the answer is “not us.”
Book a call