Product Guide 18
Fintech AI

AML Monitoring Systems: How to Build It

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:

LayerWhat it producesUsually
Customer risk ratingA band that changes how everything below behavesA field set at onboarding and never recomputed
Scenario engineAlerts, from named patternsBought, then left at defaults
Case managementA decision, with a reason and a timestampA spreadsheet
ReportingA filing, inside a deadlineManual, 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

#StepIn plain words
1Build the risk rating firstEverything below inherits it.
2Get the data model rightAggregates, not rows. The thing you cannot retrofit.
3Write named scenariosNot a score. Explainability is the requirement.
4Tune before you launchAgainst history, with the volume you can staff.
5Turn alerts into casesAnd record when suspicion formed.
6File on the clock7 working days, 15th of the month, FINnet 2.0.
7Feed it backOutcomes change the rating. The loop nobody closes.
8Keep the recordFive 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:

MomentWhat it is
Alert raisedThe scenario fired. A machine timestamp
Case openedAn analyst picked it up
Suspicion formedThe clock starts here. A human judgement, on a date
Report filedWithin 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 wrongWhyFix
Risk rating set once at onboardingStored as a column.Recompute on events, not only on a calendar.
Monitoring runs on full scansNo aggregate layer.Incremental aggregates per customer per window.
Daily run quietly becomes weeklyIt got too slow.Fix the data model. The frequency reduction is the symptom.
A score raises alertsBetter recall in testing.Named scenarios raise. A model ranks.
Alert backlogTuned for sensitivity, not capacity.Capacity belongs in the tuning table.
Nobody can explain a thresholdThe analysis was not saved.The tuning table is the deliverable.
Every filing looks lateNo suspicion formed field.Build it, require a reason, make it immutable.
CTR missedTreated as an analyst task.Monthly aggregation over related cash transactions. A query.
Precision driftsTuned once, before launch.Feed case outcomes back into the statistics.
Records kept from the wrong dateRetention keyed to the transaction.Identification records run from the END of the relationship.

Where to go next

Watch out

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.

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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.
  8. 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.

Nothing is sent from here. The link carries only this page’s title and address.