Sylvino Prevot
Case Study // 003

BlipLeads

Next.js 16 (App Router + Turbopack)NextAuth v5 + Drizzle AdapterDrizzle ORM + Neon PostgresUpstash QStash (jobs assíncronos)

Every local service business has potential customers waiting on Google Maps. No one has the time to mine them manually one by one. BlipLeads handles this scan, prioritizes businesses that genuinely need help, and delivers them ready for the sales team to reach out.

In Production Multi-tenant SaaS

Product Gallery

The product's core promise: your next customer is already listed on Google, someone just needs to reach out.

Learn more about the technical architecture

Want to understand how this was built under the hood? Open the technical dive below.

expand_more

Technical Overview

Google Maps already reveals who needs help: no website, unresponsive WhatsApp, low ratings, unaddressed reviews. BlipLeads scans Maps by niche and region, prioritizes those with the highest closing probability, and delivers ready-to-contact leads with written WhatsApp pitches, organized in a Kanban board.

Core Technologies

Next.js 16 (App Router + Turbopack) rocket_launch
NextAuth v5 + Drizzle Adapter lock
Drizzle ORM + Neon Postgres database
Upstash QStash (jobs assíncronos) bolt
dnd-kit (Kanban) view_kanban
Tailwind CSS v4 format_paint
warning

The Challenge

Extracting data from Google is the easy part. The real challenge is turning it into a B2B sales pipeline that supports multiple clients concurrently without hitting API limits, mixing client accounts, or stalling on slow queries:

  • / Processing long-running searches (Google Places + website probing) without blocking HTTP requests, using asynchronous background jobs.
  • / Isolating data per tenant (campaigns, leads, metrics) and by user role (owner/admin/operator/viewer).
  • / Preventing duplicate leads per tenant even with multiple campaigns running for the same niche and region.
  • / Automatically detecting 'dead' websites listed on Google, turning a false positive into a sales opportunity.
check_circle

The Solution

The workflow is campaign → job → lead. Creating a campaign publishes a job to Upstash QStash, triggering the search asynchronously — without freezing the page. The routine normalizes phone numbers, verifies whether listed websites are actually online, calculates priority scores, and persists everything with account-level unique indices. Two clients running campaigns for the same niche will never duplicate leads.

src/lib/scoring.ts
export function calculatePriorityScore(input: ScoringInput): number {
  let score = 0;

  if (!input.websiteUri) score += 30;

  if (input.rating != null) {
    score += (input.rating / 5) * 20;
  }

  if (input.reviewCount != null && input.reviewCount > 0) {
    score += Math.min(25, Math.log10(input.reviewCount + 1) * 15);
  }

  if (input.businessStatus === 'OPERATIONAL') {
    score += 5;
  }

  return Math.round(score * 100) / 100;
}

Infrastructure

architecture

Deploy & Jobs Assíncronos

Next.js on Vercel, Postgres on Neon, job queue on Upstash QStash. Scraping runs outside the request cycle — never blocking active users.

Previous Project 3R Barber
Next Project Coming Soon
rocket_launch