Skip to main content
Dub’s conversion tracking allows you to attribute leads and sales to your links, providing complete visibility into your link attribution funnel and ROI.

How Conversion Tracking Works

Dub uses a cookie-based attribution system to track the complete user journey:
1

User Clicks Link

When someone clicks your Dub link, Dub sets a dub_id cookie containing a unique click ID.
2

User Converts to Lead

When the user signs up or completes a lead action, you send a lead event to Dub with the dub_id from the cookie.
3

User Makes Purchase

When the user completes a purchase, you send a sale event to Dub, which associates it with the original click.
4

View Attribution

Dub attributes the lead and sale to the original link, showing complete conversion funnel metrics.
The dub_id cookie persists for 30 days by default, allowing attribution of conversions that occur within that window.

Setting Up Conversion Tracking

Step 1: Install the Analytics Script

First, install the Dub analytics script to track clicks and set the dub_id cookie.
Install the @dub/analytics package:
npm install @dub/analytics
Add the Analytics component to your root layout:
import { Analytics } from '@dub/analytics/react';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
      <Analytics />
    </html>
  );
}

Step 2: Track Lead Conversions

When a user signs up or completes a lead action, send the lead event to Dub.

Step 3: Track Sale Conversions

When a customer makes a purchase, track the sale event.
import { Dub } from 'dub';

const dub = new Dub({
  token: process.env.DUB_API_KEY,
});

// After successful payment
await dub.track.sale({
  customerExternalId: 'user_123',
  amount: 5000, // Amount in cents ($50.00)
  currency: 'usd',
  paymentProcessor: 'stripe',
  eventName: 'Purchase',
  invoiceId: 'inv_123',
});

Integration Examples

Clerk Authentication

"use client";

import { trackLead } from "@/actions/track-lead";
import { useUser } from "@clerk/nextjs";
import { Analytics } from "@dub/analytics/react";
import { useEffect } from "react";

export function DubAnalytics() {
  const { user } = useUser();

  useEffect(() => {
    if (!user || user.publicMetadata.dubClickId) return;

    // Track the lead event if user hasn't been tracked yet
    trackLead({
      id: user.id,
      name: user.fullName!,
      email: user.primaryEmailAddress?.emailAddress,
      avatar: user.imageUrl,
    }).then(async (res) => {
      if (res.ok) await user.reload();
      else console.error(res.error);
    });
  }, [user]);

  return <Analytics />;
}
Server action:
"use server";

import { dub } from "@/lib/dub";
import { clerkClient } from "@clerk/nextjs/server";
import { cookies } from "next/headers";

export async function trackLead({ id, name, email, avatar }) {
  try {
    const cookieStore = await cookies();
    const dubId = cookieStore.get("dub_id")?.value;

    if (dubId) {
      await dub.track.lead({
        clickId: dubId,
        eventName: "Sign Up",
        customerExternalId: id,
        customerName: name,
        customerEmail: email,
        customerAvatar: avatar,
      });

      cookieStore.set("dub_id", "", {
        expires: new Date(0),
      });
    }

    const clerk = await clerkClient();
    await clerk.users.updateUser(id, {
      publicMetadata: {
        dubClickId: dubId || "n/a",
      },
    });

    return { ok: true };
  } catch (error) {
    return { ok: false, error: error.message };
  }
}

Supabase Authentication

// app/api/auth/callback/route.ts
import { dub } from "@/lib/dub";
import { createClient } from "@/lib/supabase/server";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";

