Track sign-ups in your app (incl. Google & Microsoft SSO)
Fire a Lead event when someone creates an account in your SaaS – for the email/password form and for Google and Microsoft (SSO) sign-up. The form listener can't see SSO sign-ups, so you send the event yourself with a small server route and carry the click ID through the OAuth state parameter.
When the action you care about happens inside your own software – someone creating an
account – you usually want one Lead event sent to LeadJourney, with the new user's name
and email, attributed to the ad click that brought them. This guide shows exactly how, including
Google and Microsoft sign-up, which the automatic form listener can't catch on its own.
Tracking has to be live first
Sign-ups are matched back to the visitor that clicked your ad, so the tracking script has to be running on your site first. If it isn't, start with Install the tracking script (and the developer notes in Install on a custom-coded website).
Why the automatic form listener isn't enough
LeadJourney's script fires a Lead automatically when it sees a standard <form> submit. That
covers your plain email/password sign-up form. It doesn't cover Google or
Microsoft sign-up, because that flow never submits a form on your page:
- The visitor leaves your site to
accounts.google.com/login.microsoftonline.com, approves, and is redirected back to your callback. There's no<form>submit for the script to catch. - The account is often created server-side on that callback, where there's no browser to read from at all.
- An SSO button click is ambiguous – it could be a login, not a new sign-up. You only want the event on a new account.
So for sign-ups you send the Lead event yourself, exactly once, the moment a new account is
created. It's three small pieces: a server route that holds your secret, one call after sign-up
succeeds, and – for SSO – carrying the click ID through the OAuth state parameter.
The click ID is the key
When a visitor lands from a tracked link, the script saves their click ID in the browser at
localStorage.getItem('clickId'). That ID is what ties the new account back to the exact
campaign, ad set and ad. See How attribution works.
Before you start
Grab your Conversion API credentials from Settings → Events → Conversion API:
- Your Postback URL – looks like
https://api.leadjourney.io/api/v1/postback/<your-id>. - Your Postback Secret – starts with
pb_…. Treat it like a password; it stays on your server and is never exposed to the browser.
Make sure a Lead event exists in the Events Manager –
every workspace has one by default.
Step by step
Add a server route that does the sending
This one route forwards the sign-up to LeadJourney and keeps your Postback Secret safe on the server – it's never visible to visitors. The example is a Next.js App Router route, but any server endpoint (Express, a serverless function, your backend) works the same way.
// app/api/lead/route.js
export async function POST(req) {
const d = await req.json();
await fetch(
"https://api.leadjourney.io/api/v1/postback/YOUR-POSTBACK-ID",
{
method: "POST",
headers: {
"Authorization": "Bearer YOUR_POSTBACK_SECRET",
"Content-Type": "application/json",
},
body: JSON.stringify({
type: "Lead",
click_id: d.click_id,
tag: d.tag,
firstname: d.firstname,
lastname: d.lastname,
email: d.email,
}),
}
);
return Response.json({ ok: true });
}Use your own URL and secret
Replace YOUR-POSTBACK-ID and YOUR_POSTBACK_SECRET with the Postback URL and Postback
Secret from your own Settings → Events → Conversion API. Never put the secret in
client-side code or commit it to a public repo.
Call it when a new account is created
Wherever the sign-up finishes successfully – the form, the Google button and the Microsoft button – add this one call. Read the click ID from the browser:
fetch("/api/lead", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
click_id: localStorage.getItem("clickId"),
tag: "app_signup", // your campaign / source tag – a fixed word is fine
firstname: /* see table below */,
lastname: /* see table below */,
email: /* see table below */,
}),
});Send it once, on sign-up only
Fire this only when a new account is created – not on every login. Sending it on logins would create duplicate leads.
Fill in the values from the sign-up
That's the only part you map by hand. The field names differ a little per provider:
| Field to send | What to put in it |
|---|---|
firstname | The first name the user typed |
lastname | The last name the user typed |
email | The email address on the account |
click_id | localStorage.getItem('clickId') |
tag | Your campaign / source tag (a fixed word is fine) |
This is the form the automatic listener would catch – but sending it yourself here keeps all three sign-up paths consistent and gives you the name and email in the same place.
Google returns the profile from the ID token / userinfo. Map it like this:
| Field to send | Google field |
|---|---|
firstname | given_name |
lastname | family_name |
email | email |
click_id | From the OAuth state – see below |
tag | Your campaign / source tag |
Microsoft (Entra ID / Microsoft account) uses different property names:
| Field to send | Microsoft field |
|---|---|
firstname | givenName |
lastname | surname |
email | mail (fall back to userPrincipalName) |
click_id | From the OAuth state – see below |
tag | Your campaign / source tag |
Carry the click ID through Google / Microsoft SSO
For SSO there's one extra thing to solve: getting the click ID from before the redirect to your
callback after it. localStorage may be gone by the time the account is created – the callback
can run server-side, or land on a different subdomain.
The trick: use the OAuth state parameter
Put the click ID inside the OAuth state parameter before you redirect. Google and
Microsoft return state untouched on the callback, so the click ID survives the round trip –
and you can read it server-side. Decode it on the callback and send it with the Lead.
1. When the user clicks "Continue with Google / Microsoft", pack the click ID into state:
const clickId = localStorage.getItem("clickId") || "";
// Pack the click ID into `state`. Add your own CSRF token here too if you use one.
const state = btoa(JSON.stringify({ clickId, csrf: yourCsrfToken }));
// Google – Microsoft is the same idea with login.microsoftonline.com/common/oauth2/v2.0/authorize
const url = new URL("https://accounts.google.com/o/oauth2/v2/auth");
url.searchParams.set("client_id", GOOGLE_CLIENT_ID);
url.searchParams.set("redirect_uri", REDIRECT_URI);
url.searchParams.set("response_type", "code");
url.searchParams.set("scope", "openid email profile");
url.searchParams.set("state", state);
window.location.href = url.toString();2. On the callback, after the account is created, decode state and send the Lead:
// `state` comes back exactly as you sent it
const { clickId } = JSON.parse(atob(stateFromCallback));
await fetch("https://your-app.com/api/lead", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
click_id: clickId,
tag: "app_signup",
firstname: profile.given_name, // Microsoft: profile.givenName
lastname: profile.family_name, // Microsoft: profile.surname
email: profile.email, // Microsoft: profile.mail
}),
});Already using state for CSRF?
Most OAuth setups already send a random state to protect against CSRF – don't drop it. Just
store the click ID alongside it (as in the JSON above) and validate the CSRF part as usual
when it comes back.
Verify it's working
- Open your app from a tracked link (one carrying
lj_*parameters, e.g. a test ad URL or a link built with the UTM Builder) so a click ID is stored. - Create a test account – once via the form, once with Google, once with Microsoft.
- In Settings → Events the
Leadevent's Total count goes up and its status turns Active. - To inspect a single call (status code, payload, whether the click ID matched), open the API Postbacks log.
No click ID? Send the Lead anyway
A visitor who arrived organically or direct has no click ID, so clickId is empty – that's
fine. Still send the Lead: LeadJourney also matches on email, so the account is captured
and can be attributed later if that person clicked a tracked link before. Never block the
sign-up event just because the click ID is missing.
FAQ
Related articles
Events & the Events Manager
Create the Lead event and get your Conversion API credentials.
Install on a custom-coded website
Get the tracking script live in your React / Next.js app first.
How attribution works
What the click ID does once the Lead reaches LeadJourney.
API Postbacks log
Inspect each sign-up call and confirm it matched a click.
Track your iGaming funnel (registration → FTD → NGR)
The complete guide for sportsbooks and casinos – attribute every step back to the ad, affiliate or post that earned it. Track the registration, then the First-Time Deposit (FTD), redeposits and Net Gaming Revenue. The FTD and everything after it fire server-side from your player platform or CRM. The key idea: capture the click ID once at registration and replay it on every later deposit event.
Track LinkedIn from click to pipeline revenue
Use LeadJourney to attribute every LinkedIn click – sponsored posts and organic – to leads, won deals and pipeline revenue. See which sponsored post drives the most clicks, leads, sales and ROI, not just impressions.