A step-by-step guide to building AML transaction monitoring in India. Eight stages, the options at each one, exactly how each step connects to the next, real costs, and what breaks. The obligations are covered in the AML module; this is the system.
Verified September 2026Free · No signupOfficial sources only
BeginnerStart here. No prior knowledge assumed.
How to use this page
This page walks you through building one thing: the system that watches accounts after onboarding
and decides which activity a human needs to look at.
Note
This guide is deliberately narrow, and the scoping is worth stating. The AML and Compliance module already covers sanctions screening, the layered matching cascade, threshold sensitivity methodology, SAR narratives and the five statutory reports; the build sheet covers lists, engines and costs. This page does not repeat any of that. It covers the part neither does: the system — the customer risk rating that drives everything, the data model, how a scenario becomes an alert, how an alert becomes a case, and how a case becomes a filing on the clock.
Read the module for what the obligations are. Read this for what you have to
build.
Four layers, and the clock
Four layers, and the first is the one almost nobody builds properly:
Layer
What it produces
Usually
Customer risk rating
A band that changes how everything below behaves
A field set at onboarding and never recomputed
Scenario engine
Alerts, from named patterns
Bought, then left at defaults
Case management
A decision, with a reason and a timestamp
A spreadsheet
Reporting
A filing, inside a deadline
Manual, and late
Watch out
The clock most systems cannot answer for. A Suspicious Transaction Report must be filed with FIU-IND within 7 working days of forming the suspicion — and the obligation arises when the suspicion is formed, not when the transaction occurred, which may have been months earlier. That is an architectural requirement, not a policy one: your system has to record the moment an analyst formed suspicion, distinctly from when the alert fired and when the case was opened. Most case-management implementations record neither, and that timestamp is exactly what an examiner asks for.
What this is not:
Not sanctions screening. Screening looks at who. This looks at
what they do. The module covers screening.
Not a vendor comparison. The build sheet does that.
Not a model. Step 3 explains why the first version should not be one.
The whole journey, in one table
#
Step
In plain words
1
Build the risk rating first
Everything below inherits it.
2
Get the data model right
Aggregates, not rows. The thing you cannot retrofit.
3
Write named scenarios
Not a score. Explainability is the requirement.
4
Tune before you launch
Against history, with the volume you can staff.
5
Turn alerts into cases
And record when suspicion formed.
6
File on the clock
7 working days, 15th of the month, FINnet 2.0.
7
Feed it back
Outcomes change the rating. The loop nobody closes.
8
Keep the record
Five years, and the decision trail, not just the filing.
IntermediateBuild it. Pipelines, tools and working code.
Step 1 — the customer risk rating
Step 1 — the customer risk rating, which is the whole system's input
The RBI framework rests on four pillars — customer acceptance policy, customer identification,
monitoring of transactions, and risk management — and a
risk-based programme means the monitoring behaves differently for different customers.
In practice, most implementations set a risk band at onboarding, store it in a column, and never
compute it again. That is not a risk-based programme; it is a label.
What the rating should be built from: the customer's type and business, their
geography, the products they hold, their expected activity
profile, their PEP and adverse-media status, and — the part that is
usually missing — their own behaviour since onboarding.
Note
The rating must be recomputed on events, not only on a calendar. The periodic review cycle differs by risk band, but a material change — in beneficial ownership, in transaction pattern, in address — should trigger a review regardless of where the schedule stands. A rating that only moves at its annual refresh is describing a customer who no longer exists.
Steps 2 to 4 — data model, scenarios, tuning
Steps 2 to 4 — the data model, the scenarios, and tuning
Python and SQL — steps 2 to 4, the data model, named scenarios, and tuning
# STEP 2. THE DATA MODEL. THE PART YOU CANNOT RETROFIT.
# Monitoring asks questions about BEHAVIOUR OVER TIME. A transactions table
# alone forces every scenario into a full scan, and the system becomes too
# slow to run daily -- at which point someone quietly reduces the frequency.
AGGREGATES = {
# Maintain these incrementally, per customer, per window.
"windows": ["1d", "7d", "30d", "90d", "365d"],
"per_window": [
"credit_count", "credit_value", "debit_count", "debit_value",
"cash_credit_value", # CTR aggregation runs off this
"distinct_counterparties",
"distinct_geographies",
"max_single_value",
"round_amount_count", # structuring signal
],
# Store the BASELINE the customer declared, and the OBSERVED baseline.
# A scenario compares against both; they diverge, and the divergence is
# itself the finding.
"baselines": ["declared_at_onboarding", "observed_trailing_180d"],
}
# STEP 3. NAMED SCENARIOS, NOT A SCORE.
# You must be able to tell an examiner WHY an alert fired. A gradient-boosted
# score cannot answer that, and an unexplainable alert is worse than none.
# Start deterministic. Add a model LATER, to RANK the queue, never to raise
# the alert -- ranking makes no decision, so it needs no defence.
SCENARIOS = {
"structuring": "cash credits clustering just under Rs 10 lakh across a "
"rolling calendar month",
"rapid_movement": "credits followed by near-total debits within 24-48h, "
"with little balance retained",
"profile_departure": "value or volume materially above the declared AND "
"observed baseline",
"unexpected_geography": "counterparties outside the declared markets",
"dormant_reactivation": "a long-dormant account resuming at volume",
"round_amounts": "repeated exact round figures, which real trade rarely "
"produces",
}
# STEP 4. TUNE BEFORE LAUNCH, AGAINST HISTORY AND AGAINST YOUR STAFFING.
def tune(scenario, history, analysts_per_day, minutes_per_alert):
capacity = analysts_per_day * 60 * 7.5 / minutes_per_alert
out = []
for t in scenario["threshold_range"]:
alerts = history.replay(scenario, threshold=t)
out.append({
"threshold": t,
"alerts_per_day": alerts.per_day,
# The number that decides it. A threshold generating more alerts
# than you can clear is not a sensitive threshold -- it is an
# unreviewed backlog with a compliance failure inside it.
"within_capacity": alerts.per_day <= capacity,
"known_cases_caught": alerts.recall_against(history.confirmed_cases),
})
return out # record this table. The RATIONALE is the deliverable.
# WHAT TO CHECK
# [ ] aggregates are maintained incrementally, not computed on read
# [ ] both baselines are stored -- what they declared and what they do
# [ ] every scenario has a NAME and a sentence a non-engineer understands
# [ ] no model raises an alert. Ranking only
# [ ] tuning is replayed against real history, not reasoned about
# [ ] the chosen threshold is within the volume you can actually clear
# [ ] the tuning table is SAVED. "Why this threshold" is the most common
# examination question and the most common thing nobody kept
THE second finding, and it is the one that sinks most programmes: the practitioner
complaint about transaction monitoring is not that it misses things. It is that it is
so alert-heavy it generates more noise than signal, and nobody can clear the
queue.
That is a tuning failure with a specific shape: thresholds chosen for sensitivity without
reference to review capacity. A threshold producing four hundred alerts a day, given to a
team that can clear sixty, does not produce more safety. It produces a backlog, and the backlog is
itself the compliance failure — alerts that were raised, never reviewed, and are on the record
as raised.
Note
Tune against two numbers, not one: detection and capacity. And save the table. The most common examination finding in this area is not that thresholds were wrong — it is that nobody could explain how they were chosen. The module covers the sensitivity-analysis methodology; what this page adds is that the capacity column belongs in the same table, because a threshold you cannot staff is not a threshold you have chosen.
Steps 5 to 7 — cases, filing, feedback
Step 5 — alerts become cases, and the timestamp that matters
An alert is a machine output. A case is a human process. The transition is where the regulatory
clock starts, and it is almost always modelled wrongly.
Four distinct moments, and a system that collapses them cannot answer an examiner:
Moment
What it is
Alert raised
The scenario fired. A machine timestamp
Case opened
An analyst picked it up
Suspicion formed
The clock starts here. A human judgement, on a date
Report filed
Within 7 working days of the moment above
Watch out
“Suspicion formed” is a field, and it is a field an analyst sets deliberately. It is not the alert date and not the case-open date. A system that has no such field will, under examination, be read as having formed suspicion at the earliest plausible moment — which is the alert — and every filing will look late. Build the field, require a reason with it, and make it un-editable after the fact.
Python — steps 5 to 7, the four timestamps, the two clocks, the loop
from datetime import date, timedelta
# STEP 5. FOUR TIMESTAMPS. A SYSTEM THAT COLLAPSES THEM CANNOT ANSWER.
CASE_TIMESTAMPS = (
"alert_raised_at", # machine. The scenario fired
"case_opened_at", # an analyst picked it up
"suspicion_formed_on", # HUMAN JUDGEMENT. The 7-working-day clock
"report_filed_at", # the filing
)
def form_suspicion(case, analyst, reason, store):
# Deliberate, attributed, reasoned -- and not editable afterwards.
assert case["suspicion_formed_on"] is None, "already formed; do not backdate"
assert reason and len(reason.split()) >= 10, "a reason, not a checkbox"
store.set_immutable(case["id"], "suspicion_formed_on", today_ist(),
by=analyst, reason=reason)
# Without this field an examiner reads suspicion as forming at the ALERT,
# and every filing you have ever made looks late.
return str_due(today_ist())
def str_due(formed_on, holidays):
# 7 WORKING days. Not 7 calendar days.
d, left = formed_on, 7
while left:
d += timedelta(days=1)
if d.weekday() < 5 and d not in holidays:
left -= 1
return d
# STEP 6. TWO CLOCKS, TWO MECHANISMS. DO NOT BUILD THEM AS ONE.
def ctr_batch(month, ledger):
# A QUERY, not a judgement. Cash above Rs 10 lakh in a calendar month,
# aggregated across RELATED transactions. Due the 15th of the next month.
rows = ledger.cash_credits_by_customer(month) # related, not single
return [r for r in rows if r.total > 1_000_000]
# STEP 7. FEED THE OUTCOME BACK. THE LOOP NOBODY CLOSES.
def close_case(case, outcome, rating, stats):
assert outcome in ("cleared", "filed")
# A cleared case and a filed case should move the rating differently.
rating.apply_outcome(case["customer_id"], outcome)
# And both change what you know about the scenario that raised it.
stats.record(case["scenario"], outcome)
return stats.precision(case["scenario"], window="180d")
# WHAT TO CHECK
# [ ] suspicion_formed_on exists, is set by a named analyst with a reason,
# and cannot be edited after the fact
# [ ] the STR clock counts WORKING days against the actual holiday calendar
# [ ] CTR aggregates RELATED cash transactions across the month, and runs
# automatically. An analyst noticing it is not a control
# [ ] case outcomes feed both the customer rating and scenario precision
# [ ] scenario precision is reported on a rolling window, so drift is
# visible before an examiner finds it
# [ ] every timestamp in IST. A UTC filing date near a month boundary is
# a deadline argument you will lose
Step 6 — filing, and the two clocks
Two different mechanisms, frequently conflated:
STR — Suspicious Transaction Report. No value threshold. Filed within
7 working days of suspicion being formed.
CTR — Cash Transaction Report. Threshold-based: cash above
₹10 lakh in a calendar month, whether in one transaction or several related
ones. Filed by the 15th of the following month.
Both go to FIU-IND through FINnet 2.0. The module covers the full set of five
statutory reports; what matters architecturally is that CTR is an aggregation job and STR is a
workflow. Building them as one thing is why CTR filings get missed: a monthly aggregate over
related cash transactions is a query your core system must run automatically, not something an
analyst notices.
Step 7 — feed the outcome back
The loop nobody closes: a case that was cleared and a case that became a filing should
change the customer's risk rating differently, and both should change the scenario's
precision statistics.
Without that feedback, tuning is a one-off exercise performed before launch against data that is
now old, and the system's precision drifts silently in whichever direction your customer mix moves.
What it costs
AML monitoring systems — what it costs
Verified September 2026
The data modelindirect
Incremental aggregates per customer per window. Engineering, not licensing, and the one component you cannot retrofit cheaply — a monitoring system built on full scans is rebuilt rather than optimised.
The scenario enginedirect
Bought or built. See the build sheet for vendors and unit costs. The engine is rarely the problem; the defaults it ships with usually are.
Analyst capacitydirect
Most of the cost of an AML programme, and the number that should set your thresholds. Alerts per day divided by minutes per alert. Everything upstream should be tuned to it.
Case managementdirect
Bought or built, and the specification is narrow: four distinct timestamps, an immutable suspicion formed field with a reason, and a full evidence trail per case.
Filingdirect
FINnet 2.0 integration. CTR should be automatic — a monthly aggregation over related cash transactions is a query, not a judgement.
Retentiondirect
Five years, and for identification records five years from the end of the relationship rather than from the transaction. Retrievability is the requirement.
Getting it wrongindirect
Regulators consistently find that institutions with adequate technology and inadequate governance fail reviews as often as those with technology gaps. An unreviewed alert backlog is worse than a narrower threshold, because the alerts are on the record as raised.
Where to buy these: Aml Compliance Build Sheet names every tool with its unit cost. Getting Access covers which ones you can sign up for today, which need a sales call, and which are licensed.
Note
The number to compute before choosing any threshold: alerts your team can actually clear per day. Analysts × hours × 60, divided by minutes per alert. Every tuning decision should be checked against it, and a system tuned without it will be either blind or buried — and buried looks like working right up until an examination.
AdvancedShip it. Failure modes, thresholds and evidence.
Three versions you could build
Deterministic, and honest about it
Build: incremental aggregates → four or five named scenarios → thresholds
tuned against history and capacity → a case record with four timestamps → manual filing.
You get: a programme you can explain completely. For a small book this is
not a compromise, it is the right answer — and it is what an examiner would rather see
than a model nobody can account for.
Risk-rated and tuned
Build: the above, plus a customer risk rating recomputed on events → thresholds
that differ by risk band → automatic CTR aggregation → outcome feedback into both the rating
and the scenario statistics.
Trade: real engineering in the data layer, against a queue your team can clear and
a rationale you can produce.
With a model, carefully placed
Build: the above, plus a model that ranks the queue so analysts
see likely true positives first — and never raises an alert.
It breaks when: the model moves upstream into detection. Ranking makes no
decision and needs no defence; raising an alert is a decision and needs one. That distinction
is the whole of where AI belongs here.
Note
If you take one thing from this page: record the moment suspicion was formed, as its own field, set by a named analyst, with a reason, and immutable afterwards. It is the field the 7-working-day clock runs from, it is the first thing an examiner asks for, and almost no case-management implementation has it.
What goes wrong
What goes wrong
Why
Fix
Risk rating set once at onboarding
Stored as a column.
Recompute on events, not only on a calendar.
Monitoring runs on full scans
No aggregate layer.
Incremental aggregates per customer per window.
Daily run quietly becomes weekly
It got too slow.
Fix the data model. The frequency reduction is the symptom.
A score raises alerts
Better recall in testing.
Named scenarios raise. A model ranks.
Alert backlog
Tuned for sensitivity, not capacity.
Capacity belongs in the tuning table.
Nobody can explain a threshold
The analysis was not saved.
The tuning table is the deliverable.
Every filing looks late
No suspicion formed field.
Build it, require a reason, make it immutable.
CTR missed
Treated as an analyst task.
Monthly aggregation over related cash transactions. A query.
Precision drifts
Tuned once, before launch.
Feed case outcomes back into the statistics.
Records kept from the wrong date
Retention keyed to the transaction.
Identification records run from the END of the relationship.
This page is a guide, not a specification, and it deliberately does not restate the obligations — read the AML module for those. Reporting thresholds and timelines are set by FIU-IND and amended by notification. Nothing here is legal advice. Have your scenario set, your thresholds and your filing workflow reviewed by your Principal Officer and by qualified counsel.
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 commentary.
officialPrevention of Money Laundering Act, 2002 and the PML (Maintenance of Records) Rules — section 12 obligations on reporting entities to maintain records of transactions, monitor accounts, and furnish reports; and the record-retention periods of five years, running from the date of the transaction for transaction records and from the end of the customer relationship for identification records. www.indiacode.nic.in
officialFIU-IND — reporting obligations and the FINnet platform — the Suspicious Transaction Report filed within 7 working days of suspicion being formed, with no value threshold; the Cash Transaction Report for cash above ₹10 lakh in a calendar month whether in one transaction or several related ones, filed by the 15th of the following month; and submission through FINnet 2.0. fiuindia.gov.in
officialRBI Master Direction — Know Your Customer, 2016 as amended — the four pillars of customer acceptance policy, customer identification procedures, monitoring of transactions and risk management; the risk-based approach requiring monitoring to differ by customer risk category; and periodic review requirements with material changes triggering review regardless of the scheduled cycle. www.rbi.org.in
officialFIU-IND AML and CFT Guidelines for reporting entities providing services related to Virtual Digital Assets — issued 8 January 2026, bringing virtual digital asset service providers to the same compliance standard as other regulated financial institutions. Relevant if any part of your book touches VDAs. fiuindia.gov.in
officialFATF Recommendations and the APG mutual evaluation framework — the international standard the Indian framework implements. As at February 2026 India was not on any FATF increased-monitoring list. www.fatf-gafi.org
researchFederal Reserve SR 11-7 — Guidance on Model Risk Management — the model validation and governance standard widely used as a reference for monitoring models, and the basis for the position on this page that an unexplainable model should not raise an alert. www.federalreserve.gov
industryPractitioner commentary on ongoing monitoring in India — the recurring finding that onboarding verification is well built while ongoing surveillance is fragmented or so alert-heavy that it generates more noise than signal, and that institutions with adequate technology but inadequate governance fail reviews as often as those with technology gaps. Interpretation and industry reporting, not a regulatory instrument.
industryVendor and certification material on FIU-IND reporting — thresholds, timelines and the five statutory report types as summarised for practitioners and examinations. Directional; confirm every figure against the current FIU-IND guidance before building to it.
Checked September 2026. Thresholds and filing timelines are set by FIU-IND and change by notification — confirm the current figures before building to any of them.
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.