Refport
ConversionsLeads

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:

  1. Read the click ID from the incoming request (cookie header)
  2. Guard — if null, the visitor did not arrive via a referral link; skip tracking
  3. 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

app/actions/auth.ts
'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

app/api/auth/register/route.ts
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

routes/auth.ts
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 });
}

On this page