Slugy

How to track lead conversions with Slugy

Pro lead tracking: enable it on the link, capture slugy_id on your site, and attribute signups with the leads_track API.

leadsanalyticsguides

Clicks tell you who visited. Leads tell you who converted — signed up, clicked Buy Now, or joined a waitlist. Slugy attributes those actions back to the short link that brought them in.

How lead conversion works

The product flow is:

  1. Enable tracking — turn on Lead tracking when you create or edit the short link (Pro only). Without this, Slugy does not attach a click id.
  2. Click — someone opens that short link. Slugy redirects to your destination with ?slugy_id=… in the URL.
  3. Persist — your site stores that id in a first-party cookie so later pages still know the click.
  4. Lead — when they convert, your backend sends the id plus customer details to Slugy with a workspace API key.

Step 1: Use a Pro workspace

API keys, the lead-tracking toggle, and the Leads metric in Analytics all require Pro. On Free, those surfaces stay locked until you upgrade in Settings → Billing.

Step 2: Turn on Lead tracking for the link

  1. Create a new link, or open an existing one to edit.
  2. Toggle Lead tracking on.
  3. Save the link.

Only then does a click append slugy_id to the destination. Links without the toggle still collect click analytics — they just cannot be attributed as leads.

Step 3: Create an API key

  1. Open your workspace → Settings → API Keys.
  2. Create a key, name it, and copy it once (it won't be shown again).
  3. Store it as SLUGY_API_KEY in your server environment — never in browser JavaScript.

Keys belong to one workspace. A clickId from another workspace will be rejected.

Step 4: Share the short link

Point the tracking-enabled Slugy link at your landing page. Visitors must arrive through that short link so Slugy can attach slugy_id.

Example destination after a click:

https://yoursite.com/pricing?slugy_id=K7mP2nQx9vR4tLw8cB3hY1aD

Step 5: Capture slugy_id on your site

The query param lives on your domain after redirect. Slugy cannot set a first-party cookie there, so persist the id yourself on first landing — otherwise a later /checkout page will lose attribution.

lib/slugy.ts

const COOKIE = "slugy_id";

/** Read attribution id from query (first hit) or first-party cookie. */
export function getSlugyId(): string | null {
  if (typeof window === "undefined") return null;

  const fromQuery = new URLSearchParams(window.location.search).get("slugy_id");
  if (fromQuery) return fromQuery;

  const match = document.cookie.match(
    new RegExp(`(?:^|;\\s*)${COOKIE}=([^;]*)`),
  );
  return match?.[1] ? decodeURIComponent(match[1]) : null;
}

export function setSlugyIdCookie(id: string, maxAge = 60 * 60 * 24 * 90) {
  document.cookie = `${COOKIE}=${encodeURIComponent(id)}; path=/; max-age=${maxAge}; SameSite=Lax`;
}

export { COOKIE as SLUGY_ID_COOKIE };

components/capture-slugy-id.tsx

"use client";

import { useEffect } from "react";
import { getSlugyId, setSlugyIdCookie } from "@/lib/slugy";

/** Persist slugy_id from the redirect URL as a first-party cookie. */
export function CaptureSlugyId() {
  useEffect(() => {
    const id = new URLSearchParams(window.location.search).get("slugy_id");
    if (!id) return;

    setSlugyIdCookie(id);

    const url = new URL(window.location.href);
    url.searchParams.delete("slugy_id");
    window.history.replaceState({}, "", url.toString());
  }, []);

  return null;
}

export { getSlugyId };

Mount <CaptureSlugyId /> in your root layout so every landing page captures attribution. Use getSlugyId() when the conversion happens.

Step 6: Track the lead from your server

When the conversion succeeds (paid, signed up, form accepted), call Slugy from a server route — not from the client with a secret key.

API

POST https://api.slugy.co/leads_track
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
PropertyDescriptionRequired
clickIdThe slugy_id from the URL or cookie. You can also pass it as a query param on the request.Yes
eventNamee.g. buy_now, sign_upYes
customerExternalIdStable ID in your system (user id / email)Yes
customerEmailCustomer emailNo
customerNameCustomer nameNo
metadataExtra JSON (plan, source, etc.)No

A new lead returns 201 with leadEventId. Repeating the same customerExternalId + eventName in that workspace is idempotent and returns 200. Unknown or cross-workspace clickId values return 404.

Next.js example

// app/api/track-lead/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
  const apiKey = process.env.SLUGY_API_KEY;
  if (!apiKey) {
    return NextResponse.json({ error: "Missing SLUGY_API_KEY" }, { status: 500 });
  }

  const { clickId, customerExternalId, customerEmail, customerName } =
    await req.json();

  if (!clickId || !customerExternalId) {
    return NextResponse.json(
      { error: "clickId and customerExternalId required" },
      { status: 400 },
    );
  }

  const res = await fetch("https://api.slugy.co/leads_track", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      clickId,
      eventName: "buy_now",
      customerExternalId,
      customerEmail,
      customerName,
      metadata: { source: "portfolio" },
    }),
  });

  const data = await res.json().catch(() => ({}));
  return NextResponse.json(data, { status: res.status });
}

From your Buy Now / signup handler on the client:

import { getSlugyId } from "@/lib/slugy";

async function onBuyNow() {
  const clickId = getSlugyId();
  if (!clickId) return; // organic visit — no Slugy attribution

  await fetch("/api/track-lead", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      clickId,
      customerExternalId: email,
      customerEmail: email,
      customerName: name,
    }),
  });
}

Where to see leads

  • Open Analytics in your workspace.
  • Click the Leads metric next to Clicks. Lead analytics load when you select that metric.
  • Filter by link, country, device, and time range the same way as clicks.

Tips

  • Same customerExternalId + eventName in a workspace is idempotent — it won't double-count.
  • Click attribution lives 90 days in Redis (and as long as you keep the first-party cookie). After that, Slugy still tries to resolve the click from stored analytics.
  • Always send the lead after the action succeeds on your backend.
  • Skip the client call when getSlugyId() is empty — that visitor did not come through a tracking-enabled short link.

Get started

Upgrade to Pro, enable Lead tracking on a link, add the cookie snippet, and wire leads_track on your next conversion event.