How We Built Prospecting OS — Architecture & Lessons
Prospecting OS started as a frustration. I was doing B2B outreach manually — scraping LinkedIn, writing messages, tracking responses in a spreadsheet. Every tool I tried either did one thing well (scraping, email, CRM) but nothing connected end-to-end.
So I built the thing I wanted to exist.
The Stack
- Next.js 14 (App Router) — server components for static pages, client components for interactive UIs. The layout system keeps the shell persistent across route changes.
- Supabase — Postgres with real-time subscriptions. The leads table streams updates to the UI without polling. Row-level security means each user only sees their own data.
- Claude API (Anthropic) — powers the message lab and lead scorer. Structured prompts with lead data produce personalized outreach messages and ICP fit scores with reasoning.
- Apify — handles LinkedIn and Google Maps scraping via their actor marketplace. API keys are proxied through our backend so they never touch the browser.
- Tailwind CSS — utility-first with CSS variables for theming. Dark mode by default because sales teams practically live in their CRM.
Architecture Decisions I'd Make Again
1. Server-side data fetching with React Context for state
We use a global AppProvider with useReducer for UI state (filters, search, modals) while data fetching happens server-side. This avoids the complexity of Redux or Zustand while keeping things predictable.
2. AI API keys in browser memory only
The Claude API key is entered by the user, stored in React state, and never persisted to localStorage or the database. It resets on page reload. This eliminates a whole category of security concerns.
3. Mock mode for development
A single toggle switches between live Apify scraping and a local dataset of 50 realistic leads. This means I can demo the product anywhere, anytime, without burning API credits or waiting for scrapers to finish.
What I'd Do Differently
- **Start with a Postgres schema, not ORM.** We went straight to Supabase client calls, which worked fine, but I'd add Prisma or Drizzle for migration management from day one.
- **Add rate limiting earlier.** The Apify and Claude APIs both have rate limits. We added client-side throttling late and should've built it into the API routes from the start.
- **Tests.** We shipped fast and tested manually. The product works but adding features now requires more regression testing than it should.
The Takeaway
You don't need a big team to ship a production AI product. The tools are there. What matters is knowing the workflow deeply enough to automate it — and being willing to ship something imperfect and iterate.