HubSpot
Source of truth for companies and contacts. Everything reconciles back here; local storage is a cache, never an authority.
I ran an AI-agent sourcing pipeline that found, scored, verified and shipped event-organizer leads for Startup World Cup — the Pegasus Tech Ventures competition whose global final lands in San Francisco on 6 November 2026.
Four continents of regional coverage, a CRM held as the single source of truth, and a data-integrity audit that caught the failure which would have made most of the pipeline worthless before a single email went out.
Startup World Cup runs regional qualifying events all over the world; the winners fly to San Francisco for the final. Someone has to find the people who can host a regional — the accelerator directors, chamber-of-commerce heads, ecosystem builders and festival organizers who already convene a startup crowd in their city — and get them into the CRM in a state good enough to email.
That was my remit at Jackson Venture. Three tracks ran in parallel — Regionals, Sponsorships and Seminar — against a stack where HubSpot is the source of truth, Apollo sends the sequences, and Clay fills the gaps. The constraint was never the CRM's throughput. It was finding real people, in the right cities, with contact details that hold up.
My role. I specified the rules, drove an AI coding agent to build and run the pipelines, then verified the output before anything shipped. I did not hand-write the codebase — I directed it, tested it against reality, and owned what came out the other end. Every number on this page traces to a file or a dated report in the repository.
Eight stages, with a hard line drawn between the parts that touch the network and the parts that must stay deterministic. Scoring and dataset generation run offline so they are reproducible and testable; discovery and writes are isolated behind their own scripts and gates.
Candidates are not ranked by vibes. Organization Fit (1–5) scores on two paths — an existing event with real attendance and multi-year history, or a standalone convener with a network and the power to run one — and takes the higher. Persona Fit (1–5) scores decision authority, from founder or executive director down to affiliated volunteer. The two sum to a 2–10 Total Fit Score, carried alongside a 0–100 confidence figure that drops when evidence is thin.
The rule that mattered most: missing evidence lowers confidence, it never inflates a score. A guessed email or a single unverified source costs points. That principle is what made the integrity audit in section 05 possible at all.
Source of truth for companies and contacts. Everything reconciles back here; local storage is a cache, never an authority.
Sequencing and outreach. Read-only to the agent — sequence assignment stays a human decision.
Enrichment of last resort for LinkedIn URLs and emails, run in deliberate batches with the return audited.
Web search for discovery, LLM extraction for parsing, a Python service and CLI pipelines to hold it together.
The first Asia run nearly failed. Singapore returned zero contacts — in one of the densest startup ecosystems on earth — which is the kind of result that tells you the tool is broken, not the market. Three faults, diagnosed and fixed:
The contact finder kept returning "Unable to determine" instead of real names, so I moved it to LinkedIn-based resolution. An over-aggressive quality filter was discarding genuine organizations — Enterprise Singapore and NUS among them — so I loosened it. And the event filter was inventing plausible-sounding events that did not exist, so I hard-restricted it to verifiable dates in the target window.
Singapore went from 0 to 6. Every European country hit the 3–6 target, most at 6.
| Region | Countries | Contacts | Note |
|---|---|---|---|
| Asia | 11 | 38 | 1 duplicate removed at dedup |
| Europe | 21 | 122 | Every country hit target |
| Americas | — | 207 | North, Central and South |
| MENA / Africa / Oceania | — | 123 | Thinnest data coverage |
| Shipped import-ready | 490 | 17 columns, clean, deduped |
A further 239 events were prepared as their own import, and a 300-city coverage map classified every location as actively covered, lapsed, in progress or whitespace — the artifact that answers "where are we not looking?" rather than "who did we find?".
I reported the weakness with the wins. Email coverage across Asia and Europe was 2 and 3 contacts respectively — the contacts had names, companies and LinkedIn, but the email vendors simply have no data for those markets. And where no genuine event existed in the window, the column shipped empty rather than filled with something invented. Both went in the end-of-day report on the day they were found.
The recurring operational question was simple to state: which qualified contacts in HubSpot are not yet in the Apollo list or sequence, and are actually ready to email? Answering it honestly is where most of the pipeline's value turned out to sit.
| Stage | Records | What removed them |
|---|---|---|
| Qualified in HubSpot | 2,364 | Starting pool |
| Not yet in Apollo | 1,213 | 1,151 already sequenced |
| Has an email address | 75 | 1,138 unreachable — no email at all |
| Passed every field check | 61 | Blank fields would break the send |
The headline is not the 61. It is the 1,138. Eighty-three percent of the qualified pool has no email address, which means targeting quality is irrelevant until enrichment is solved — and roughly 655 of them looked enrichable, so the fix was buyable. That reframing went to the decision-makers as the single biggest constraint on scaling Regional outreach, with a recommendation attached: approve the 61, but trim the seven contacts sitting at the same chamber of commerce down to one or two before sending.
After extending the working batch to 504 ranked contacts, I asked for something that sounds mundane: confirm the LinkedIn URL resolves to a real person. It does not sound like the most important task of the month. It was.
LinkedIn answers HTTP 999 to every anonymous request, so a real profile and a fake one look identical to any automated checker. Verification meant searching for each person by name and employer and comparing the returned profile slug against the stored one — around 470 searches.
The failure pattern was consistent enough to diagnose. Slugs had been built by gluing a person's name to their company or city — but genuine LinkedIn slugs carry a random numeric suffix that cannot be guessed. Every synthesized URL was therefore wrong by construction.
Pulling the thread found fabricated people, not just fabricated links. Two examples, anonymised:
Both traced to the same upstream import batches, which had supplied 506 of the 995 gap contacts. I flagged the source, not just the symptoms, and asked for twenty rows to be hand-checked before anything from those batches went live.
Rows 101–504 were split by verdict: 218 clean (140 of them with the URL corrected rather than merely accepted), 123 quarantined, 63 sent back for re-sourcing.
Then the counter-intuitive move. Contacts with no LinkedIn URL at all turned out to be the better pool: 91% had an event name against 2% for the cleaned batch. Records with a fabricated URL were fabricated throughout; records sourced properly from real events had the event but no URL. So I rebuilt the working list around the 141 no-URL records and sent those to Clay for enrichment.
Clay returned 85 of the 141. Fifteen already existed in other sheets and were removed, leaving 70 clean rows with LinkedIn populated 70/70 — and one open question I put back to the vendor rather than swallowing: was dropping 56 of the 141 deliberate, or a silent failure?
An agent with CRM credentials and no brakes is a liability. The rails I worked behind, and insisted on:
No record reaches HubSpot unless two separate environment flags are both set. Schema changes need a third. Dry-run is the default state, not a mode you opt into.
The agent is structurally forbidden from writing the Apollo sequence field. Deciding who gets emailed is a person's call.
Scores cite only source URLs already collected. Thin evidence lowers confidence rather than quietly filling the gap.
When two files went into the wrong Apollo list and sequence on 3 August, the first move was a revert manifest — an exact record of what was uploaded so it could be pulled back cleanly.
Two-thirds of the days logged in this project ended with "Cost today: none — all work was read-only." That is the point. Analysis is cheap and reversible; sends are neither.
A 12,415-record sponsorship list had to go into Clay with a unique identifier. The naive version of this job — group by company name, keep one — destroys real records, because the same company legitimately appears twice with different countries or different sponsorship status.
So the rule was narrower: remove a record only when it is fully identical to another, field by field. Twenty-eight duplicate name groups covering 57 records were compared line by line; only 19 exact duplicates were removed. Every same-name-different-data record survived.
| Item | Count |
|---|---|
| Original records | 12,415 |
| Duplicate name groups examined | 28 |
| Records inside those groups | 57 |
| Fully identical, removed | 19 |
| Delivered to Clay, with sequential unique ID | 12,396 |
The source CSV was never modified. The deduplication and the final import file were separate artifacts, each with a written record of what changed and why — so the decision could be audited or reversed by someone who was not in the room.
The pattern that produced everything above: state the rule precisely, let the agent execute at volume, then check a sample against reality before it scales. The agent is fast and tireless and will confidently produce plausible garbage; the value I add is knowing which output to distrust and saying so early.
These are real instructions from the working log — they are the whole method in miniature:
Every working day closed with a written end-of-day summary in the same shape: what was done, the cost incurred, the blockers, and the specific decisions needed from someone senior. Not a status update — a decision request. "Review the 340 duplicate flags, it costs nothing and unlocks 89 event-linked contacts" is a sentence a manager can act on in ten seconds.
I can take a vague growth objective — "get us regional event organizers, worldwide" — and turn it into a scored, deduplicated, verified pipeline with an audit trail. I will use an AI agent to do in an afternoon what would take a team a fortnight. And I will tell you, in writing and on the day, when 78% of what it produced does not survive contact with reality.
The second part is the job.
More of my work — RAG platforms, multi-agent systems, and production AI backends.
Back to portfolio