Skip to main content
📞 1-888-784-3881🚀 Start Project
📝Guides

Building a Lead Capture API

Complete guide to building a production-ready lead capture system.

⏱️ 20 min read

Overview

Build a complete lead capture system with form handling, validation, notifications, and CRM integration.

Architecture

[Landing Page] → [API Route] → [Validation] → [PropTechUSA] → [Notifications]

[Rate Limit]

Step 1: Create the API Route

// app/api/leads/route.ts

import { NextRequest, NextResponse } from 'next/server';

import { proptech } from '@/lib/proptech';

import { z } from 'zod';

const leadSchema = z.object({

name: z.string().min(2),

email: z.string().email(),

phone: z.string().min(10),

address: z.string().optional(),

message: z.string().optional(),

});

export async function POST(request: NextRequest) {

try {

const body = await request.json();

// Validate input

const data = leadSchema.parse(body);

// Create lead in PropTechUSA

const lead = await proptech.leads.create({

...data,

source: 'website',

metadata: {

ip: request.ip,

userAgent: request.headers.get('user-agent'),

referrer: request.headers.get('referer'),

},

});

return NextResponse.json({ success: true, id: lead.id });

} catch (error) {

if (error instanceof z.ZodError) {

return NextResponse.json(

{ error: 'Validation failed', details: error.errors },

{ status: 400 }

);

}

return NextResponse.json(

{ error: 'Internal server error' },

{ status: 500 }

);

}

}

Step 2: Add Rate Limiting

import { Ratelimit } from '@upstash/ratelimit';

import { Redis } from '@upstash/redis';

const ratelimit = new Ratelimit({

redis: Redis.fromEnv(),

limiter: Ratelimit.slidingWindow(5, '1 m'), // 5 requests per minute

});

// In your API route:

const ip = request.ip ?? '127.0.0.1';

const { success } = await ratelimit.limit(ip);

if (!success) {

return NextResponse.json(

{ error: 'Too many requests' },

{ status: 429 }

);

}

Step 3: Send Notifications

// After creating lead

await Promise.all([

// Slack notification

proptech.notify.slack({

channel: '#leads',

message: New lead: ${lead.name} (${lead.email}),

}),

// Email to team

proptech.notify.email({

to: 'team@yourdomain.com',

subject: 'New Lead Received',

leadId: lead.id,

}),

]);

Step 4: Frontend Form

'use client';

import { useState } from 'react';

export function LeadForm() {

const [loading, setLoading] = useState(false);

const [success, setSuccess] = useState(false);

async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {

e.preventDefault();

setLoading(true);

const formData = new FormData(e.currentTarget);

const response = await fetch('/api/leads', {

method: 'POST',

headers: { 'Content-Type': 'application/json' },

body: JSON.stringify(Object.fromEntries(formData)),

});

if (response.ok) {

setSuccess(true);

}

setLoading(false);

}

if (success) {

return <div>Thanks! We'll be in touch soon.</div>;

}

return (

<form onSubmit={handleSubmit}>

<input name="name" placeholder="Name" required />

<input name="email" type="email" placeholder="Email" required />

<input name="phone" type="tel" placeholder="Phone" required />

<textarea name="message" placeholder="Message" />

<button type="submit" disabled={loading}>

{loading ? 'Submitting...' : 'Get My Cash Offer'}

</button>

</form>

);

}