export async function GET(request: Request) {
  const { searchParams, origin } = new URL(request.url);
  const code = searchParams.get("code");
  const next = searchParams.get("next") ?? "/";

  if (code) {
    const supabase = createClient(cookies());
    const { data, error } = await supabase.auth.exchangeCodeForSession(code);

    if (!error) {
      const { user } = data;
      const dub_id = cookies().get("dub_id")?.value;
      
      // Check if user is new (created in last 10 minutes)
      const isNewUser =
        new Date(user.created_at) > new Date(Date.now() - 10 * 60 * 1000);

      if (dub_id && isNewUser) {
        await dub.track.lead({
          clickId: dub_id,
          eventName: "Sign Up",
          customerExternalId: user.id,
          customerName: user.user_metadata.name,
          customerEmail: user.email,
          customerAvatar: user.user_metadata.avatar_url,
        });

        cookies().delete("dub_id");
      }

      return NextResponse.redirect(`${origin}${next}`);
    }
  }

  return NextResponse.redirect(`${origin}/auth/auth-code-error`);
}

Shopify Store

  1. Install the Dub Shopify App
  2. Navigate to Online Store > Themes > Customize
  3. Go to App embeds tab
  4. Enable the Dub Analytics Script
Sales are automatically tracked when customers complete checkout through Shopify.

Conversion Metrics

Once tracking is set up, view conversion metrics in your Dub dashboard:
{
  clicks: number,        // Total clicks on the link
  leads: number,         // Users who converted to leads
  conversions: number,   // Leads who made purchases
  sales: number,         // Total number of sales
  saleAmount: number,    // Total revenue in cents
}

Conversion Funnel

1

Clicks

Total users who clicked your link
2

Leads

Users who signed up or completed a lead actionConversion Rate: Leads / Clicks
3

Sales

Leads who made a purchaseLead-to-Sale Rate: Sales / Leads
4

Revenue

Total revenue from all salesAverage Order Value: Revenue / Sales

Analytics Dashboard

View detailed conversion analytics:
  • Conversion timeline: Leads and sales over time
  • Top converting links: Best performing links by conversion rate
  • Customer details: Name, email, avatar for each lead
  • Revenue tracking: Total revenue and AOV
  • Attribution window: When conversions occurred relative to click

Advanced Configuration

Custom Event Names

// Different lead events
await dub.track.lead({
  clickId: dubId,
  eventName: 'Trial Started',
  customerExternalId: user.id,
});

await dub.track.lead({
  clickId: dubId,
  eventName: 'Waitlist Joined',
  customerExternalId: user.id,
});

await dub.track.lead({
  clickId: dubId,
  eventName: 'Newsletter Subscribed',
  customerExternalId: user.id,
});
Filter analytics by event name to see performance by lead type.

Allowed Hostnames

For security, restrict which domains can track conversions:
// In workspace settings
{
  allowedHostnames: ['yourdomain.com', 'app.yourdomain.com']
}
Only pages on these domains can use the client-side analytics script.

Publishable Key

import { Analytics } from '@dub/analytics/react';

<Analytics 
  publishableKey="pk_live_xxxxx"
  allowedHostnames={['yourdomain.com']}
/>
Get your publishable key from workspace settings > Conversion Tracking.

Best Practices

Server-side lead and sale tracking is more reliable than client-side because it:
  • Cannot be blocked by ad blockers
  • Executes in a controlled environment
  • Provides accurate attribution
Use your internal user IDs as customerExternalId to:
  • Prevent duplicate leads
  • Associate multiple events with the same customer
  • Link sales back to the original lead
Before deploying to production:
  1. Click your test link
  2. Verify dub_id cookie is set
  3. Trigger a test signup
  4. Check that lead appears in Dub dashboard
  5. Test a purchase to verify sale tracking

Troubleshooting

  • Verify analytics script is loaded on your site
  • Check that dub_id cookie is set after clicking link
  • Ensure API key has correct permissions
  • Verify customerExternalId is unique per user
  • Check that link has trackConversion: true
  • Confirm customer was tracked as a lead first
  • Verify customerExternalId matches between lead and sale
  • Check that sale occurred within attribution window (30 days)
  • Ensure payment processor integration is configured
  • Delete dub_id cookie after tracking lead
  • Use unique customerExternalId to prevent duplicates
  • Implement idempotency in your tracking code

Build docs developers (and LLMs) love