Bot and fake account detection for fintech, before you pay for KYC
Maskbreak checks the visit, not the applicant: whether a script, a proxy or a fake browser is behind the form, and whether the device already sits behind other accounts of yours. Your server gets back allow, review or block with the reasons, in time to decide whether an application is worth an identity check, whether a login needs a second factor and whether a payout should wait. It does not verify identity documents, and it does not replace MFA.
Four moments where a fintech pays for fraud
Each one has a price when the visitor is not who they claim to be: a KYC fee spent on a fake applicant, a drained balance, a payout you cannot pull back, a bonus paid twice. The check is the same at every one of them: two tokens from the browser, one server-side call, one decision.
Onboarding, before the identity check
Farmed applications arrive from fake browser profiles behind residential proxies, or from scripts that fill the form faster than anyone reads it. A block here (proxy_detected, antidetect_browser, automation_detected) means the application never reaches your KYC vendor, so you never pay for that check. A review goes to KYC on the stricter path.
Login, against account takeover
Credential stuffing tests leaked passwords from a different home address on every attempt, so per-IP limits never trip. An attempt flagged for automation or a proxy is refused before the password is checked, so it tells the bot nothing about which pairs work. A VPN on its own is review: ask for the second factor, do not lock the customer out.
Payouts, withdrawals and new beneficiaries
A taken-over account is worth nothing until the money moves, so this is where the attacker has to show up again. Check the visit a second time. Send the account ID and device.linked_accounts says how many of your accounts this device has been used with. Two can be a household sharing a laptop. One device behind the withdrawals of several customers is what a mule or farm operation looks like from your side: hold the payout.
Promo credits, referral and signup bonuses
A bonus is a published price for a fake account. Check again at the claim, not only at signup: one device behind several claiming accounts raises multi_account_device, and a fake browser profile can raise antidetect_browser. Hold the credit rather than the account. A real customer gets the credit a little later; a farm loses its revenue.
Fake account detection for fintech: what gives a farmed application away
The documents are the part the attacker paid for. A synthetic or rented identity is built to pass document checks, and KYC catches it late, at a fee per check. Faking the visit at volume leaves traces instead. Every “different person” needs a different connection and a different device, application after application, and that shows up as a relay, a tampered browser or a device reused across applications, which can be checked before you pay for KYC.
Proxy plus fake browser is the farming signature. A residential proxy gives each application a clean home address in an eligible country, and a fake (antidetect) browser gives it a fresh device profile. When Maskbreak’s network intelligence recognises the exit as a proxy, it reports network.proxy: true while network.datacenter is false; not every residential exit is known. Maskbreak’s device check reports signs that the browser environment is being faked (device.antidetect, with a tampering_score from 0 to 1). Both signals are block under the default policy.
A new profile per application does not reset the check. It looks for signs that the browser is lying about itself rather than for a device it has seen before. How much it sees varies with the tool and its settings.
Web visits only. emulator_detected is also block by default where the device check reports it, but the check runs in the browser. It is not a mobile-app SDK and does not see traffic from your native app.
Repeat devices, counted inside your own account. Send your application or account ID with each check. device.linked_accounts counts how many of your accounts the device has been used with, and the reason multi_account_device appears once that is more than one. IDs are linked as keyed hashes and never compared with another customer’s. The reason is added without changing the decision. To act on it, escalate in your own code (treat an allow that carries multi_account_device as review, or apply your own threshold to device.linked_accounts), or set Multi-accounting to Review or Block on the console’s Rules tab. A rule changes the decision only for the signal it names, so a proxy, bot, fake-browser or emulator block on the same visit stays a block. The same count catches self-referral and bonus farming.
Throwaway inboxes. Send the applicant’s email too, and a disposable domain adds disposable_email, which moves an allow to review. It is a domain check only: no mailbox verification, no email or phone reputation. More on fake signups.
What it will not catch. One patient fraudster applying once, from their own laptop on their own broadband, produces an ordinary visit and an allow. That application is a job for your KYC vendor and transaction monitoring. The visit check takes out the industrial part (the farms, the scripts, the rented device profiles) so the expensive checks spend their time on the rest.
behind a run of signup-bonus registrations that each look like a different person on a different device. The fintech playbook takes the farming stack apart (residential proxy pool, fake browser profiles, throwaway inboxes, scripted form fill) and shows where each layer breaks. It is a worked model, not a customer result.
Read the fintech playbookBot detection for fintech: login, payouts and one-time codes
Credential stuffing looks like a thousand strangers. Each attempt comes from a different residential address, so the per-IP limits and blocklists that stop one noisy server see nothing. What the attempts share is the tooling, which can show up as automation_detected for scripted and headless browsers, proxy_detected for the relay, tor_exit_node for Tor. All three are block by default. More in credential stuffing detection at the login.
Review is a step-up, not a refusal. A customer signing in over a VPN from a hotel is normal, which is why a VPN on its own returns review, with the VPN service named when known (network.service). Treat review as “ask for the passkey or second factor now”, not “decline”. The password and MFA still decide who the user is; the visit check decides how much proof to ask for. Why blanket VPN blocks cost real customers.
Check again before money leaves. A session that passed login can still be hijacked later, so evaluate the visit again on withdrawals, payouts, new beneficiaries and changes to payout details. You can store the device.visitor_id seen at sign-in and compare it at payout: a different device is a reason to step up. It is a risk signal, not proof of identity or a cryptographic device binding. See the account takeover playbook.
Gate the one-time code, too. Every OTP you send costs money, and a bot requesting codes for number ranges it earns a share on never types them in. A visit check before the send keeps scripted requests off your SMS bill. The SMS pumping guide shows the send-to-verify ratio that exposes it in your own data.
What the fraud detection API returns
Two example responses, selected fields. The first is a signup application from a fake browser behind a residential proxy, on a device already used for three other applications. The second is an existing customer signing in over a VPN.
// Example response: onboarding, fake browser behind a residential proxy { "decision": "block", "risk_score": 93, "reasons": ["proxy_detected", "antidetect_browser", "multi_account_device"], "network": { "vpn": false, "proxy": true, "datacenter": false, "tor": false }, "device": { "antidetect": true, "automation": false, "tampering_score": 0.8, "linked_accounts": 4, "multi_account": true } } // Example response: existing customer, VPN only { "decision": "review", "risk_score": 35, "reasons": ["vpn_detected"], "network": { "vpn": true, "proxy": false, "datacenter": false, "tor": false, "service": "<operator name when known>" } }
Route on decision, not on the score. A rule you set on the Rules tab changes decision and keeps the engine’s own answer in engine_decision, so your code never re-derives a threshold. See decision versus score.
Reasons you can put in a case note. Every reason is a stable machine code, which gives support and compliance a written answer to “why was this application declined?”. Keep it out of the customer-facing error: naming the signal that fired is free tuning feedback for the attacker.
Missing evidence is not a clean result. When the browser check did not complete, the device block is absent; once you enforce, hold the action for a verification step instead of treating it as allow. Each response also reports its own processing time in evaluated_in_ms, and measured figures are on /status.
What allow, review and block should do at each moment
Maskbreak returns the decision; what it sets off is your policy. A starting point that keeps real customers moving and makes the attacker pay at the step that matters:
- Onboarding.
block: decline before the KYC session is created.review: send the applicant to KYC on the enhanced tier.allow: standard KYC, which still decides who the applicant is. - Login.
block: refuse before the password is checked, with a generic error.review: ask for the passkey or second factor.allow: your usual login and MFA policy, unchanged. - Payout, withdrawal or new beneficiary.
block: hold the money and alert your team.review: step up, then release after a delay you choose.allow: pay out, with your transaction monitoring still in place. - Promo, referral or signup credit.
block: no credit.review: hold the credit until the account shows real activity.allow: pay it. - No browser evidence. Missing tokens,
degraded: trueor nodeviceblock is not a clean result, and scripted credential stuffing often sends no browser evidence at all. At login, hold before the password is checked and put a verification step first. At onboarding, hold the application before the KYC session is created. At payout, hold the money. - Maskbreak unreachable. A genuine outage needs a fallback policy you choose and write down before you enforce, not during the outage: for example the enhanced KYC tier at onboarding, the second factor at login, and a hold at payout, because a payout cannot be pulled back. Fraud API timeouts covers the options.
What Maskbreak does not do for a fintech
No KYC, document or liveness checks. It does not read passports, match selfies or look up credit files, and it does no sanctions, PEP or adverse-media screening. It sits in front of the vendor that does.
No AML transaction monitoring or payment-risk scoring. It scores the visit, not the transfer, the card or the order. There is no chargeback guarantee.
Not MFA. It tells you when to ask for more proof. Passwords, passkeys, second factors and session management stay yours.
No shared identity network and no review team. Account linking counts your own accounts only, and your account IDs are never compared with another customer’s. There is no shared identity or consortium network, and no analyst reviews your cases. The device history fields (device.times_seen, device.first_seen) are the exception to “yours only”: they count sightings of the device across all checked traffic.
No published detection rate. None has been measured, so none is quoted. Run the check in watch mode on your own traffic and judge it by your own Events.
No compliance by association. Using a fraud API does not by itself make a deployment compliant. The check needs no name or document, but the visitor’s IP address is personal data under GDPR; the DPA and the privacy notice describe what is processed and for how long.
Put the check in front of the identity step
The browser snippet (sentinel.js) adds two tokens to your form; your server forwards them with the API key. The example runs at onboarding, before the KYC session is created, and starts in watch mode: it logs each verdict, lets every application through and fails open if Maskbreak is unreachable. After a few days, compare the log with the Events in your console, then switch to enforce.
The same pattern goes on login, payout and bonus-claim routes, each with fresh tokens from that page’s form. Official SDKs: npm @sentinelsup/sdk, PyPI sentinelsup, Packagist sentinelsup/sdk; other stacks call the HTTP API (see the Rails, Go and Spring Boot guides). The integration guide has the full enforce checks.
// Onboarding: evaluate the visit before you create the KYC session // Watch mode first: log the verdict, always continue, fail open const body = req.body || {}; // sentinel.js adds monocle + sentinel_fp to your form let risk = null; try { const verdictRes = await fetch('https://maskbreak.com/v1/evaluate', { method: 'POST', signal: AbortSignal.timeout(5000), headers: { 'Authorization': 'Bearer ' + process.env.MASKBREAK_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ token: body.token || body.monocle, // network evidence fingerprintEventId: body.fingerprintEventId || body.sentinel_fp, // device evidence tz: body.tz || body.sentinel_tz, accountId: String(user.id), // the applicant record your server created, never a form field email: user.email // disposable-domain check; the address is not stored }) }); if (verdictRes.ok) risk = await verdictRes.json(); } catch {} // unreachable or timed out: onboarding still runs console.log('[maskbreak]', JSON.stringify({ decision: risk?.decision ?? null, reasons: risk?.reasons ?? null, risk_score: risk?.risk_score ?? null, degraded: risk ? !!risk.degraded : null })); // Enforce later: if (risk?.decision === 'block') return res.status(403).json({ error: 'Application declined' }); // Keep the message generic (never name the signal). Missing browser evidence is not a clean result: hold it before KYC. // if (risk && (risk.degraded || !risk.device)) return res.status(409).json({ error: 'Verification required' }); // Review, and your chosen outage fallback (Maskbreak unreachable), go to the stricter KYC path: // if (!risk || risk.decision === 'review') user.kycTier = 'enhanced';
Fintech fraud detection questions
POST /v1/evaluate before it creates the application or checks the password. Depending on the tool and its settings, scripted and headless browsers can raise automation_detected, relays proxy_detected or tor_exit_node, and fake (antidetect) browser profiles antidetect_browser. All of these are block under the default policy, and real customers get no CAPTCHA to solve.device.linked_accounts) and adds multi_account_device when that is more than one. IDs are linked as keyed hashes inside your account only; they are never compared with another customer’s. The reason does not change the decision on its own. Escalate an allow that carries it to review in your own code, or set Multi-accounting to Review or Block on the console’s Rules tab. Either way a proxy or fake-browser block on the same visit stays a block: a rule changes the decision only for the signal it names.Run it on your onboarding form first
Free plan: 10,000 visitor checks a month, every signal included, no card. Paid plans from €29 a month.