Back to blog
Building an AI-Powered Lead Management System with Next.js
How I built a production-ready CRM that automatically qualifies leads using Google Gemini AI, automates follow-ups, and generates executive insights.
17 min read·Talha Bilal
Share:

Introduction
Most businesses struggle with the same problem: leads come in through forms, contact pages, and emails, but there's no systematic way to qualify them. Sales teams waste hours manually reviewing inquiries, trying to determine which leads deserve immediate attention and which can wait. High-value opportunities get lost in the noise, and follow-ups slip through the cracks.
I built the AI Lead Management System to solve this problem. It's a production-ready CRM platform that automatically analyzes every lead submission using Google Gemini AI, assigns intelligent priority scores, manages multi-channel follow-ups, and generates executive-level business intelligence — all in real-time.
This case study documents the architectural decisions, technical implementation, and engineering tradeoffs behind building a full-stack SaaS application that processes leads from capture to conversion.
The Problem
Traditional lead management involves several manual, time-consuming steps:
- Manual Lead Review — Someone has to read every form submission to determine its quality
- Subjective Scoring — Different team members assess leads differently, creating inconsistency
- Missed Follow-ups — Without automation, follow-up emails are forgotten or delayed
- No Business Intelligence — Analytics require manual data exports and spreadsheet analysis
- Disconnected Tools — CRM, email, analytics, and workflow automation exist in separate systems
For small businesses and agencies, this creates two major problems: high-value leads get buried in low-quality submissions, and follow-up sequences require manual tracking and execution.
The goal was to build a system that eliminates manual qualification, automates follow-up workflows, and provides real-time business intelligence — while maintaining production-grade reliability and security.
Solution Overview
The AI Lead Management System is a Next.js 16 application that combines several technologies into a cohesive platform:
- Public Lead Capture Form — Modern, responsive landing page for lead submission
- AI-Powered Lead Analysis — Google Gemini 2.5 Flash analyzes every lead in seconds
- Intelligent Scoring — Each lead receives a 0-100 score and HIGH/MEDIUM/LOW priority
- Automated Follow-ups — Multi-channel follow-up sequences with AI-generated content
- Admin Dashboard — Real-time analytics, lead management, and business intelligence
- Workflow Automation — Integration with n8n and Inngest for background processing
- Secure Authentication — Neon Auth for serverless user management
The system is built entirely with TypeScript, uses PostgreSQL for data persistence, and integrates with external services for email delivery and workflow orchestration.

Architecture
High-Level System Design
The application follows a layered architecture pattern that separates concerns and maintains clear boundaries between components:
Rendering diagram...
Key architectural decisions:
- Next.js App Router — Server components for performance, client components only where interactivity is needed
- Service Layer Pattern — Business logic isolated from API routes for testability and reuse
- Atomic Transactions — Lead + LeadAnalysis created together to prevent orphaned records
- Non-Blocking Webhooks — Workflow triggers don't block lead creation
- Database Retry Logic — Automatic retry mechanism handles transient connection issues
- Graceful Degradation — System functions even if AI or webhooks are unavailable
Data Flow
Rendering diagram...
The design ensures lead submission never fails due to webhook issues, AI analysis is required for data quality, database operations are atomic (no partial records), and the dashboard shows real-time data without polling.

