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:
- 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.
- Click — someone opens that short link. Slugy redirects to your destination with
?slugy_id=…in the URL. - Persist — your site stores that id in a first-party cookie so later pages still know the click.
- 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
- Create a new link, or open an existing one to edit.
- Toggle Lead tracking on.
- 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
- Open your workspace → Settings → API Keys.
- Create a key, name it, and copy it once (it won't be shown again).
- Store it as
SLUGY_API_KEYin 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=K7mP2nQx9vR4tLw8cB3hY1aDStep 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| Property | Description | Required |
|---|---|---|
clickId | The slugy_id from the URL or cookie. You can also pass it as a query param on the request. | Yes |
eventName | e.g. buy_now, sign_up | Yes |
customerExternalId | Stable ID in your system (user id / email) | Yes |
customerEmail | Customer email | No |
customerName | Customer name | No |
metadata | Extra 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+eventNamein 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.