The Outbound Prospecting Tool
How Ops Hub finds, enriches, and safely pushes prospects to HubSpot.
What it is
The Outbound Prospecting Tool automates the top-of-funnel process: find potential clients by industry and location, enrich them via Apollo.io, generate personalised email sequences via Claude, and push non-excluded prospects into HubSpot for the AM to review. The exclusion gate is the most important component -- it ensures BTT never cold-emails an existing or previously contacted client.
Data sources
- Google Places API: finds up to 20 companies matching [industry] in [location] with names, websites, and basic metadata.
- Apollo.io: enriches organisations by domain and finds decision makers (founder, owner, C-suite, director -- max 2 per org). Bulk-matches email and phone (costs Apollo credits).
- Claude API (Haiku): generates a 3-step personalised email sequence per contact, grounded in BTT brand context from lib/brand-context.js.
- HubSpot: creates contact + company + deal for each non-excluded prospect. Deal is named after the invitee email (BTT convention).
The exclusion gate
Every prospect goes through lib/prospecting/exclusions.js before any HubSpot write. The gate runs two checks -- once at the company/domain level and once at the contact/email level:
- Domain match against the clients Supabase table (retained clients are always excluded)
- HubSpot company by domain: lifecycle in the always-exclude set (retained client, signed, closed won) OR has an open deal in an exclude-deal-stage
- HubSpot contact by email: same lifecycle check, plus Phase 1 Lead conditional (excluded only if outreach_campaign tag is set, meaning prior outreach already happened)
- HubSpot deals: open deal at Replied stage or any excluded stage
If any check fires, the prospect is logged to prospecting_exclusions with a reason code and the run_id for audit. The domain is never touched again for that run.
Orchestrator flow
The single entry point is lib/prospecting/orchestrator.js. One run = one queue item (industry + location combo):
- Load the queue row (prospecting_queue)
- Open a prospecting_runs row (status = running)
- Google Places: find 20 candidates
- Pre-screen all candidate domains via the exclusion gate
- Apollo org enrichment (costs credits -- gated by enrich=true option)
- Apollo people search: decision makers per org
- Cost confirmation: orchestrator returns an estimate; caller decides whether to proceed
- Apollo bulk-match with reveals (email + phone)
- Pre-screen all candidate emails via the exclusion gate
- HubSpot push per surviving prospect (gated by write=true option)
- Claude: generate 3-step email sequence per contact (gated by generateEmails=true option)
- Close out run and queue rows
Default mode is dry-run. Each escalation step must be explicitly opted in.
Per-prospect review before push
The Ops Hub UI at /(hub)/prospecting shows candidates for each run before they are pushed to HubSpot. The AM reviews each candidate and selects which ones to push. Unselected candidates are recorded but not created in HubSpot.
Permanent domain blocklist
A prospecting_blocklist table holds domains that should never appear as candidates regardless of exclusion-gate state. Manageable via the Settings UI. A blocklisted domain is skipped at the pre-screen stage before Apollo spend is incurred.
Status sync
The /api/cron/prospecting-status-sync cron (hourly :15) reads HubSpot deal stages for pushed prospects and syncs the outreach_status column on prospecting_audits. The /api/cron/prospecting-replies cron (every 30 min) detects prospect replies in HubSpot and flags them on the run.
Phases
- Phases 1-3 (shipped): core orchestrator, per-run candidate review, per-prospect push selection, status sync, snapshot dashboard, permanent blocklist, BTT TOV email generation with regenerate action.
- Phase 4 (future): Vercel Workflow refactor for runs exceeding ~25 prospects per push. Requires Next.js 14 to 15 upgrade. Not needed until scaling beyond current volumes.
Cost tracking
Each run records Apollo credit spend, Claude API cost, and DataForSEO cost in prospecting_runs.total_cost_gbp with sub-breakdown columns. These feed the AI Costs dashboard provider breakdown.
Key files
- lib/prospecting/orchestrator.js -- main entry point
- lib/prospecting/exclusions.js -- the exclusion gate
- lib/prospecting/apollo.js -- Apollo API wrapper
- lib/prospecting/places.js -- Google Places wrapper
- lib/prospecting/email-gen.js -- Claude email generation
- lib/prospecting/hubspot.js -- HubSpot push helpers
- app/(hub)/prospecting/ -- UI pages