Manual lead tracking
Call track.lead() from any auth system — Clerk, Auth0, Supabase, custom sessions, or anything else.
When to use this
Use manual lead tracking when you are not using Better Auth, or when you need fine-grained control over when and how leads are reported. Common scenarios:
- Custom JWT / session-based auth
- Third-party auth providers: Clerk, Auth0, Supabase Auth, Firebase Auth
- Multi-step sign-up flows where lead attribution should fire at a specific step
- Server-side rendering frameworks where Better Auth is not in use
If you use Better Auth, the @refport/better-auth plugin handles lead tracking automatically — no manual call needed.
The pattern
Every lead tracking call follows the same three steps:
- Read the click ID from the incoming request (cookie header)
- Guard — if
null, the visitor did not arrive via a referral link; skip tracking - Call
track.lead()inside a try/catch so tracking never blocks sign-up
const clickId = /* read from request */;
if (!clickId) return;
try {
await refport.track.lead({
clickId,
eventName: 'Sign Up',
customerExternalId: user.id,
customerEmail: user.email,
customerName: user.name,
});
} catch {
/* never let tracking failure surface to the user */
}Next.js App Router
Inside a Server Action
'use server';
import { cookies } from 'next/headers';
import { Refport } from 'refport';
const refport = new Refport({ apiKey: process.env.REFPORT_API_KEY! });
export async function signUpAction(formData: FormData) {
const user = await yourAuthProvider.createUser({
email: formData.get('email') as string,
});
try {
const cookieStore = await cookies();
const clickId = cookieStore.get('refp_id')?.value;
if (clickId) {
await refport.track.lead({
clickId,
eventName: 'Sign Up',
customerExternalId: user.id,
customerEmail: user.email,
customerName: user.name,
});
}
} catch {}
return user;
}Inside a Route Handler
import { type NextRequest, NextResponse } from 'next/server';
import { getClickIdFromRequest } from 'refport';
import { refport } from '@/lib/refport';
export async function POST(request: NextRequest) {
const body = await request.json();
const user = await yourAuthProvider.createUser(body);
try {
const clickId = getClickIdFromRequest(request);
if (clickId) {
await refport.track.lead({
clickId,
eventName: 'Sign Up',
customerExternalId: user.id,
customerEmail: user.email,
customerName: user.name,
});
}
} catch {}
return NextResponse.json(user);
}Express.js
import { Router } from 'express';
import { getClickIdFromCookie } from 'refport';
import { refport } from '../lib/refport';
const router = Router();
router.post('/signup', async (req, res) => {
const user = await yourAuthProvider.createUser(req.body);
try {
const clickId = getClickIdFromCookie(req.headers.cookie);
if (clickId) {
await refport.track.lead({
clickId,
eventName: 'Sign Up',
customerExternalId: user.id,
customerEmail: user.email,
customerName: user.name,
});
}
} catch {}
res.json(user);
});
export default router;Generic pattern (any framework with Fetch API Request)
If your framework gives you a standard Request object (Hono, Bun, Deno, Fastify with fetch compat, etc.), use getClickIdFromRequest():
import { getClickIdFromRequest } from 'refport';
async function handleSignUp(request: Request) {
const user = await yourAuthProvider.createUser(await request.json());
try {
const clickId = getClickIdFromRequest(request);
if (clickId) {
await refport.track.lead({
clickId,
eventName: 'Sign Up',
customerExternalId: user.id,
customerEmail: user.email,
customerName: user.name,
});
}
} catch {}
return Response.json(user);
}If you only have a raw cookie string (e.g. from a non-standard framework), use getClickIdFromCookie() directly:
import { getClickIdFromCookie } from 'refport';
const clickId = getClickIdFromCookie(rawCookieHeader);Deduplication
Refport deduplicates lead events on customerExternalId + eventName within a 7-day window. It is safe to call track.lead() on every sign-up without worrying about double-counting — if the same user signs up again within 7 days with the same event name, only the first event is recorded.
Error handling
Always wrap tracking calls in try/catch. A network error, an invalid API key, or a Refport service interruption must never cause your sign-up or purchase flow to fail.
try {
await refport.track.lead({ /* ... */ });
} catch (err) {
/* log the error if you want visibility, but do not re-throw */
logger.warn('Refport lead tracking failed', { err });
}