Use Cases

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.

For a sportsbook or casino the moment that pays the bills isn't the click or even the sign-up – it's the First-Time Deposit (FTD), and then every deposit after it. Those moments are spread out over days or weeks and almost all of them happen inside your player platform, not on a marketing page: someone registers, funds their account, redeposits, and generates Net Gaming Revenue (NGR) over their lifetime. This guide wires every one of those stages to LeadJourney so you can see, per campaign, ad set and ad, not just how many registrations an ad produced but how many turned into FTDs and how much NGR they generated.

Tracking has to be live first

Every stage is 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). The big-picture overview lives in the iGaming & Sports Betting playbook.

The iGaming funnel, mapped to LeadJourney

Pick the stages that exist in your product and skip the rest. Each event name is just the type you send – rename them to whatever your team already says (REG, FTD, QFTD, RD, NGR).

Funnel stageEvent typeHow you send itRevenue?
Registration / sign-upLeadForm listener, or the server route below
First-Time DepositWonClientServer route from your player platform / CRM✓ deposit amount
Qualifying deposit (optional)QualifiedDepositThe same server routeoptional
RedepositRedepositThe same server route
Net Gaming RevenueNGRUpdateThe same server route (from a nightly cron)
Reactivation (optional)ReactivationThe same server routeoptional

Two ways to send a stage – and when to use each

  • Registration → the form listener can often catch it. If players register with an on-page form, the tracking script's automatic form capture can fire Lead for you. If registration is an in-app step or uses SSO, send it from the server route instead.
  • FTD, redeposit, NGR → send them yourself from your player platform with a small server route. These are state changes only your backend knows about, so you define the trigger and post the event to LeadJourney's Conversion API.

The one idea that makes iGaming tracking work

Here's the catch that's unique to iGaming, and the single most important thing in this guide:

The click ID lives in the browser, at registration. But the FTD, redeposits and NGR happen later – almost always server-side, in your player platform or a nightly cron, with no browser and no localStorage in sight.

So you can't read the click ID again when those later events fire. The fix is simple:

Capture the click ID once, store it on the player, replay it forever

At registration, read localStorage.getItem('clickId') and save it on the player record in your own database (a column like lj_click_id). Every later event – FTD, redeposit, NGR – just reads that stored value back and sends it along. One capture, reused for the whole player lifetime.

Email and phone are your safety net

LeadJourney also matches events on email and phone. Always send them with every event, so even if a click ID was never captured (an organic, affiliate-redirect or direct registration) or got lost, the stages still stitch onto the same player – and can be attributed later if that person clicked a tracked link before. iGaming verifies phone numbers anyway, so it's a strong match key. See How attribution works.

Before you start

Grab your Conversion API credentials from Settings → Events → Conversion API:

  • Your Webhook URL – looks like https://api.leadjourney.io/api/v1/postback/<your-id>.
  • Your Postback Secret – treat it like a password; it stays on your server and is never exposed to the browser.

Then create the events you'll use in the Events Manager. Lead and WonClient exist in every workspace by default; add the deposit stages:

EventSuggested modeNotes
Lead (registration)Ignore duplicate postbacksOne per player – drops accidental repeats. Keep Send back to Ads on.
WonClient (FTD)Edit conversion with new dataTurn Include Revenue Value on and send the deposit amount. Keep Send back to Ads on.
QualifiedDepositIgnore duplicate postbacksOnly fires once, when a deposit clears your threshold.
RedepositCreate new conversionEach redeposit is a new event – or increment WonClient instead (see below).
NGRUpdateEdit conversion with new dataKeeps one running NGR value per player up to date.

Why “Ignore duplicate postbacks” matters here

Player-platform events are often sent from retried webhooks or a cron that re-scans players, so the same event can fire more than once. Setting registration and qualifying-deposit events to Ignore duplicate postbacks keeps your funnel counts honest. See the mode table in Events & the Events Manager.

Step by step

Capture and store the click ID at registration

When a new player registers, read the click ID from the browser and persist it on the player. Do this once – it's what every later deposit event depends on.

// In your registration handler, the moment the account is created
const clickId = localStorage.getItem("clickId") || "";

// Save it on the player record (pseudo-code – use your own ORM / DB)
await db.players.update(newPlayer.id, { lj_click_id: clickId });

Registration via app or SSO?

