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 stage | Event type | How you send it | Revenue? |
|---|---|---|---|
| Registration / sign-up | Lead | Form listener, or the server route below | — |
| First-Time Deposit | WonClient | Server route from your player platform / CRM | ✓ deposit amount |
| Qualifying deposit (optional) | QualifiedDeposit | The same server route | optional |
| Redeposit | Redeposit | The same server route | ✓ |
| Net Gaming Revenue | NGRUpdate | The same server route (from a nightly cron) | ✓ |
| Reactivation (optional) | Reactivation | The same server route | optional |
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
Leadfor 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
localStoragein 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:
| Event | Suggested mode | Notes |
|---|---|---|
Lead (registration) | Ignore duplicate postbacks | One per player – drops accidental repeats. Keep Send back to Ads on. |
WonClient (FTD) | Edit conversion with new data | Turn Include Revenue Value on and send the deposit amount. Keep Send back to Ads on. |
QualifiedDeposit | Ignore duplicate postbacks | Only fires once, when a deposit clears your threshold. |
Redeposit | Create new conversion | Each redeposit is a new event – or increment WonClient instead (see below). |
NGRUpdate | Edit conversion with new data | Keeps 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(orNGRUpdate) 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
WonClientto Keep original data, increment payout so each deposit adds on top (good for "total deposited"). This mode isWonClient-only. - Net out withdrawals / bonus cost – send a conversion with negative
revenue, or let yourNGRUpdatealready account for them so NGR stays the source of truth.
See the mode table in Events & the Events Manager.
Verify it's working
- 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. - Walk the funnel with a test player: register, make a test First-Time Deposit, then a redeposit (or run your NGR cron).
- In Settings → Events each event's Total count goes up and its status turns Active.
- 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
Related articles
iGaming & Sports Betting playbook
The big-picture overview: funnel, setup and reporting for operators.
Events & the Events Manager
Create your funnel events, pick modes, and get the Conversion API credentials.
Track sign-ups in your app (incl. SSO)
The deep dive on the registration event, including Google & Microsoft SSO.
API Postbacks log
Inspect each FTD and deposit call and confirm it matched a click, email or phone.
Track your full SaaS funnel (sign-up → trial → purchase)
The complete guide for SaaS products – attribute every stage of your lifecycle back to the ad, post or email that earned it. Track demo calls and free sign-ups, then Trial Started, Trial Ended and the paid Purchase. Purchases come in automatically through Stripe; the in-product milestones are sent with a tiny server route. The key idea: capture the click ID once at sign-up and replay it on every later event.
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.