Turning paper cheques into data? The eight steps, the CTS image specification, and what the October 2025 clearing change means: Cheque Reading: How to Build It →
Building the monitoring system rather than reading the obligations? The eight steps, the data model, and the timestamp the 7-day clock runs from: AML Monitoring Systems: How to Build It →
Every tool for this module, how to use each one, what it costs, the best combinations and three recommended builds: Fraud Build Sheet →
Fraud is not one problem
"Fraud" covers several unrelated problems that need different systems. Conflating them is why fraud projects stall.
| Type | What happens | Who loses |
|---|---|---|
| Account takeover | Someone gains access to a real customer’s account | The customer, then usually you |
| Application fraud | A fake or stolen identity opens an account or takes a loan | You |
| Transaction fraud | Unauthorised payments from a compromised account or card | Customer or issuer, depending on rules |
| Authorised push payment scam | The real customer is tricked into sending money themselves | The customer — and this is the fastest-growing category |
| Mule accounts | Real people let their accounts be used to move stolen funds | The original victim; you become the laundering rail |
| First-party fraud | A real customer borrows with no intention to repay, or disputes a legitimate charge | You |
The last two are the awkward ones, because the person is genuine and passes every identity check. No liveness system and no KYC process catches a real person acting for someone else. Only behaviour does.
The false positive problem
Catching fraud is easy if you do not mind blocking everyone. The entire difficulty is catching fraud without blocking genuine customers.
Consider what a false positive actually costs. A genuine customer has their payment declined at a checkout, or their account frozen. They call support. They are annoyed, possibly publicly. Published work on card-not-present fraud finds customers who experience a false decline are significantly more likely to churn within ninety days.
So the real objective is not "catch the most fraud". It is maximise fraud caught per unit of genuine-customer friction. Those produce very different systems.
A fraud system that blocks too aggressively gets switched off. Not by the fraud team — by whoever owns revenue. This happens routinely and is the most common way a technically good fraud system dies.
What AI can and cannot do here
| Task | How well AI does it |
|---|---|
| Score a transaction for risk in real time | Very well. Sub-50ms at high percentiles is achievable. |
| Spot behaviour unlike this customer’s normal | Very well. This is the core strength. |
| Find rings and networks of connected accounts | Well, with graph methods. Not possible looking at transactions one at a time. |
| Adapt to a new fraud pattern | Only after it has seen examples. There is always a window where the new attack works. |
| Tell a scam from a genuine transaction the customer chose to make | Poorly. Both are authorised by the real customer on their real device. This is the hardest open problem. |
| Explain why it blocked something | Partially. Enough for an analyst, rarely enough for a customer. |
| Decide whether to freeze an account | It should not, alone. Freezing has legal consequences — see the Advanced lane. |
The structural fact: fraud is a network, not a transaction
A single transaction viewed alone tells you very little. ₹40,000 moving at 2am is normal for some customers and alarming for others.
Fraud rarely happens in isolation. It happens in rings — shared devices, shared IPs, shared beneficiary accounts, funds hopping between accounts in sequence. The signal lives in the connections, not in any individual payment.
In India this is visible at scale. Stolen funds are typically layered through four to six hops across UPI-linked accounts before withdrawal. The single strongest mule signal is behavioural: a previously dormant account that suddenly receives and forwards multiple transfers within minutes.
No per-transaction model sees that. It is a graph problem, and it is why the Intermediate lane spends time on graph features rather than just better classifiers.
Real-time architecture and the latency budget
Fraud scoring has a hard deadline. If the decision arrives after the payment is authorised, you have built a reporting system, not a fraud system.
TRANSACTION ARRIVES
|
1. HARD BLOCKS sanctioned party, known-mule beneficiary,
| customer kill-switch active
| -> sub-millisecond, in-memory lookup
|
2. FEATURE ASSEMBLY three tiers, assembled in parallel:
| a) request features amount, channel, beneficiary, time
| b) profile features precomputed, from a feature store
| (customer's 30/90-day baselines)
| c) velocity features streaming counters over 1min/1h/24h windows
| (Redis / Flink / Kafka Streams)
|
3. MODEL SCORING gradient-boosted model on tabular features
| + graph embedding for the entity network
| -> budget: under 50ms at p99
|
4. POLICY LAYER score + rules + customer context
| -> allow | step-up | review | block
|
5. ACTION step-up beats block wherever possible
|
6. LOG + FEEDBACK outcome eventually labelled -> retraining
LATENCY BUDGET (typical authorisation window is 100-300ms total)
hard blocks < 1 ms
feature assembly < 25 ms <- the usual bottleneck
model inference < 15 ms
policy + response < 10 ms
Precompute everything that can be precomputed. A feature that needs a
database join at scoring time is a feature you cannot afford.The feature store is the architecture
Most of the engineering difficulty is not the model. It is having the right numbers available within twenty milliseconds.
Customer baselines — average transaction size, usual hours, typical beneficiaries, normal device — are computed in batch and read at scoring time. Velocity counters are maintained in a streaming layer. Neither can be a database query when the payment is waiting.
Training-serving skew is the silent killer here. If a feature is computed one way in your offline training pipeline and slightly differently in the online path, the model performs worse in production than in backtest and nobody can work out why. Compute features once, in shared code, used by both paths.
Velocity features — the cheapest signal you will ever buy
Before any model, before any graph, build velocity counters. They carry more fraud signal per unit of engineering effort than anything else available.
import time
from collections import defaultdict, deque
class VelocityTracker:
"""Sliding-window counters keyed by any entity: customer, device,
beneficiary, IP. Velocity features carry more fraud signal per byte
than almost anything else, and they are cheap."""
WINDOWS = {"1m": 60, "1h": 3600, "24h": 86400}
def __init__(self):
self.events = defaultdict(deque) # key -> deque of (ts, amount)
def record(self, key, amount, ts=None):
ts = ts or time.time()
self.events[key].append((ts, amount))
self._evict(key, ts)
def _evict(self, key, now):
cutoff = now - max(self.WINDOWS.values())
dq = self.events[key]
while dq and dq[0][0] < cutoff:
dq.popleft()
def features(self, key, ts=None):
ts = ts or time.time()
self._evict(key, ts)
dq = self.events[key]
out = {}
for name, secs in self.WINDOWS.items():
window = [(t, a) for t, a in dq if t >= ts - secs]
out[f"{key.split(':')[0]}_count_{name}"] = len(window)
out[f"{key.split(':')[0]}_amount_{name}"] = sum(a for _, a in window)
out[f"{key.split(':')[0]}_distinct_amts_{name}"] = len({a for _, a in window})
return out
# The combinations that matter most in practice:
# customer:<id> -> is this customer behaving unusually?
# device:<fp> -> one device driving many customers = ring
# beneficiary:<vpa> -> many senders to one destination = mule
# ip:<addr> -> shared infrastructure
#
# The ratio features beat the raw counts:
# amount / customer_avg_amount_30d
# count_1h / customer_avg_count_per_hour_30d
# A customer doing 3 transactions in an hour is unremarkable.
# A customer who averages 0.2/hour doing 3 in an hour is not.The key move is at the bottom of that block: use ratios against the customer’s own baseline, not raw counts. Absolute thresholds produce the classic complaint that a fraud system flags every high-value customer. Relative thresholds do not.
Graph features — seeing the ring
A per-transaction model cannot see that forty accounts share one device, or that money entered an account and left it within ninety seconds. Graph features can.
import networkx as nx
def build_transaction_graph(txns):
"""Nodes: accounts, devices, beneficiaries.
Edges: observed relationships. Directed for money flow."""
G = nx.DiGraph()
for t in txns:
G.add_edge(t["from_account"], t["to_account"],
amount=t["amount"], ts=t["ts"])
if t.get("device_fp"):
G.add_edge(f"dev:{t['device_fp']}", t["from_account"], kind="uses")
return G
def mule_signals(G, account, txns_by_account):
"""The behavioural fingerprint of a mule account, per RBI-published
detection logic and industry practice."""
f = {}
inflows = list(G.in_edges(account, data=True))
outflows = list(G.out_edges(account, data=True))
f["distinct_senders"] = len({u for u, _, _ in inflows})
f["distinct_beneficiaries"] = len({v for _, v, _ in outflows})
# THE signal: money arrives and leaves almost immediately
pass_through = []
for _, _, ie in inflows:
for _, _, oe in outflows:
gap = oe["ts"] - ie["ts"]
if 0 < gap < 600: # out within 10 minutes
ratio = oe["amount"] / ie["amount"] if ie["amount"] else 0
if 0.85 < ratio < 1.0: # nearly the full amount
pass_through.append(gap)
f["pass_through_events"] = len(pass_through)
f["median_hold_seconds"] = (sorted(pass_through)[len(pass_through)//2]
if pass_through else None)
# Dormancy then sudden activity - the classic mule awakening
ts = sorted(t["ts"] for t in txns_by_account.get(account, []))
if len(ts) >= 2:
gaps = [b - a for a, b in zip(ts, ts[1:])]
f["max_dormant_days"] = max(gaps) / 86400
f["dormant_then_burst"] = (f["max_dormant_days"] > 60
and len([g for g in gaps[-10:] if g < 300]) >= 3)
# Shared device across unrelated accounts
devices = [u for u, _, d in G.in_edges(account, data=True)
if str(u).startswith("dev:")]
f["accounts_sharing_device"] = max(
(len(list(G.successors(d))) for d in devices), default=0)
# Position in the laundering chain: funds typically hop 4-6 times
try:
f["downstream_depth"] = len(nx.descendants_at_distance(G, account, 3))
except Exception:
f["downstream_depth"] = 0
return fDo you need a graph neural network?
Probably not at first. Hand-engineered graph features — shared device counts, pass-through timing, in-degree and out-degree, distance to a known-bad account — capture a large share of the value and are explainable.
GNNs add real lift on top of that. Published comparisons of ensembles combining gradient-boosted trees with GNN embeddings report catching meaningfully more fraud than either alone, with lower false positives because the model sees context rather than an isolated transaction. Research results report false positive reductions around a third against GNN baselines.
The sensible progression: velocity features first, then hand-built graph features, then a GNN embedding fed as extra columns into your existing gradient-boosted model. That last pattern — GNN embeddings as features rather than the GNN as the classifier — keeps the explainability of trees while gaining network context.
The India context: mules, UPI and the shared-intelligence layer
Indian payment fraud has a specific shape, and infrastructure has been built specifically to counter it.
The mule economy
The dominant pattern is not the old OTP scam. It is recruitment at scale: thousands of legitimate account holders, often in tier-2 and tier-3 towns, paid or tricked into letting their accounts move stolen funds. Proceeds hop four to six times across UPI-linked accounts before withdrawal.
The numbers are large. Over 2.47 million Layer-1 mule accounts had been flagged nationally as of early 2026, and one intelligence report counted 524,121 suspected mule accounts and VPAs flagged in March 2026 alone.
The national infrastructure you should know about
| System | What it does |
|---|---|
| MuleHunter.AI | RBI-developed ML model for mule account detection. Piloted in two public sector banks in late 2024, since expanded across dozens of banks. Reported to have detected over 4.7 lakh mule accounts. |
| DPIP — Digital Payments Intelligence Platform | Built with NPCI. Shares verified fraud signals across banks and fintechs in real time, so a pattern invisible to one institution is visible to the network. |
| CPFIR — Central Payment Fraud Information Registry | Cross-bank fraud reporting and tracking, operating since 2024. |
| IDPIC | Indian Digital Payment Intelligence Corporation, incorporated as a Section 8 company in October 2025, mandated to detect and analyse digital payment fraud in real time. |
| I4C Suspect Registry | Law-enforcement intelligence on suspect accounts, shared with RBIH for faster detection across banks. |
| .bank.in / .fin.in domains | Restricted domains for verified financial institutions, aimed at phishing. |
An RBI discussion paper circulated in April 2026 proposed further user-facing controls including a transaction lag on certain payments, a customer kill switch, trusted-person authorisation and a cap on flows through flagged accounts. Proposals at time of writing, with consultation open. Check the current position before designing around any of them.
The registry
Fraud detection platforms
Verified May 2026Device and behavioural signals
Verified May 2026Build-your-own stack
Verified May 2026Registry reflects what was publicly visible in May 2026. Indian fraud infrastructure is moving unusually fast — verify current status of DPIP, MuleHunter access and reporting obligations directly with RBI and NPCI sources.
A prompt for designing your fraud system
You are a fraud risk lead who has built real-time detection for
digital payments in India.
My situation:
- Product: [e.g. UPI-based payments app / lending app / neobank]
- Transaction volume: [per day]
- Average ticket: [amount]
- Current controls: [describe, or "none"]
- Known fraud types hitting me: [describe, or "unknown"]
- Latency budget available: [ms]
- Team size: [describe]
Give me:
1. The three fraud types most likely to hit this product first,
and why.
2. A velocity feature set I can build in week one with no ML - the
specific counters and the ratio features derived from them.
3. Rules to deploy before any model, with starting thresholds and
how to calibrate them.
4. What to add in month 2-3: graph features, then models.
5. A step-up authentication ladder - which risk bands get which
challenge, designed to minimise friction on genuine customers.
6. What I must log for RBI fraud reporting.
7. The three ways fraudsters will adapt once these controls are live.
Be concrete about thresholds and say where they must be calibrated
on my own data rather than copied.Step up, do not block
The single highest-leverage design decision in a fraud system is replacing the binary block-or-allow with a graduated ladder of friction.
BANDS = [
# (max_score, action, friction_cost, description)
(0.20, "allow", 0, "no intervention"),
(0.55, "allow_watch", 0, "allow, but flag for post-hoc review"),
(0.75, "step_up_soft", 1, "in-app confirm: 'You are sending Rs X to Y'"),
(0.88, "step_up_hard", 3, "OTP or biometric re-auth"),
(0.95, "delay_review", 8, "hold, notify customer, analyst reviews"),
(1.01, "block", 20, "decline and alert"),
]
def decide(score, ctx):
"""Context modifies the band, it does not replace it."""
for ceiling, action, friction, _ in BANDS:
if score < ceiling:
break
# Escalate on contextual aggravators
if ctx.get("beneficiary_first_seen") and ctx.get("amount") > ctx.get("cust_p95_amount", 1e9):
action = _escalate(action)
if ctx.get("beneficiary_on_suspect_registry"):
return "block", "beneficiary_flagged"
if ctx.get("device_new") and ctx.get("credential_changed_24h"):
action = _escalate(action) # classic account-takeover pair
# De-escalate on mitigators - this is what protects revenue
if ctx.get("beneficiary_paid_before_count", 0) >= 3 and not ctx.get("device_new"):
action = _deescalate(action)
if ctx.get("customer_tenure_days", 0) > 730 and ctx.get("prior_fraud_count", 0) == 0:
action = _deescalate(action)
return action, "scored"
ORDER = ["allow", "allow_watch", "step_up_soft", "step_up_hard",
"delay_review", "block"]
def _escalate(a): return ORDER[min(ORDER.index(a) + 1, len(ORDER) - 1)]
def _deescalate(a): return ORDER[max(ORDER.index(a) - 1, 0)]
# The design principle: a step-up converts a would-be false positive
# into mild friction instead of a lost customer. Blocking should be
# reserved for cases where being wrong is cheaper than being right.This reframes the whole problem. A borderline transaction no longer forces a choice between losing money and losing a customer. It gets a confirmation prompt, which costs the genuine customer three seconds and costs the fraudster the attack.
RBI guidance on UPI fraud controls points the same way — for high-risk transactions, add step-up authentication rather than outright blocking, specifically to reduce customer friction. The regulatory direction and the commercial incentive agree here, which is not always the case.
Measuring it honestly
Fraud metrics are unusually easy to game, and the gaming is usually unintentional.
# The four numbers to report. Anything else is decoration.
1. FRAUD CAUGHT RATE (recall)
fraud blocked / total fraud attempted
Problem: you never see the denominator for fraud you missed
and nobody reported. Treat as a lower bound.
2. FALSE POSITIVE RATE
genuine transactions blocked or challenged / genuine transactions
Report separately for BLOCKED and CHALLENGED. They cost
very different amounts.
3. PRECISION AT THE BLOCK THRESHOLD
of everything we blocked, what share was genuinely fraud?
If this is under ~0.5 you are blocking more good customers
than bad actors and the system will be switched off.
4. VALUE-WEIGHTED, NOT COUNT-WEIGHTED
Rs of fraud prevented / Rs of genuine volume disrupted
This is the number that survives contact with the business.
WHAT NOT TO OPTIMISE
- accuracy: with a ~0.1% fraud rate, always predicting
"not fraud" gives 99.9% accuracy and catches nothing
- AUC alone: it ignores where your threshold actually sits
- alerts generated: rewards noise
THE NUMBER NOBODY MEASURES AND SHOULD
Analyst hours per fraud caught.
If your team spends 40 hours reviewing to stop Rs 50,000 of
fraud, the system is not working regardless of its AUC.The number that should shape your priorities
A Parliamentary Standing Committee report noted that of ₹2,294.79 crore lost to cyber fraud in India in 2022, ₹0.57 crore was recovered.
That is roughly a quarter of one percent. Once funds have moved through a layering chain, they are effectively gone.
The implication is blunt: prevention is not one option among several, it is the only one that works. Investment in detection and in the seconds before money leaves is worth vastly more than investment in recovery processes. Budget accordingly.
Feedback loops and the labelling problem
Fraud models need labels, and fraud labels are late, incomplete and biased.
- Late. A chargeback can arrive months after the transaction. Your model is training on a version of the world that has since changed.
- Incomplete. Much fraud is never reported. Small amounts especially. Your "genuine" class is contaminated with undetected fraud.
- Biased by your own actions. You never learn what a blocked transaction would have done. Same structural problem as reject inference in credit.
What to do
- Capture analyst decisions as labels immediately, rather than waiting for chargebacks. Faster and usually more accurate.
- Let a small random sample through above the block threshold. Costly, and it is the only way to measure precision at the threshold honestly.
- Log the counterfactual. When a step-up challenge is abandoned, record it — abandonment after challenge is a strong fraud signal and a free label.
- Retrain frequently. Fraud patterns shift far faster than credit risk. Monthly is typical; weekly is not unusual.
Shadow mode is the standard safe deployment pattern and worth following: score every live transaction in parallel with the existing system without taking action, compare outcomes, then transfer authority gradually on a defined percentage of volume with thresholds calibrated on live data.
The legal problem with freezing accounts
This is the part most technical treatments omit, and in India it is now consequential.
When fraud is detected, the instinctive response is to freeze the receiving account. Courts have begun pushing back on how broadly that is applied.
The Andhra Pradesh High Court ruled that a merchant’s bank account cannot be frozen merely because a fraudster used it to receive a small UPI payment, reasoning that a vendor cannot reasonably verify the criminal history of every customer. Similar rulings have come from Kerala and Rajasthan.
The tension is genuine: aggressive freezing stops laundering chains, and it also strands innocent merchants and customers who have done nothing wrong.
Designing proportionately
- Freeze the amount, not the account, where your systems allow it. Lien-marking the disputed sum leaves the rest of the balance usable.
- Tier your response to evidence strength. A single small inbound from a reported fraud is not the same as an account showing pass-through behaviour across forty senders.
- Build a fast unfreeze path with a human decision-maker and a service-level commitment. The reputational damage comes from the days it takes to reverse, not the freeze itself.
- Record the basis. If you cannot articulate why this account was frozen on this evidence, you will struggle when it is challenged.
Treat account freezing as a legal action with a technical trigger, not a technical action with legal paperwork. The distinction changes who signs off and what you log.
Adversarial adaptation
Fraud is the only risk domain where the data-generating process actively responds to your model.
| Your control | How it gets defeated | Counter |
|---|---|---|
| Amount threshold | Structuring — many payments just under the limit | Aggregate over windows, not per transaction |
| Velocity limits | Slow, patient fraud spread over days | Longer windows; graph view across accounts |
| Device fingerprinting | Anti-detect browsers, device farms, spoofed fingerprints | Behavioural biometrics; fingerprint instability is itself a signal |
| New-beneficiary flags | Aging beneficiaries — small legitimate payments first | Ratio of aging payment to eventual payment |
| Mule detection | More mules, shorter lifespans, one use each | Network-level sharing — this is exactly what DPIP exists for |
| Everything above | Social engineering: the real customer sends the money | Nothing technical fully solves this. See below. |
The unsolved one
Authorised push payment scams remain genuinely unsolved. The customer is real, the device is real, the credentials are real, the intent is real. Every signal says legitimate, because it is legitimate ’ the customer has been deceived about who they are paying and why.
Partial mitigations: beneficiary name-matching before confirmation, scam-pattern warnings tailored to the specific scam type, cooling-off periods on first payments to new beneficiaries, and behavioural biometrics that can detect the hesitation and coaching patterns of someone being talked through a transfer on the phone.
None of these are reliable. Be honest about it internally, because the alternative is a fraud team held responsible for a problem the technology cannot currently solve.
What to log, and where this module ends
{
"decision_id": "frd_01HY2...",
"transaction_id": "txn_88231",
"timestamp": "2026-05-23T02:14:08.221Z",
"latency_ms": 38,
"request": {"amount": 48500, "channel": "upi",
"beneficiary_vpa_hash": "sha256:2f1c...",
"beneficiary_first_seen": true},
"features": {
"velocity": {"cust_count_1h": 4, "cust_amount_24h": 122000,
"benef_distinct_senders_24h": 19},
"ratios": {"amount_vs_cust_p95": 3.2, "count_vs_baseline": 8.0},
"graph": {"pass_through_events": 0, "accounts_sharing_device": 1,
"distance_to_known_bad": 4},
"device": {"fp_stable": true, "device_age_days": 412, "new_device": false}
},
"model": {"name": "txn_risk_v6", "version": "6.2.0",
"score": 0.81, "top_contributors":
["amount_vs_cust_p95", "benef_distinct_senders_24h",
"beneficiary_first_seen"]},
"policy": {"version": "fraud-policy-v23", "band": "step_up_hard",
"escalations": ["new_beneficiary_high_amount"],
"deescalations": []},
"action": {"taken": "step_up_hard", "challenge": "otp",
"challenge_result": "passed", "final": "allowed"},
"outcome": {"reported_fraud": null, "chargeback": null,
"analyst_label": null, "label_pending": true},
"reporting": {"cfrms_reportable": false}
}Two fields earn their place. latency_ms, because a fraud system that silently degrades to 400ms has stopped being a fraud system. And label_pending, because the record must be findable later when the outcome finally arrives.
Where this module ends
- Identity verification at onboarding is Module 01. Fraud assumes the account exists; it does not verify who opened it.
- Credit risk is Module 02, and it is a different question — a credit model asks "can they repay", a fraud model asks "is this them". First-payment default sits awkwardly between the two and needs both.
- Sanctions screening, SAR filing and regulatory reporting are AML & Compliance. Fraud detection feeds AML but is not the same obligation.
- Disputes and chargebacks are Payments & Reconciliation.
- Model governance — validation, drift, documentation — is Governance.
Illustrative code throughout. A production fraud system handling real customer money needs security review, independent model validation, legal sign-off on the account-action path, and a documented process for the cases where the model is wrong — because it will be.
Sources
Every figure, rule and date on this page, and where to check it. Entries are typed so you can see which are primary-sourced and which are industry reporting.
- officialRBI circular on customer liability in unauthorised transactions — the zero, limited and full liability bands and the reporting clocks. www.rbi.org.in
- officialNPCI — UPI dispute resolution — the TCC/RET mechanism and the chargeback route. www.npci.org.in
- officialRBI directions on digital payment security controls — authentication and monitoring expectations. www.rbi.org.in
- industryMule-account typology reporting — the behavioural signals in the detection section.
Tooling, pricing and cost sources for this module are on its build sheet: sources →
Checked September 2026. Regulation in this area is actively developing; the date is part of the claim.
Ask an AI about this page
Opens your assistant with this page as the source, and a question rather than a summary. It will ask what you are building before it answers.
Nothing is sent from here. The link carries only this page’s title and address.