If players register in a native app or through social / SSO login, localStorage may not be where you read it. Carry the click ID through the OAuth state parameter (or your app's deep link) and store it the same way – the full pattern is in Track sign-ups in your app (incl. Google & Microsoft SSO).

Add one server route that sends any deposit event

This single route forwards an event to LeadJourney and keeps your Postback Secret safe on the server. The example is a Next.js App Router route; any backend (Express, a serverless function, your platform's webhook handler) works the same way.

// app/api/lj-event/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: d.type,           // "Lead" | "WonClient" | "Redeposit" | "NGRUpdate" | ...
        click_id: d.click_id,   // the value you stored at registration
        email: d.email,         // always send this – durable match key
        phone: d.phone,         // iGaming verifies phone anyway – another strong key
        first_name: d.first_name,
        last_name: d.last_name,
        revenue: d.revenue,     // deposit amount / NGR – leave out for plain registration
      }),
    }
  );

  return Response.json({ ok: true });
}

Use your own URL and secret

Replace YOUR-POSTBACK-ID and YOUR_POSTBACK_SECRET with the Webhook 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. The full payload field reference is on the Conversion API tab.

Fire each stage

At each step, call the route with the right type. Because you stored the click ID at registration, every call can include it – no browser needed.

Fire once, right after the account is created (same place you stored the click ID). If players register with an on-page form, the tracking script's form capture may already do this for you – in that case skip the manual call to avoid a duplicate.

await fetch("/api/lj-event", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    type: "Lead",
    click_id: localStorage.getItem("clickId"), // or the value you just stored
    email: newPlayer.email,
    phone: newPlayer.phone,
    first_name: newPlayer.firstName,
    last_name: newPlayer.lastName,
  }),
});

The headline conversion. Fire it from your player platform the moment the player's first deposit clears – read the stored click ID and send the deposit amount as revenue:

await sendEvent({
  type: "WonClient",
  click_id: player.lj_click_id,  // stored at registration
  email: player.email,
  phone: player.phone,
  revenue: deposit.amount,       // the FTD amount, in your workspace currency
});

Counting only “qualifying” FTDs?

If bonus-only or tiny deposits shouldn't count, send a separate QualifiedDeposit (type QualifiedDeposit) only when the deposit clears your threshold, and report on that instead of raw FTDs. You can send both – WonClient for every FTD and QualifiedDeposit for the ones that count.

Each subsequent deposit. Send it with revenue so the player's total value grows over time:

await sendEvent({
  type: "Redeposit",
  click_id: player.lj_click_id,
  email: player.email,
  revenue: deposit.amount,
});

Two ways to count repeat deposits

Either send each Redeposit as its own event (Create new conversion), or keep one WonClient per player and add each deposit on top with Keep original data, increment payout – see the section below.

NGR usually has no single user action behind it – it's a calculated figure. Fire it from a nightly cron that totals each player's net gaming revenue and sends the latest value:

// Pseudo-code for a daily job
const players = await db.players.activeSince(yesterday);
for (const p of players) {
  await sendEvent({
    type: "NGRUpdate",
    click_id: p.lj_click_id,
    email: p.email,
    revenue: p.ngrToDate,   // running NGR for this player
  });
}

NGR is the truest value signal

FTD count tells you who funded; NGR tells you who's actually profitable after winnings and bonus cost. Set NGRUpdate to Edit conversion with new data so each run updates the same value rather than stacking.

Affiliate traffic

Affiliates and influencers send players to your registration page, which already runs the tracking script – so their players are captured automatically. To compare partners against your direct media on the same CPR / CPA / NGR basis, give each affiliate a unique source / sub-id in their link (built with the UTM Builder or your own URL parameters) so it lands as its own traffic channel. Every event above then carries the same click ID, so FTDs, redeposits and NGR are attributed per affiliate – ideal for settling CPA, revenue-share or hybrid deals against real value.

Post-FTD value (NGR and player LTV) without double-counting

A player keeps depositing and generating revenue – decide once how that should show up:

  • One conversion, kept up to date – set WonClient (or NGRUpdate) to Edit conversion with new data so the latest figure updates the same conversion (good for "current player value / NGR").
  • Every deposit as new revenue – set WonClient to Keep original data, increment payout so each deposit adds on top (good for "total deposited"). This mode is WonClient-only.
  • Net out withdrawals / bonus cost – send a conversion with negative revenue, or let your NGRUpdate already account for them so NGR stays the source of truth.

See the mode table in Events & the Events Manager.

Verify it's working

  1. Open your site 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. Walk the funnel with a test player: register, make a test First-Time Deposit, then a redeposit (or run your NGR cron).
  3. In Settings → Events each event's Total count goes up and its status turns Active.
  4. To inspect a single call (status code, payload, whether the click ID, email or phone matched), open the API Postbacks log.

No click ID? Send the event anyway

A player who arrived organically, via an affiliate redirect or direct may have no click ID, so click_id is empty – that's fine. Still send every stage with the email and phone: LeadJourney matches on those too, so the player is captured and can be attributed later if they clicked a tracked link before. Never block a deposit event just because the click ID is missing.

FAQ

On this page