Last Updated on December 15, 2025 by Caesar Fikson
If your funnel math doesn’t add up, trust the math.
You know the feeling. Clicks look healthy, CTRs are fine, traffic quality hasn’t changed, yet registrations and FTDs suddenly slide—or wobble in ways that don’t match seasonality, geo mix, or promo changes. Shaving or broken tracking? Before you point fingers, build evidence the way a forensic accountant would: reconstruct the click-to-cash chain, compare independent telemetry with the program’s reports, and isolate where value goes missing.
At NOWG, I treat this like an investigation. We don’t accuse; we measure. Then the facts do the talking.
What “shaving” looks like in practice (and what it often isn’t)
Shaving is the systematic under-reporting of referrals, registrations, or monetization events that should be credited to you.
The most common early tells: a sudden skew in device or browser ratios; registration spikes that don’t translate to FTDs despite unchanged bonus terms; postbacks that stop firing on certain subids; conversions imported days late so cookie windows lapse.
Equally common: entirely innocent causes—Safari’s ITP purging cookies, a timezone mismatch that misattributes late-night deposits, ad-blockers that kill pixels, or a simple reporting filter applied to your partner’s BI dashboard. Your job is to separate malice from math.
Build an “evidence spine” before you poke the bear
Three independent sensors, minimum.
First, your own server logs (or a lightweight redirect) that stamp every outbound click with a unique click_id and store the user agent, IP/ASN, referrer, timestamp, and landing URL. Second, a privacy-respecting analytics view (e.g., GA4 or your own dashboard) that tracks lander sessions, registration button clicks, and exit paths—no PII, just events.
Third, the operator’s S2S postback to your endpoint, keyed by that same click_id. When those three tie together, you get a tamper-evident chain of custody from your media to their cashier.
The click-to-cash chain: where value commonly disappears
Your ad → your page → affiliate link (with click_id) → operator’s lander → registration → KYC → first deposit → first wager.
Drops can be legitimate—KYC friction, rejected payment, fraud checks. But some are not: stripped parameters during redirects, broken postbacks, cookie windows too short for the GEO, mis-mapped subids, or models that silently prefer last-touch internal promos over your original referral.
A simple causality table you can actually use
| Symptom | Likely cause | How to prove it fast | What to do next |
|---|---|---|---|
| Clicks steady, lander sessions down | Bot or anti-spam filters blocking | Compare server redirect logs vs analytics sessions | Lower bot thresholds on your end; add server-side tracking |
| Regs steady, FTDs drop on Safari/iOS | ITP or cookie purge kills attribution | Segment by browser; look for Safari skew | Switch to S2S tracking; extend attribution window |
| Postbacks stop on certain subids | Operator tag manager update lost macros | Request raw server logs for those click_ids | Re-validate macro mapping; ship a QA subid daily |
| FTDs credited next day not same day | Timezone/reporting cutoff mismatch | Compare UTC stamps vs partner’s local cutoffs | Align to UTC in both postback and BI reports |
| All metrics fine, net rev collapses | Bonus abuse or policy change | Pull per-player bonus and net-win data | Adjust targeting; renegotiate terms or limit abuse cohorts |
S2S beats pixels when browsers get hostile
Modern browsers don’t love third-party cookies.
Apple’s Intelligent Tracking Prevention (ITP) has been hammering cross-site identifiers for years, which can quietly crush pixel-based affiliate attribution—especially on Safari and iOS-heavy traffic. If your operator still leans on front-end pixels and short cookies, your conversions will look shaved even if no one touched a scalpel.
Read Apple’s own write-up on full third-party cookie blocking to understand why you can’t “fix” this in the browser: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/. The practical fix is S2S: you generate a unique click_id, the operator stores it server-side on registration, and all monetization events fire to your endpoint via server-to-server postbacks keyed to that click_id. No cookie, no problem.
The minimum viable S2S contract (don’t go to war without it)
| Field (macro) | Why it matters | Audit tip |
|---|---|---|
| click_id | Joins your logs to theirs | Seed unusual IDs (e.g., NOWG-TS-1697041234) so they’re unmissable |
| event | registration, FTD, deposit, wager | Enforce allowed values; reject garbage |
| amount & currency | Cash confirmation | Reconcile against your own seed deposits |
| player_id (hashed) | Cohort analysis without PII | Hash algorithm documented or you can’t join |
| ts (UTC) | Window alignment | Reject future/past nonsense; store raw string and parsed time |
Honeytokens, seed accounts, and watermark deposits—done ethically
I seed every partner with a small set of QA users that never touch bonuses and always follow the same journey. The usernames carry watermarks (e.g., nowg_2025_11_23_1620) and I fund first deposits with “signature amounts” ($17.13, $19.87, amounts no normal cashier defaults to).
Those amounts become beacons in both the operator’s ledger and my own. If my postback says $17.13 credited at 16:27 UTC and the partner shows nothing—or shows $20 credited at local midnight—I’ve got a clean discrepancy to escalate. Keep this ethical: don’t abuse promos, don’t launder traffic via QA, don’t share anyone’s PII.
You’re testing plumbing, not gaming the house.
Funnel math that spots shaving in minutes
Before you argue, benchmark your funnel like a mechanic. Pick a calm 7-day window with at least 2,000 clicks (to tame randomness). Compute these:
- Lander take-rate = lander sessions ÷ outbound clicks
- Reg-rate = registrations ÷ lander sessions
- FTD-rate = FTDs ÷ registrations
- Deposit per FTD = total deposits ÷ FTDs
Now compare against your 90-day rolling medians. You’re looking for structural breaks, not noise. When FTD-rate falls 40% on Safari but is flat on Chrome, it’s not your copy—it’s attribution.
When deposit per FTD stays flat but FTD count drops while registrations hold steady, postbacks are dying. Build a tiny table and color it by device/browser; the pattern usually jumps out.
A sanity-check template you can paste into your doc
| Segment | Lander take-rate | Reg-rate | FTD-rate | Delta vs 90-day |
|---|---|---|---|---|
| All traffic | 0.54 | 0.23 | 0.18 | −22% FTD-rate |
| Safari/iOS | 0.52 | 0.24 | 0.09 | −53% FTD-rate |
| Chrome/Android | 0.56 | 0.22 | 0.19 | +2% FTD-rate |
If you see an iOS cliff and a Chrome plateau, stop arguing creative. Fix tracking.
Timezones and currencies: the silent killers
I’ve seen perfect postbacks “miss” because the operator cuts their day at 00:00 CET while your BI assumes UTC. Deposits near midnight slide into tomorrow on their side and never reconcile in your “today” report. Same for currency—if your dashboards aggregate inputs in EUR but a partner’s API returns USD without a consistent FX rate, your “missing $” are just rounding ghosts. Insist on UTC everywhere in the data contract and store both raw and normalized currency values with the rate applied that day. The day you don’t is the day you spend six hours chasing phantoms.
Cookie windows and internal last-click cannibalization
Some programs quietly run last-click attribution across their own touchpoints: internal banners, live-ops promos, push notifications. If your player registers via your link at noon, doesn’t deposit, and later taps a push banner at 19:00 before depositing, the deposit might credit to “house marketing”—or the affiliate with the last cookie, not you.
Ask bluntly: is attribution “affiliate vs affiliate last-click” or “affiliate gets priority over house”?
Then prove it.
Run a two-cell test: cell A with no internal promos on day one of the user’s journey, cell B with normal promos. If A’s FTD-rate is magically higher at the same operator, internal last-click ate your lunch.
GA4 and model mismatch: you’re not crazy, your model is
If you switched from last-click Universal Analytics to GA4’s data-driven attribution, your own analytics may move credit away from the affiliate touchpoint toward later interactions by design. That’s not shaving; it’s a model.
Review Google’s documentation on GA4’s data-driven attribution so you know what’s happening under the hood: https://support.google.com/analytics/answer/11517529.
For audits, stick to deterministic joins (click_id) over modeled shares. When you need “who gets paid,” models are commentary; click_ids are truth.
A controlled A/B route that exposes bad plumbing (without burning bridges)
Route 10–20% of your eligible traffic to the same operator through a second, isolated link with a distinct postback endpoint and a different click_id namespace (prefixes help). Keep geo, device, and placement identical. If stream A reports 100 registrations and stream B reports 62 across multiple days with identical quality signals on your side, the problem is not your audience.
With two independent feeds, the operator’s own logs can’t wave away variance as “seasonality.”
How to ask for data without starting a war
Good-faith operators will share raw logs for disputed click_ids: their registration timestamp, player hash, deposit totals, and postback attempts with status codes. Ask for precisely that, by listing 10–20 specific click_ids and times, not “send me everything.”
Provide your own evidence pack: your server log for each click_id, the analytics session ID, your postback log (including any 4xx/5xx responses), and the UTC windows you consider in scope.
Avoid blamey adjectives. Precision keeps everyone calm—and makes it easier for the partner’s engineers to fix what’s really broken.
A tidy escalation checklist that gets answers
| Item | Why it unlocks the fix |
|---|---|
| 10–20 disputed click_ids with UTC stamps | Engineers can search logs in seconds |
| Your redirect logs (IP/UA/referrer) | Proves the click actually happened |
| Postback receipts (raw JSON + status) | Shows whether their server tried—and what you replied |
| One screenshot per cohort (browser/device) | Visual pattern = fast empathy |
| Your ask (“replay postbacks” or “fix macro mapping”) | Engineers need a concrete action |
Distinguish shaving from variance (basic stats, no PhD)
Small programs can look erratic simply because sample sizes are tiny.
If your daily FTDs per partner hover around 8–12, a single VIP’s behavior can swing net revenue or FTD count by 20–30% day to day. Use weekly windows for inference and compute a simple proportion test: compare this week’s FTD-rate to the trailing 8-week average; if the 95% confidence intervals barely overlap, you’ve likely got a real shift.
And if the shift exists only on Safari or only on one GEO, it’s a tech problem nine times out of ten.
Red flags that justify pausing traffic
If a partner refuses to share raw logs for specific click_ids, if attribution rules are secret or change mid-month without notice, if postbacks randomly stop over weekends, if late “manual adjustments” appear in statements without per-player detail, or if their BI has no export of row-level events, pause and protect your bankroll.
Reputable programs will dig with you. If you get PR speak instead of packet captures, move spend.
Don’t break the law to prove a point
Never collect or store PII you don’t need, don’t coerce players into sharing screenshots of sensitive account pages, and don’t instruct users to circumvent a casino’s T&Cs to “force” conversions.
Keep your QA users separate from real traffic and never touch bonuses you didn’t earn. You’re running an audit, not a sting.
When tracking is fixed but money is still missing
Sometimes the plumbing is fine and the finance layer is the problem. Watch for negative carryover applied despite your contract, bundled products that swallow casino revenue under sports losses, or chargeback clawbacks applied to future months without documentation. Ask for the reconciliation waterfall by player segment:
deposits → withdrawals → bonuses → net win → fees/taxes → your share.
If that’s unavailable, your “accounting” is vibes. Push back.
A short personal note (and why I’m stubborn about process)
Years ago, we watched a mid-tier program “lose” our iOS-heavy conversions for weeks. The affiliate team swore everything was healthy. Our tables screamed otherwise—Safari FTD-rate had halved;
Chrome was flat. We seeded signature deposits with $17.13 and $19.87 amounts across both browsers, collected postbacks, and requested raw logs by click_id.
The fix landed 48 hours later: a tag-manager change had stripped query params on a specific iOS lander template. No drama, just evidence and a concrete ask.
Since then I refuse to escalate without the spine—server logs, analytics event IDs, and raw postbacks. You sleep better when the numbers are your bodyguard.
A pragmatic path to “trust but verify”
Run S2S everywhere you can. Stamp every outbound click with a unique click_id. Store your own logs. Align on UTC. Agree the event dictionary and stick to it. Seed obvious QA users with watermark deposits. Segment results by browser/device and by GEO before you assign blame. Use weekly windows for inference, not single days.
And when the data says it’s broken, escalate with specifics and ask for replay/reconciliation—not a lecture.
If you want to shortcut the setup, I built simple, battle-tested templates at NOWG—a click-id generator, a paste-and-go postback receiver with validation and replay, and a funnel-math dashboard that color-codes Safari vs Chrome so you see ITP-driven cliffs before your revenue “mysteriously” dips. Spin up the free tools, drop in your partner macros, and you’ll know by next week whether the program is crediting you correctly—or whether your stats are being quietly shaved.