Use Cases

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 sendWhat to put in it
firstnameThe first name the user typed
lastnameThe last name the user typed
emailThe email address on the account
click_idlocalStorage.getItem('clickId')
tagYour 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 sendGoogle field
firstnamegiven_name
lastnamefamily_name
emailemail
click_idFrom the OAuth state – see below
tagYour campaign / source tag

Microsoft (Entra ID / Microsoft account) uses different property names:

Field to sendMicrosoft field
firstnamegivenName
lastnamesurname
emailmail (fall back to userPrincipalName)
click_idFrom the OAuth state – see below
tagYour 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

  1. 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.
  2. Create a test account – once via the form, once with Google, once with Microsoft.
  3. In Settings → Events the Lead event's Total count goes up and its status turns Active.
  4. 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

On this page