Case study · Territory intelligence

RoutePro: a whole market, ready to work.

Turning an entire geographic market into a sales territory someone can actually work.

Role
Founder, sole builder
One modeled territory
8,378 facilities in 35 miles
Service value found
$2M+ a year, one market
Contact coverage
About 90%

The problem

No database lists these markets.

Local commercial service companies (hood cleaners, fire inspectors, grease trap pumpers) don't have a traditional account database. Their market is spread across Google Maps, directories, public datasets, and thousands of small websites.

Standard filters fail here. Only 38% of hood cleaners sit under any one LinkedIn industry, and the matching industry code returns 184,000 contacts, mostly the wrong ones.

How do you turn every potential business in a territory into a ranked list of accounts a salesperson can actually work?

What I built

From raw places to a ranked territory.

It combines 4+ primary data sources (paid databases, public data, and AI-assisted enrichment) and reaches about 90% contact coverage. Each business is stored once and shared across every customer; contacts are licensed separately and unlocked per account.

It's built to give field sales teams the same account-mapping workflow that cut my own mapping from about 4 hours a day to 30 minutes. At a modeled 10-rep deployment, that is about 9,100 hours a year, or roughly 4.4 full-time employees of capacity.

  1. 1
    Market dataOverture Maps and OpenStreetMap places, loaded into PostGIS
  2. 2
    Business discoveryEvery business inside a territory the customer draws on the map
  3. 3
    EnrichmentWebsite, category and location data
  4. 4
    ICP classificationAccounts classified by vertical
  5. 5
    Score and tierTier A, B and C so reps start with the best accounts
  6. 6
    Decision makersLicensed contacts, unlocked and billed per account
  7. 7
    Territory mapMapbox view of the whole ranked territory
  8. 8
    Routes, CRM and outreachRoute planning, contacts, campaigns and a reply inbox inside the app

Architecture

The stack, and why each piece.

Next.js 15TypeScriptTurborepoPostgreSQL 16PostGISNeonPrismaClerkMapbox GLOverture MapsPlusVibeVitestGitHub ActionsClaude Code
LayerChoiceWhy
AppNext.js 15, TypeScript, TurborepoOne deployable for UI and API, with shared packages for data, jobs and providers
DatabasePostgreSQL 16 and PostGIS on NeonGeospatial queries are the core of the product
Data accessPrisma for CRUD, raw SQL for geoPrisma can't model geography types, so geo reads use hand-written SQL functions
TenancyClerk orgs and row-level securityThree separate layers of tenant isolation, so any one can fail without leaking data
MapsMapbox GLClustering at territory-scale point counts
QualityVitest, GitHub Actions12 test files run in CI
Data sourceCost per 1kCan store and re-serve?Verdict
Overture Maps$0Yes (CDLA-Permissive 2.0)Chosen
OpenStreetMap$0Yes (ODbL, share-alike)Fallback
Google Places$32No, prohibited by its termsRejected
Yelp Fusion$229–643/moNo, 24-hour capRejected
D7 Lead FinderUnpublishedNo, and no coordinatesRejected

What I learned

What worked, what didn't, what's next.

What worked

  • Writing the architecture and data model before code: 63 commits in nine days, with no rewrites of the core schema.
  • Storing each business once and serving it to every customer. A second customer unlocking the same contact is served from cache, so the provider isn't paid twice.

What didn't

  • My first plan assumed Google Places data. Its terms prohibit storing names and addresses and using coordinates for point-in-polygon queries, which is exactly what a territory is. The licence decided the architecture.

Data limitations

  • Maps misses operators without a Google Business Profile, and includes fake ones. One fire protection "company" was a network of about 136 template sites.
  • A list graded 5,395 "confirmed" hood cleaners held about 2,229 real businesses once every site was re-read, and a hand check of 100 still found only half were real. I now hand-audit a random sample before trusting any machine grade.
  • Only about 7% of hood cleaning sites name an owner, so personalization comes from the company's own story instead.

What I'd change and build next

  • Territories now work for every vertical. Next is account data from a source licensed for re-serving, starting with OpenMart.
  • Add a site-check step to every record before it can be tiered, so bad data never reaches a rep's list.