Offer clicks from app WebViews without device data: why it happens and how to fix it
A device scan is what tells you that ten accounts are one phone, that a "Pixel 10" is an emulator, or that a clock set in Dhaka sits behind a US home IP. When offer clicks arrive without one, all of that goes quiet. On one offerwall network we watched a third of the clicks arrive that way, almost all of them from Android apps. This is what we found and what fixed it.
Short answer
Many Android apps open offer links in a WebView that loads your script but never sends its POST, because the app routes traffic through its own network layer or blocks unknown hosts. Retry the scan over a small GET when the POST does not answer, serve a short scan page on your click URL for clicks that arrive without one, and ask publishers to allow your scan domain and enable DOM storage in their WebView.
- Share without a scan
- About 32% of one network's offer clicks in a day.
- Main source
- One publisher app: 569 clicks a day, none with a scan.
- Cause
- The script loaded, but its POST never left the app.
- Fix
- A compressed GET retry after 1 second: 26 app scans an hour recovered at once.
What the gap looked like
The network sends every offer click to CashoutGuard with the publisher as the source, and most clicks come with a request_id from the browser script: the device, its clock, its GPU and its persistent ID. In the logs of a normal day, 924 of 2,917 offer clicks had none. That is not spread evenly. One publisher alone sent 569 clicks a day and not a single one carried a scan, while the publishers whose users click inside the offerwall page scored above 90%.
The user agents told the story. Almost every click from that publisher ended in "; wv)", the mark of an Android WebView: the publisher runs its own app and opens offer links inside it, instead of in the phone's browser.
Our server logs added the decisive detail. The same phones downloaded the agent script, so JavaScript was running, but no scan ever reached us from them: not even the preflight request a browser sends before some POSTs. The request was made inside the app and went nowhere.
Why WebViews lose the scan
A WebView is a browser engine the app controls. Three things it often does differently from Chrome explain most of the losses:
- The app filters network requests. Many apps intercept WebView traffic to add headers, block ads or keep users inside approved domains. Interception layers built on a plain HTTP client often handle GET requests and silently drop POST bodies, or allow only a list of hosts. Script tags and images pass; a JSON POST to an unknown host does not.
- Storage is off by default. Android WebViews start with DOM storage disabled. The persistent device ID cannot be kept, so even when a scan arrives, the same phone looks new more often.
- Short waits. A scan page that gives up after a few seconds loses the slowest phones, and a WebView cold start is slow.
None of this is fraud in itself. The publisher's app is simply built for showing pages, not for sending data to a third party. But farms know which doors are left open, and an app that hides devices is an easy door.
What a click without a scan cannot show
Without a device scan, a click is judged on its IP address and on what your server knows about the account. That still catches a lot: VPNs and hosting networks, Tor, IPs shared by many accounts, impossible offer speed. What it misses are the signals that need the device itself:
| Signal | Needs the device scan? |
|---|---|
| Same phone behind several accounts | Yes |
| Emulator or virtual machine | Yes |
| Phone model that does not match its GPU | Yes |
| Browser clock or language from another country | Yes |
| Automation and headless browsers | Yes |
| VPN, proxy, hosting and Tor IPs | No |
| IP shared by many accounts | No |
| Offer completed impossibly fast | No |
The first five are exactly how phone farms on residential proxies get caught: the IP is a clean home connection, but the device gives them away. A publisher whose clicks carry no scan is therefore the publisher where a farm is hardest to see.
Fix 1: let the script retry over GET
If script tags load and POSTs do not, the scan can travel the way the script itself came in. CashoutGuard's agent now sends the scan as a POST first. If there is no answer after one second, it sends the same scan as a compressed GET (about 1.6 KB, answered as JSONP), and whichever arrives first wins. Browsers where the POST works never notice; apps that drop it still get scanned.
To see where scans fail, the agent also reports a failed or abandoned scan with a single image request: the error and the step where it stopped (probe, post or retry). It carries no personal data. That report is how we knew the POST, not the script, was the problem.
In the first hour after the change, about 31 scans an hour arrived that would have been lost, 26 of them from app WebViews. You do not need to change anything for this: every site on the current agent gets it.
Fix 2: a scan page on your click URL
Some clicks never touch a page with your script at all: the publisher's app or server builds the offer link and the user lands straight on your click URL. For those, serve a short page from the click URL when the request has no scan yet. It loads the agent, scans the device in about a second and returns to the same URL with the request_id, which your click handler sends to CashoutGuard with the offer_click. After five seconds it continues without a scan, so no click is ever lost. The docs have the page ready to copy.
Then ask the publishers whose clicks still arrive without a scan to adjust their WebView. Two settings fix most of it:
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true // keeps the persistent device ID
// If the app filters requests, allow cashoutguard.com (GET and POST).How to keep an eye on it
- Install page: sources or publishers whose offer clicks mostly arrive without a scan in the last 7 days, each linked to the clicks themselves.
- Events: the "Without device scan" filter, combined with a source, shows exactly which clicks are affected.
- Overview: a warning when fewer than 60% of a week's offer clicks carry a scan.
- Alerts: a notification the same day if the share of browser events with a scan drops to a third of its usual level, which usually means the script was removed in a redesign or a new app version blocks it.
Coverage will never be 100%: some users block scripts, some phones are slow, and some apps are simply closed. The goal is that no publisher, and no farm, can hide a large part of its traffic from the device checks.