Technology Stack
Core Framework
Next.js 16 (App Router) serves as the foundation: server components by default reduce client-side JS, built-in API routes remove the need for a separate backend, TypeScript gives full type safety from database to UI, and the framework ships automatic code splitting, image optimization, and edge deployment support out of the box.
Database Layer
PostgreSQL via Neon provides serverless database hosting with connection pooling, branching for preview deployments (planned), automatic point-in-time backups, and multi-region low-latency deployment.
Prisma ORM handles all database interactions:
typescript
1// Atomic transaction: Lead + Analysis created together2const lead = await prisma.lead.create({3 data: {4 fullName: parsed.data.fullName,5 email: parsed.data.email,6 // ... other fields7 analysis: {8 create: {9 score: analysis.score,10 priority: analysis.priority,11 summary: analysis.summary,12 recommendation: analysis.recommendation,13 },14 },15 },16});This pattern guarantees no orphaned analysis records, no leads without analysis, and a single database transaction for consistency.
AI Integration
Google Gemini 2.5 Flash powers lead analysis, follow-up content generation, and business intelligence reporting. The service layer abstracts the implementation:
typescript
1export async function analyzeLead(leadData: LeadAnalysisInput) {2 if (!genAI) return null; // Graceful degradation3 4 const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash" });5 const result = await model.generateContent(prompt);6 7 // Parse JSON from AI response8 const analysis = JSON.parse(jsonMatch[0]);9 10 // Validate and clamp scores11 analysis.score = Math.max(0, Math.min(100, analysis.score));12 13 return analysis;14}Why Gemini 2.5 Flash: cost-effective for production use, fast response times (under 2 seconds typical), structured JSON output support, and multimodal capabilities for future features.
Authentication
Neon Auth (v0.4.2-beta) provides serverless authentication:
typescript
1// API route protection2export async function requireApiAuth(): Promise<AuthUser> {3 const { data: session } = await auth.getSession();4 5 if (!session?.user) {6 throw new AuthError("Unauthorized", 401);7 }8 9 return {10 id: session.user.id,11 email: session.user.email,12 name: session.user.name,13 role: session.user.role,14 };15}Benefits: no auth database tables to manage, secure HTTP-only cookie sessions, built on Better Auth (proven library), and role-based access control ready out of the box.
Database Schema Design
The schema is optimized for CRM workflows and maintains referential integrity through cascade deletes.
Core Models
Lead — central entity containing contact information and CRM status:
typescript
1model Lead {2 id String @id @default(cuid())3 fullName String4 email String5 phone String?6 company String?7 budget String?8 projectType String?9 message String10 status LeadStatus @default(NEW)11 12 // Relations13 analysis LeadAnalysis?14 workflowLogs WorkflowLog[]15 followUps FollowUp[]16 followUpLogs FollowUpLog[]17 18 createdAt DateTime @default(now())19 updatedAt DateTime @updatedAt20}LeadAnalysis — one-to-one relationship storing AI insights:
typescript
1model LeadAnalysis {2 id String @id @default(cuid())3 leadId String @unique4 lead Lead @relation(fields: [leadId], references: [id], onDelete: Cascade)5 6 score Int @default(0)7 priority Priority @default(LOW)8 budgetCategory String?9 summary String @db.Text10 recommendation String @db.Text11 12 createdAt DateTime @default(now())13 updatedAt DateTime @updatedAt14}Design decisions: CUID primary keys (collision-resistant, URL-safe, sortable by creation time), cascade deletes (removing a lead removes all related records automatically),
@db.Text fields for unlimited-length AI content, nullable fields for lower-friction forms, and automatic timestamp tracking on every model.Follow-up System
FollowUp — scheduled follow-up tasks with delivery tracking:
typescript
1model FollowUp {2 id String @id @default(cuid())3 leadId String4 followUpNumber Int // 1st, 2nd, 3rd5 subject String6 content String? @db.Text7 status FollowUpStatus @default(PENDING)8 channel FollowUpChannel @default(EMAIL)9 scheduledFor DateTime10 sentAt DateTime?11 errorMessage String? @db.Text12 13 @@index([leadId, status])14 @@index([status, scheduledFor])15}Index strategy:
[leadId, status] powers fast queries for a lead's pending follow-ups, and [status, scheduledFor] makes the cron job's "what's due" query cheap.This schema supports multi-channel follow-ups (EMAIL, SMS, CALL, WHATSAPP), configurable intervals, duplicate prevention, and error logging for failed attempts.
Business Intelligence
AIInsight — stores AI-generated reports:
typescript
1model AIInsight {2 id String @id @default(cuid())3 summary String @db.Text4 recommendations String @db.Text5 keyFindings String @db.Text6 riskAlerts String? @db.Text7 opportunities String? @db.Text8 period InsightPeriod // WEEKLY or MONTHLY9 createdAt DateTime @default(now())10 11 @@index([period, createdAt])12}This enables historical trend analysis, comparative reporting (this week vs. last week), and executive summaries without re-processing raw data.
Key Features
1. AI Lead Analysis
Every lead submission triggers an AI analysis pipeline. The model evaluates budget indicators, project scope clarity, urgency signals, and company legitimacy (domain email, company name, professional tone).
typescript
1const prompt = `You are a lead qualification expert for a software development agency.2
3Lead Information:4- Name: ${leadData.fullName}5- Email: ${leadData.email}6- Company: ${leadData.company || "Not provided"}7- Budget: ${leadData.budget || "Not provided"}8- Message: ${leadData.message}9
10Provide analysis in JSON format:11{12 "score": <number from 0-100>,13 "priority": "LOW" | "MEDIUM" | "HIGH",14 "budgetCategory": <string>,15 "summary": <2-3 sentence summary>,16 "recommendation": <specific next action>17}18
19Scoring guidelines:20- Score 80-100: High budget, clear scope, urgent timeline21- Score 50-79: Moderate budget, reasonable scope22- Score 0-49: Low budget, vague requirements`;Response parsing is defensive — it extracts JSON from markdown code blocks or raw text, validates structure, and clamps scores to a valid range:
typescript
1const jsonMatch = text.match(/\{[\s\S]*\}/);2if (!jsonMatch) throw new Error("No JSON found");3
4const analysis = JSON.parse(jsonMatch[0]);5
6if (7 typeof analysis.score !== "number" ||8 !["LOW", "MEDIUM", "HIGH"].includes(analysis.priority)9) {10 throw new Error("Invalid response structure");11}12
13analysis.score = Math.max(0, Math.min(100, analysis.score));This ensures malformed AI responses don't crash the system, scores stay within bounds, and invalid priorities are caught early.

2. Follow-up Automation
The follow-up system runs on a daily cron schedule via n8n:
Rendering diagram...
Follow-up emails are personalized using lead context, generated with a structured prompt that asks for a subject line and body in JSON. Two more layers of robustness round this out:
typescript
1// Duplicate prevention2const existing = await prisma.followUp.findFirst({3 where: { leadId: lead.id, followUpNumber: followUpNumber },4});5
6if (existing) {7 console.log(`Follow-up ${followUpNumber} already exists for lead ${lead.id}`);8 continue;9}typescript
1// Error handling2try {3 await sendEmail({ to: lead.email, subject: followUp.subject, html: followUp.content });4 await prisma.followUp.update({5 where: { id: followUp.id },6 data: { status: "SENT", sentAt: new Date() },7 });8} catch (error) {9 await prisma.followUp.update({10 where: { id: followUp.id },11 data: { status: "FAILED", errorMessage: error.message },12 });13}Together these guarantee no silent failures, retry capability, and a full audit trail for debugging.


3. Business Intelligence
The AI Insights feature generates executive-level reports by aggregating KPIs, priority distribution, top opportunities, lead trends, workflow metrics, and status distribution in parallel, then feeding that aggregate into a structured prompt asking for an executive summary, key findings, recommendations, risk alerts, and growth opportunities.


Technical Deep Dives
Atomic Database Transactions
One critical requirement was preventing orphaned records — if AI analysis succeeds but the database write fails, you'd have analyzed a lead without storing the result. Prisma's nested create operations solve this by running in a single transaction, so both records are created or neither is, with no manual transaction handling needed.
Database Retry Logic
Serverless databases occasionally hit transient connection issues. Rather than failing immediately, operations retry with backoff:
typescript
1export async function retryOperation<T>(2 operation: () => Promise<T>,3 options: { maxRetries: number; delayMs: number; operationName: string }4): Promise<T> {5 let lastError: Error;6 7 for (let attempt = 1; attempt <= options.maxRetries; attempt++) {8 try {9 return await operation();10 } catch (error) {11 lastError = error;12 if (error.code === "P2002") throw error; // Don't retry validation errors13 if (attempt < options.maxRetries) {14 await sleep(options.delayMs);15 }16 }17 }18 19 throw lastError;20}This pattern handles connection pool exhaustion, temporary network issues, and database maintenance windows.
Non-Blocking Webhook Triggers
Lead creation must succeed even if external webhooks fail, so the n8n trigger fires asynchronously with a fire-and-forget pattern, a 10-second timeout, and outcome logging to the
WorkflowLog table — errors are caught and logged, never propagated back to the user.Authentication Strategy
Auth is enforced consistently via a shared helper, with a typed
AuthError and a centralized error handler so every API route returns consistent error responses:typescript
1export async function requireApiAuth(): Promise<AuthUser> {2 const { data: session } = await auth.getSession();3 if (!session?.user) throw new AuthError("Unauthorized", 401);4 return { id: session.user.id, email: session.user.email, name: session.user.name, role: session.user.role };5}Challenges and Tradeoffs
AI response reliability. Models occasionally return malformed JSON. The fix was defensive parsing with validation, fallbacks, and score clamping — more code complexity in exchange for production reliability.
Follow-up scheduling. Determining when to send follow-ups requires date calculations and status tracking. Solved with database-driven scheduling on indexed queries — at the cost of needing daily cron infrastructure (n8n or Inngest).
Webhook reliability. External webhooks can fail or time out. Solved with non-blocking triggers and comprehensive logging — but webhooks now have to be idempotent, since they may fire more than once.
Database connection pooling. Serverless functions can exhaust connections. Solved with Neon's pooled connection string plus retry logic, at the cost of managing two connection strings (
DATABASE_URL and DATABASE_URL_UNPOOLED).Performance Considerations
Typical production response times: lead submission 2-4 seconds (includes AI analysis), dashboard load 200-400ms (server-rendered), insights generation 5-8 seconds (AI-heavy), and follow-up processing 1-2 seconds per lead.
Optimization came from parallel data fetching with
Promise.all(), strategic database indexes, server components to cut client-side JS, and edge deployment for low-latency API routes. Switching dashboard stats from multiple sequential count queries to a single groupBy aggregation cut dashboard load time from 800ms to 200ms.AI cost management: Gemini 2.5 Flash pricing (as of June 2026) runs $0.075 per 1M input tokens and $0.30 per 1M output tokens. At roughly 1,000 tokens per lead analysis, 500 per follow-up, and 3,000 per insight report, the system costs about $0.60/month in AI spend at 1,000 leads/month.
Security Considerations
Input validation uses Zod schemas on every endpoint, which combined with Prisma's parameterized queries and React's default escaping covers SQL injection, XSS, buffer overflow, and type confusion.
Authentication security relies on HTTP-only cookies, secure flag in production, SameSite=Lax for CSRF protection, and automatic session expiry — password handling (bcrypt hashing, per-password salting, brute-force protection) is delegated entirely to Neon Auth.
API security for automation endpoints uses custom header authentication (a shared secret checked against
N8N_SECRET_KEY), with rate limiting currently handled by Vercel's built-in DDoS protection — per-IP rate limiting with Redis is a planned addition.Environment variables (API keys, auth secrets, webhook keys) are never committed to git, differ per environment, are rotated quarterly, and are scoped to minimum required permissions.

Developer Experience
TypeScript provides end-to-end type safety, from Prisma-generated database types through the service layer's input/output interfaces to Zod-validated API routes — the compiler catches contract mismatches before they ship.
Code is organized by responsibility:
services/ for pure business logic, lib/ for shared utilities, components/ for presentational UI, app/api/ for thin controller routes, and app/dashboard/ for server-rendered pages. This makes the codebase easy to test, maintain, and refactor.Prisma handles migrations with reviewed SQL, auto-generated rollback scripts, and regenerated types after every schema change. Local setup is a four-step process: clone and install, configure environment variables, push the schema and seed the database, then start the dev server — with Prisma Studio available as a visual database browser.
Lessons Learned
Start with type safety. Using TypeScript from day one prevented entire classes of bugs via the Prisma → Service → API → UI type chain — no runtime type errors, confident refactoring, self-documenting code.
The service layer is worth it. Business logic outside the API routes is easy to test, reusable across routes and server components, and easy to mock —
analyzeLead() alone is reused across the submission endpoint, a batch analysis script, and the test suite.AI requires defensive programming. Models are non-deterministic, so assumptions that hold 99% of the time will eventually break in production — always validate responses, clamp values, and log unexpected formats.
Database indexes matter. Strategic indexes on
FollowUp cut dashboard load time from 800ms to 200ms — a 10x improvement from two well-placed compound indexes.Non-blocking webhooks improve UX. Moving webhook triggers to the background dropped lead submission response time from 5-8 seconds to 2-4 seconds, at the cost of needing proper logging to catch silent webhook failures.
Future Improvements
Short-term (next 3 months): a customizable email template system (Handlebars-style variables instead of pure AI generation, for consistent brand voice and lower AI costs), bulk lead import from CSV/Excel with column mapping, and advanced time-series analytics using the already-installed Recharts library.
Medium-term (3-6 months): multi-user support with lead assignment, an activity feed, and role-based permissions; SMS and WhatsApp integration via Twilio and the WhatsApp Business API; and user-tunable lead scoring weights for budget, urgency, clarity, and company signals.
Long-term (6-12 months): a Chrome extension for one-click lead capture from LinkedIn and email, a React Native mobile app for on-the-go lead management with push notifications, and a Zapier integration to connect the system to thousands of other apps.
Conclusion
Building the AI Lead Management System required balancing speed against reliability, AI capability against cost, feature richness against simplicity, and flexibility against sensible defaults.
The result is a production-ready system demonstrating full-stack proficiency (Next.js, TypeScript, PostgreSQL, AI integration), solid system design (layered architecture, service patterns, atomic transactions), production thinking (error handling, retry logic, graceful degradation), and real business value for sales teams and agencies.
By the numbers: 890 source files, 8 database models with full referential integrity, 9 API endpoints with consistent authentication, 41 React components, and 100% TypeScript coverage.
This case study demonstrates the ability to architect, implement, and deploy a complete SaaS application that combines modern web technologies with AI capabilities to deliver measurable business value.
Have questions about this implementation or want to discuss a similar project? Get in touch.
