Tracking
Track signups, conversions and custom events
Signups aren't detected automatically — here's the one line that records them.
Pageviews are automatic. Anything meaning-specific to your product — a signup, a plan upgrade, a demo booked — is not: VisitTrack can't know which button or which redirect means 'this person signed up'. You tell it, with one call.
From your own code (recommended)
Call this exactly where your code already knows the account was created — after the API responds, not when the button is clicked. That way a failed signup never counts as one.
// After your signup succeeds:
window.visitrack("signup");
// With extra detail, if useful:
window.visitrack("signup", { plan: "pro", provider: "google" });Without touching your logic
For a one-click flow — an OAuth button that redirects away, say — put the attribute straight on the element. It fires on click and on keyboard activation, with no JavaScript from you.
<button data-vt-goal="signup">Sign up with Google</button>This counts the click, not a confirmed account — someone who abandons the Google consent screen still counts. If that matters, report the signup from your server instead (below).
React, Next.js and TypeScript
Use optional chaining so nothing throws when an ad blocker stops the script, and declare the global once for TypeScript:
// global.d.ts
declare global {
interface Window {
visitrack?: ((name: string, props?: Record<string, unknown>) => void) & {
identify?: (userId: string, traits?: Record<string, unknown>) => void;
visitorId?: () => string;
};
}
}
export {};
// in your signup form
async function onSubmit(values: FormValues) {
const res = await fetch("/api/signup", { method: "POST", body: JSON.stringify(values) });
if (!res.ok) return;
const user = await res.json();
window.visitrack?.("signup", { method: "email" });
window.visitrack?.identify?.(user.id, { plan: user.plan });
}OAuth and accounts created on the server
When the account is created in a server callback (Google/GitHub OAuth, a magic link, an invite), send the event from the server with a secret API key from Settings → API / MCP. To attribute it to the visitor's earlier visits, carry their visitor id — window.visitrack.visitorId() in the browser — through the flow, for example in a cookie set right before the redirect. Report each signup from one place only, so nothing is counted twice.
// before redirecting to the OAuth provider (browser)
document.cookie = "vt_vid=" + window.visitrack?.visitorId?.() + "; path=/; max-age=600; samesite=lax";
// in your OAuth callback, only when a NEW user was created (server)
await fetch("https://visitrack.app/api/track", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VISITRACK_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ type: "event", visitorId: cookies.vt_vid, name: "signup", props: { method: "google" } }),
});Let your AI assistant do it
The Signups tab has a “Copy setup instructions for AI” button: a prompt with all of the above that you paste into Cursor, Claude Code or Copilot with your app open. It finds your signup handlers and adds the event for you.
Checking it works
The script ignores localhost on purpose, so test on your deployed site: sign up once with a fresh account and open the Signups tab — it appears within a few seconds.
Scroll-depth events
Fires once when the element is at least half visible — useful for 'did anyone actually reach the pricing table'.
<section data-vt-scroll="pricing_viewed"> … </section>Where these show up
- An event named 'signup' populates the Signups tab, including source and time-to-conversion.
- Any custom event can be turned into a goal in Settings, which then appears on the Overview and Goals tabs.
- Revenue is separate — it comes from your payment provider's webhook (Stripe, Polar, Lemon Squeezy, Paddle or Razorpay — see Revenue attribution), not from a browser event, so it can't be faked by a page visitor.
Name events once and keep the name
The event name is the identifier. Renaming 'signup' to 'sign_up' later starts a new, empty series rather than renaming the old one.
Something missing? Tell us.
AI agent or LLM? Read this page as markdown.