> ## Documentation Index
> Fetch the complete documentation index at: https://speakeasy-20cf8bdf.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Supabase

> Learn how to track lead conversion events with Supabase and Dub

When it comes to conversion tracking, a `lead` event happens when a user performs an action that indicates interest in your product or service. This could be anything from:

* Signing up for an account
* Adding a product to cart
* Joining a mailing list

<Frame>
  <img className="rounded-lg border border-gray-100" src="https://assets.dub.co/help/conversion-lead-event.png" alt="A diagram showing how lead events are tracked in the conversion funnel" />
</Frame>

In this guide, we will be focusing on tracking new user sign-ups for a SaaS application that uses Supabase for user authentication.

## Prerequisites

Before you get started, make sure you follow the [Dub Conversions quickstart guide](/conversions/quickstart) to get Dub Conversions set up for your links:

1. [Enable conversion tracking for your links](/conversions/quickstart#step-1-enable-conversion-tracking-for-your-links)
2. [Install the @dub/analytics client-side SDK](/sdks/client-side/introduction)
3. [Install the Dub server-side SDK](/conversions/quickstart#step-3-install-the-dub-server-side-sdk)

## Configure Supabase

Next, configure Supabase to track lead conversion events in the auth callback function.

Here's how it works in a nutshell:

1. In the `/api/auth/callback` route, check if:
   * the `dub_id` cookie is present.
   * the user is a new sign up (created in the last 10 minutes).
2. If the `dub_id` cookie is present and the user is a new sign up, send a lead event to Dub using `dub.track.lead`
3. Delete the `dub_id` cookie.

<CodeGroup>
  ```typescript Next.js App Router theme={null}
  // app/api/auth/callback/route.ts
  import { cookies } from "next/headers";
  import { NextResponse } from "next/server";
  import { createClient } from "@/lib/supabase/server";
  import { waitUntil } from "@vercel/functions";
  import { dub } from "@/lib/dub";

  export async function GET(request: Request) {
    const { searchParams, origin } = new URL(request.url);
    const code = searchParams.get("code");
    // if "next" is in param, use it as the redirect URL
    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;
        // if the user is created in the last 10 minutes, consider them new
        const isNewUser =
          new Date(user.created_at) > new Date(Date.now() - 10 * 60 * 1000);
        // if the user is new and has a dub_id cookie, track the lead
        if (dub_id && isNewUser) {
          waitUntil(
            dub.track.lead({
              clickId: dub_id,
              eventName: "Sign Up",
              externalId: user.id,
              customerName: user.user_metadata.name,
              customerEmail: user.email,
              customerAvatar: user.user_metadata.avatar_url,
            })
          );
          // delete the clickId cookie
          cookies().delete("dub_id");
        }
        return NextResponse.redirect(`${origin}${next}`);
      }
    }

    // return the user to an error page with instructions
    return NextResponse.redirect(`${origin}/auth/auth-code-error`);
  }
  ```

  ```typescript Next.js Pages Router theme={null}
  // pages/api/auth/callback.ts
  import { NextApiRequest, NextApiResponse } from "next";
  import { createClient } from "@supabase/supabase-js";
  import { dub } from "@/lib/dub";

  export default async function handler(
    req: NextApiRequest,
    res: NextApiResponse
  ) {
    const { code, next = "/" } = req.query;
    const origin = `${req.headers["x-forwarded-proto"]}://${req.headers.host}`;

    if (typeof code === "string") {
      const supabase = createClient(
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        process.env.SUPABASE_SERVICE_ROLE_KEY!
      );

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

      if (!error) {
        const { user } = data;
        const { dub_id } = req.cookies;

        // if the user is created in the last 10 minutes, consider them new
        const isNewUser =
          new Date(user.created_at) > new Date(Date.now() - 10 * 60 * 1000);

        // if the user is new and has a dub_id cookie, track the lead
        if (dub_id && isNewUser) {
          dub.track
            .lead({
              clickId: dub_id,
              eventName: "Sign Up",
              externalId: user.id,
              customerName: user.user_metadata.name,
              customerEmail: user.email,
              customerAvatar: user.user_metadata.avatar_url,
            })
            .catch(console.error); // Handle any errors in tracking

          // delete the clickId cookie
          res.setHeader(
            "Set-Cookie",
            `dub_id=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT`
          );
        }

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

    // return the user to an error page with instructions
    return res.redirect(`${origin}/auth/auth-code-error`);
  }
  ```
</CodeGroup>

Here's the full list of attributes you can pass when sending a lead event:

| Property         | Required | Description                                                                                                              |
| :--------------- | :------- | :----------------------------------------------------------------------------------------------------------------------- |
| `clickId`        | **Yes**  | The unique `dub_id` parameter that the lead conversion event is attributed to.                                           |
| `eventName`      | **Yes**  | The name of the event. Example: "Sign up".                                                                               |
| `externalId`     | **Yes**  | The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer. |
| `customerEmail`  | No       | The email address of the customer. If not passed, a random email address will be generated.                              |
| `customerName`   | No       | The name of the customer. If not passed, a random name will be generated (e.g. "Big Red Caribou").                       |
| `customerAvatar` | No       | The avatar URL of the customer. If not passed, a random avatar URL will be generated.                                    |

## Example App

To learn more about how to track leads with Supabase, check out the following example app:

<Card title="Supabase + Next.js App Router Example" icon="github" href="https://github.com/steven-tey/extrapolate/blob/main/app/api/auth/callback/route.ts">
  Check out a real-world example of this in action – Extrapolate uses Supabase
  Auth and Next.js App Router to track new user sign-ups.
</Card>

## View your conversions

Once you've enabled conversion tracking for your links, all your tracked conversions will show up on your [Analytics dashboard](https://app.dub.co/analytics). We provide 3 different views to help you understand your conversions:

* **Time-series**: A [time-series view](https://dub.co/help/article/dub-analytics#1-time-series-analytics-chart) of the number clicks, leads and sales.

<Frame>
  <img src="https://mintcdn.com/speakeasy-20cf8bdf/-Y0j4C06SQOILvRL/images/conversions/timeseries-chart.png?fit=max&auto=format&n=-Y0j4C06SQOILvRL&q=85&s=7d752129ab0f59fb7ac44b5c83d608c9" alt="Time-series line chart" width="1600" height="900" data-path="images/conversions/timeseries-chart.png" />
</Frame>

* **Funnel chart**: A funnel chart view visualizing the conversion & dropoff rates across the different steps in the conversion funnel (clicks → leads → sales).

<Frame>
  <img src="https://mintcdn.com/speakeasy-20cf8bdf/-Y0j4C06SQOILvRL/images/conversions/funnel-chart.png?fit=max&auto=format&n=-Y0j4C06SQOILvRL&q=85&s=bd252526bae397088168478a8097c10d" alt="Funnel chart view showing the conversion & dropoff rates from clicks → leads → sales" width="1600" height="900" data-path="images/conversions/funnel-chart.png" />
</Frame>

* **Real-time events stream**: A [real-time events stream](https://dub.co/help/article/real-time-events-stream) of every single conversion event that occurs across all your links in your workspace.

<Frame>
  <img src="https://mintcdn.com/speakeasy-20cf8bdf/-Y0j4C06SQOILvRL/images/conversions/events-table.png?fit=max&auto=format&n=-Y0j4C06SQOILvRL&q=85&s=256bd06254e543c40f1ae268424e17e1" alt="The Events Stream dashboard on Dub" width="1600" height="900" data-path="images/conversions/events-table.png" />
</Frame>
