Skip to content
Back to all work
AI Systems2026

PE Deal Sourcing & Qualification

Lead discovery through to qualified deal for a private-equity funnel — BANT scoring with a rationale attached, and a heuristic that answers when the model can't.

Role
Architect & sole engineer
FastAPIPython 3.11SQLAlchemyNext.js 14TypeScriptRechartsSWR

The application

Screens, captured from the running app

Booted against its seeded database and walked route by route — these are the real pages, not mockups.

Deal flow dashboard with pipeline count, qualified leads, win rate, stage funnel and lead sources.
60 leads across all eight stages. The funnel is a direct read of stage counts, not a reconstruction from activity logs.
Lead sourcing screen with CSV import and enrichment status per lead.
Discovery and enrichment — bulk import from an Apollo or ZoomInfo export, then firmographic fill.
BANT qualification view showing per-dimension scores with rationale and red flags.
Qualification with a rationale per dimension. A deal team acts on “reached the operations lead, not the sponsor” — never on a 74.
Kanban-style deal pipeline board with stage columns and SLA timers.
The pipeline board. Stage transitions are explicit and timestamped, so time-in-stage is data.
Outreach sequences across LinkedIn, email, Meta and phone with per-step delays.
Multi-touch sequences with per-step delays and enrollment tracking.

The problem

A deal team's funnel is wide at the top and the work of narrowing it is manual: pull an Apollo or ZoomInfo export, enrich it, decide who is worth contacting, run a sequence, and record what came back. Most of that is judgement applied at volume, which is exactly the shape of work that gets skipped when the list is long.

Qualification with a rationale

Leads are scored on BANT — budget, authority, need, timing — plus firm-specific criteria. The model returns a rationale per dimension and any red flags, not a bare number.

This is the design decision the rest of the system hangs on. A deal team will not act on a score of 74. They will act on "authority: reached the operations lead, not the sponsor" — because that sentence tells them what to do next. A number that cannot be argued with is a number that gets ignored.

There is a deterministic heuristic scorer behind the same interface, using firmographics and engagement signals. It is worse than the model and it always answers — so the pipeline has no dead state when the API is down, over budget, or unavailable for policy reasons.

The pipeline is a state machine

new → enriched → contacted → meeting_set → qualified → diligence → closed_won | closed_lost

Transitions are explicit and timestamped, which makes time-in-stage a property of the data rather than something reconstructed later from activity logs. The funnel chart on the dashboard is a direct read of stage counts — the current seed carries 60 leads spread across all eight stages, with an 11-lead qualified cohort and a 50% win rate.

Source attribution rides along: inbound, LinkedIn, ZoomInfo, Apollo, manual. Knowing that inbound produced 20 of 60 leads is only useful next to knowing how many of each source converted, so the analytics join stage to source rather than reporting them separately.

Outreach

Multi-touch sequences across LinkedIn, email, Meta and phone, with per-step delays and enrollment tracking. Drafting is model-assisted with the same mock fallback. CRM sync is Salesforce-shaped and logged rather than sent, because a demo that writes to a real CRM is a demo nobody can run twice.

Stack

FastAPI with async SQLAlchemy over SQLite in development and PostgreSQL in production; Next.js 14 with SWR and Recharts on the front; the full mock provider layer meaning the entire funnel — import, enrich, score, sequence, report — runs with zero API keys.

You have a process thatshould be a system.

Tell me what arrives, who has to act on it, and where it currently falls over. That conversation is usually enough to scope the build.