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: AML Build Sheet →
What AML actually asks you to do
Anti-money laundering is not fraud detection. Fraud asks "is someone stealing from us". AML asks "is our business being used to move criminal money", and the obligation exists whether or not you lose a rupee.
Four duties, and they are separate systems:
- Know who your customer is — covered in Module 01.
- Screen them against lists — sanctions, politically exposed persons, adverse media.
- Monitor their transactions for patterns that suggest laundering.
- Report what you find to the financial intelligence unit, on time, in the prescribed format.
The fourth duty is the one that carries personal liability. In India the Principal Officer is a named individual with statutory responsibility for filing. This is not a system that can be fully automated away, and it should not be designed as though it can.
Why sanctions screening is harder than a database lookup
The naive picture is a list of bad names and a lookup. The reality is that names are not identifiers.
A single Russian name may legitimately appear as Aleksandr, Alexander or Aleksander depending on which transliteration standard was used. Arabic and Chinese names have the same problem, worse. Add patronymics that are sometimes included and sometimes not, name order that varies by culture, and sanctions list entries that are frequently missing a date of birth or any national identifier at all.
So screening is probabilistic matching, not lookup. And the moment it becomes probabilistic you inherit a trade-off you cannot escape.
The precision paradox
Loosen your matching and you catch more real hits and drown in false ones. Tighten it and the alert volume becomes manageable while genuine matches slip through.
There is no threshold that gives you both. Industry analysis calls this the precision paradox, and it is the central operational fact of AML screening.
The consequence: most institutions configure conservatively, generate enormous alert volumes, and employ large teams to clear them. False positive rates in the low single-digit percentages are normal, and pushing much below that starts producing false negatives that are unacceptable in higher-risk jurisdictions.
Beware anyone selling near-zero false positives. In screening, an unusually low false positive rate is not a sign of a better system — it is usually a sign of thresholds tuned tight enough to miss real matches. That is the failure that ends in an enforcement action.
The case that shows what failure looks like
In November 2025 the UK sanctions authority fined Bank of Scotland £160,000. The details are worth knowing precisely, because nothing about it was exotic.
A UK-designated Russian individual opened an account at Halifax using a UK passport. The name on the passport differed from the sanctions list entry in four small ways: one changed character in the forename, one additional character in the forename, a missing middle name, and one changed character in the surname. Every one of those was a common Russian-to-English transliteration equivalent.
The individual was on the list. The screening system was configured correctly and operated as configured. No alert was generated at account opening, or at any point until manual identification eighteen days later.
Two systemic failures were identified: the screening system could not reconcile the character changes, and the bank had not enhanced its sanctions list with a commercial data provider — despite having done exactly that for its PEP screening, which did generate an alert.
Read that last sentence again. The same institution, the same customer, the same week. The PEP screening alerted because it used enriched data. The sanctions screening did not because it did not. The difference was a data decision, not an algorithm decision.
The layered matching cascade
No single algorithm solves name matching. The working approach runs several in sequence, each catching what the previous one misses.
NAME TO SCREEN
|
0. NORMALISE case, punctuation, honorifics, whitespace,
| Unicode NFKC, common prefix handling
| (bin/ibn, van/von, al-, O'/Mc)
|
1. EXACT normalised string equality
| -> cheap, catches the obvious, misses everything real
|
2. FUZZY edit distance (Levenshtein / Jaro-Winkler),
| token-set comparison for reordered names
| -> catches typos and minor transliteration drift
|
3. PHONETIC Double Metaphone / Soundex / NYSIIS
| -> catches "sounds the same, spelled differently",
| which is most of the transliteration problem
|
4. EMBEDDING vector similarity over name embeddings
| -> catches alias and semantic relationships the
| character-level methods cannot see
|
5. CONTEXT SCORING DOB, nationality, address, ID number
| -> this is what converts a name hit into a
| decision. Without it you are guessing.
|
6. LLM ENRICHMENT ONLY after a deterministic method has flagged.
| Summarise, explain, draft rationale.
| NEVER as the primary screen.
|
ALERT or CLEAR (with reasoning recorded either way)Note where the LLM sits: at step six, after a deterministic method has already flagged. That placement is deliberate and covered below.
import re, unicodedata
from jellyfish import jaro_winkler_similarity, metaphone
HONORIFICS = {"mr","mrs","ms","dr","prof","shri","smt","sri","md","mohd"}
PARTICLES = {"bin","ibn","al","el","van","von","de","da","del","der","du"}
def normalise(name: str) -> str:
"""Deterministic, versioned, and identical in training and production.
Every change to this function changes your screening results - so it
is versioned and the version is logged with every decision."""
s = unicodedata.normalize("NFKC", name).lower()
s = re.sub(r"[^a-z\s]", " ", s)
tokens = [t for t in s.split() if t and t not in HONORIFICS]
return " ".join(tokens)
def name_scores(query: str, candidate: str) -> dict:
q, c = normalise(query), normalise(candidate)
qt, ct = set(q.split()), set(c.split())
exact = 1.0 if q == c else 0.0
# Token-set: handles reordering and missing middle names, which is
# exactly the Bank of Scotland failure mode.
core_q = qt - PARTICLES
core_c = ct - PARTICLES
overlap = (len(core_q & core_c) / max(1, min(len(core_q), len(core_c))))
fuzzy = jaro_winkler_similarity(q, c)
# Phonetic on each token pair - catches transliteration directly
ph_q = {metaphone(t) for t in core_q}
ph_c = {metaphone(t) for t in core_c}
phonetic = len(ph_q & ph_c) / max(1, min(len(ph_q), len(ph_c)))
return {"exact": exact, "token_overlap": overlap,
"fuzzy": fuzzy, "phonetic": phonetic}
def context_score(subject: dict, listed: dict) -> dict:
"""Context is what turns a name hit into a decision.
Missing data must NOT count as a mismatch - sanctions entries are
routinely incomplete, and treating absence as disconfirmation is
how real matches get discarded."""
out, checked = {}, 0
if subject.get("dob") and listed.get("dob"):
checked += 1
out["dob_match"] = subject["dob"] == listed["dob"]
out["dob_year_match"] = subject["dob"][:4] == listed["dob"][:4]
if subject.get("nationality") and listed.get("nationality"):
checked += 1
out["nationality_match"] = (subject["nationality"].upper()
== listed["nationality"].upper())
if subject.get("national_id") and listed.get("national_id"):
checked += 1
out["id_match"] = subject["national_id"] == listed["national_id"]
out["context_fields_checked"] = checked
out["context_available"] = checked > 0
return out
def screen(subject, listed_entry, thresholds):
ns = name_scores(subject["name"], listed_entry["name"])
cs = context_score(subject, listed_entry)
name_hit = (ns["exact"] == 1.0
or ns["fuzzy"] >= thresholds["fuzzy"]
or (ns["phonetic"] >= thresholds["phonetic"]
and ns["token_overlap"] >= thresholds["token_overlap"]))
if not name_hit:
return {"alert": False, "scores": ns}
# A name hit WITH contradicting hard context can be auto-discounted.
# A name hit with NO context available must go to a human.
if cs.get("id_match") is False:
return {"alert": True, "priority": "low",
"reason": "name_hit_id_mismatch", "scores": ns, "context": cs}
if cs.get("dob_match") is False and cs.get("dob_year_match") is False:
return {"alert": True, "priority": "medium",
"reason": "name_hit_dob_mismatch", "scores": ns, "context": cs}
return {"alert": True,
"priority": "high" if cs.get("id_match") or cs.get("dob_match") else "medium",
"reason": "name_and_context_consistent" if cs["context_available"]
else "name_hit_no_context_available",
"scores": ns, "context": cs}The rule that prevents the Bank of Scotland failure
Look at the context_score function. Missing data does not count as a mismatch.
Sanctions list entries are routinely incomplete — no date of birth, no national identifier, sometimes only a name and a country. If your logic treats "no DOB on the list entry" as evidence against a match, you will discard genuine hits systematically. Absence of data is not disconfirmation.
Where LLMs help, and where they must not be used
The research here is genuinely encouraging and the deployment advice is genuinely restrictive. Both matter.
What the evidence shows
A comparison of several large language model families against standard fuzzy matching algorithms across realistic thresholds found the language model approach reduced false positives by around 92% and increased detection rates by about 11% relative to the best fuzzy baseline — at meaningfully higher computational cost. Separate Federal Reserve research reached a consistent conclusion, that LLM-assisted screening outperforms pure fuzzy matching at distinguishing true from false positives at volume.
That is a large gap. It is worth taking seriously.
Why you still do not put it in the primary screen
Two reasons, and neither is about capability.
Determinism. A screening decision must be reproducible. The same customer screened against the same list version must produce the same result, today and in three years when an examiner asks. Generative models are not deterministic in the way a regulator means by the word.
Auditability. You must be able to state why a name cleared. "The model judged it not a match" is not a compliance explanation. A fuzzy score of 0.81 against a documented threshold of 0.85, with the threshold supported by sensitivity analysis, is.
The practical rule: deterministic methods decide whether an alert is raised. LLMs work on alerts that already exist — triaging, summarising adverse media, drafting rationale, explaining a match to an analyst. Never let a generative model be the thing that decides a name does not need screening.
Where they earn their place
- Alert triage — ranking a queue so analysts see the likeliest true positives first.
- Adverse media review — reading and summarising hundreds of articles about a common name, which is otherwise pure analyst time.
- Match rationale drafting — producing a first draft of why this alert was cleared or escalated, for an analyst to check and sign.
- SAR narrative drafting — see the Advanced lane, with the same human-signs-it constraint.
Transaction monitoring
Screening looks at who. Monitoring looks at what they do. The classic typologies are well known and still carry most of the detection load.
| Typology | Pattern | Detection approach |
|---|---|---|
| Structuring | Many transactions just under a reporting threshold | Aggregate over rolling windows, not per transaction. Watch for amounts clustering just below a round threshold. |
| Layering | Rapid movement through multiple accounts | Graph traversal — see Module 03. Hop count and hold time. |
| Rapid pass-through | Funds in and out within minutes, near-full amount | Hold-time distribution per account. |
| Round-tripping | Money returns to origin through intermediaries | Cycle detection in the transaction graph. |
| Unusual for profile | Activity inconsistent with declared occupation or income | Peer-group baselining, not absolute thresholds. |
| High-risk geography | Exposure to jurisdictions on FATF or internal lists | Corridor rules plus volume monitoring. |
| Trade-based laundering | Over- or under-invoicing against goods | Genuinely hard. Requires trade data you usually do not have. |
Rules still do most of the work in production AML monitoring, and that is not a failure of imagination. A rule is explainable, testable, and can be defended to an examiner line by line. Models add lift on top by reducing false positives within alerts the rules generate — not usually by replacing the rules.
India: what you must actually file
Under the Prevention of Money Laundering Act 2002, a reporting entity must register with FIU-IND, appoint two named officers, maintain records for five years, and file prescribed reports electronically through the FINnet 2.0 portal.
The five reports
| Report | Trigger | Timing |
|---|---|---|
| CTR — Cash Transaction Report | Cash transactions above ₹10 lakh, single or aggregated in a month | Monthly, typically by the 15th of the following month |
| STR — Suspicious Transaction Report | No monetary threshold. Any transaction giving rise to reasonable suspicion, attempted or completed | Within 7 working days of forming the suspicion. Delays counted per day. |
| CCR — Counterfeit Currency Report | Forged or counterfeit notes | Monthly |
| NTR — Non-profit Transaction Report | Receipts by non-profit organisations above the prescribed threshold | Monthly |
| CBWTR — Cross-Border Wire Transfer Report | Cross-border wire transfers above the prescribed threshold | Monthly |
Two officers, two people
The Principal Officer is operationally responsible for filing and is the liaison with FIU-IND. The Designated Director carries board-level accountability for PMLA compliance overall.
Mapping both roles to the same individual is a common and avoidable mistake. They are distinct positions under the framework. For some categories of reporting entity, FIU-IND guidance also sets minimum experience expectations for the Principal Officer.
Tipping off
You must not tell the customer that a suspicious transaction report has been filed, or that they are under scrutiny. This is an offence, and it constrains product design directly.
Practical consequences: no status that a customer service agent can read out. No automated notification tied to an STR. No support macro that says "your account is under review for compliance reasons". Agents need a script that is truthful and uninformative, and the system must not surface the reason to them in the first place.
The registry
Sanctions and watchlist data
Verified May 2026Screening and monitoring platforms
Verified May 2026Build-your-own components
Verified May 2026Registry reflects what was publicly visible in May 2026. Sanctions lists change continuously — screening against a stale list is itself a finding. Automate list refresh and log the list version with every screening decision.
A prompt for designing your screening configuration
You are an AML compliance officer who has been through regulatory
examinations on sanctions screening.
My situation:
- Business: [e.g. NBFC lending / payments / VDA exchange / neobank]
- Jurisdiction(s): [e.g. India, plus customers in UAE and Singapore]
- Customer volume: [new customers per month]
- Customer name profile: [e.g. predominantly Indian names, some Arabic,
some Cyrillic transliterations]
- Current screening: [describe, or "none"]
Give me:
1. Which lists I must screen against, and which I should screen
against beyond the minimum.
2. A matching cascade design - which algorithms in which order, with
starting thresholds and the rationale for each.
3. How to run a documented threshold sensitivity analysis so the
thresholds have a defensible basis rather than being defaults.
4. Which name populations in my customer base will generate the most
false positives, and what to do about it WITHOUT lowering recall.
5. My reporting obligations, the deadlines, and who must be appointed.
6. What an examiner will ask to see, and what I should be keeping
from day one to answer it.
7. Three ways this configuration could fail an examination even while
operating exactly as designed.
Be specific. Where regulation should be verified with a qualified
adviser, say so rather than stating it as settled.Threshold setting is a documented exercise, not a default
The most common examination finding in screening is not that thresholds were wrong. It is that nobody could explain how they were chosen.
Regulatory expectation is a documented threshold sensitivity analysis: test detection rates and alert volumes across a range of settings, and select thresholds with a quantitative rationale recorded at the time.
import numpy as np
def sensitivity_analysis(test_pairs, threshold_grid):
"""test_pairs: list of (subject, listed_entry, is_true_match)
Build this set deliberately - it is the most valuable compliance
artefact you will produce.
It must include:
- known true matches with exact names
- known true matches with transliteration variants
- known true matches with a missing middle name
- known true matches with one changed character
- high-similarity NON-matches (common names)
- your own customer base sampled realistically"""
rows = []
for t in threshold_grid:
tp = fp = fn = tn = 0
for subject, listed, truth in test_pairs:
hit = screen(subject, listed, t)["alert"]
if hit and truth: tp += 1
elif hit and not truth: fp += 1
elif not hit and truth: fn += 1
else: tn += 1
rows.append({
"thresholds": t,
"recall": tp / max(1, tp + fn), # % of real matches caught
"precision": tp / max(1, tp + fp),
"alerts_per_1000": 1000 * (tp + fp) / max(1, len(test_pairs)),
"missed_matches": fn, # the number that ends careers
})
return rows
# HOW TO CHOOSE:
# 1. Set a minimum acceptable recall FIRST, as a policy decision
# signed off by compliance - not an engineering optimisation.
# 2. Among configurations meeting that recall, pick the one with the
# lowest alert volume.
# 3. Record the analysis, the date, the test set version, the decision
# and who approved it.
# 4. Re-run whenever the list provider, the matching code, the
# normalisation function, or the customer mix changes.Build the manipulation test set deliberately: transliteration variants, character substitutions, missing name elements, reordered tokens. Industry benchmarking against exactly these structured variants is how the gaps in a matching engine become visible. Your test set is the thing that would have caught the Bank of Scotland failure before an examiner did.
Alert adjudication and the analyst queue
Screening produces alerts. Alerts need clearing. That clearing is most of the cost of an AML programme, and it is where AI earns its place.
What to automate and what not to
| Step | Automate? | Why |
|---|---|---|
| Raising the alert | Deterministic only | Must be reproducible and explainable |
| Ranking the queue | Yes | Analysts see likeliest true positives first; no decision is made |
| Gathering context | Yes | Pulling KYC record, transaction history, prior alerts — pure time saving |
| Summarising adverse media | Yes, with sources | Reading 200 articles about a common name is not a judgement task |
| Drafting the rationale | Draft only | Analyst verifies and signs |
| Clearing the alert | No | This is the decision. A human owns it. |
| Deciding to file an STR | No | Named statutory responsibility |
The one exception worth considering
Deterministic auto-discounting of alerts with a hard contradiction — a national ID that does not match, a date of birth decades apart — is defensible, because the rule is explicit, testable and reproducible.
Even then: log it as a cleared alert with the specific rule that cleared it, sample a percentage for human review, and be able to produce the list on demand. An auto-clear you cannot enumerate is an auto-clear you cannot defend.
SAR narratives with AI assistance
Writing a suspicious transaction report narrative is slow, formulaic and high-stakes. It is a good fit for drafting assistance and a terrible fit for automation.
You are assisting a compliance analyst drafting a Suspicious
Transaction Report narrative. You are producing a FIRST DRAFT for
the analyst to verify, edit and sign. You are not deciding whether
to file.
RULES - these are absolute:
- Use ONLY facts present in the case data below. Never infer,
never fill gaps, never add plausible detail.
- If a field is missing, write "not available in case record".
Do not estimate.
- State observations, not conclusions about guilt.
Write "funds were transferred within 4 minutes of receipt",
not "the customer was laundering money".
- Do not speculate about predicate offences.
- Use neutral, factual language throughout.
STRUCTURE (keep these headings):
1. Subject - who, account details, relationship length, KYC status
2. Activity - what was observed, with dates, amounts, counterparties
3. Why it is suspicious - which typology, which specific facts
support it, which internal alert triggered
4. Supporting evidence - what is attached and what was checked
5. Action taken - what the institution has done so far
CASE DATA:
---
{case_data}
---
After the draft, list separately:
- "GAPS": any fact an examiner would expect that is absent here
- "VERIFY": any statement the analyst must independently confirm
before signingThree constraints in that prompt do the real work. Only facts present in the case data — an invented detail in a regulatory filing is a serious problem. Observations, not conclusions — the report says what happened, it does not allege a crime. And the GAPS and VERIFY sections, which turn the model from something that produces confident text into something that flags its own weaknesses.
The analyst signs the filing and the Principal Officer is accountable for it. A drafting tool that makes it easy to sign without reading is worse than no tool at all. Design the interface so the draft must be actively edited, and log the difference between the generated draft and the filed version.
What examiners actually look for
Patterns that recur across enforcement actions in this area:
- Threshold rationale. Not what the thresholds are — how they were chosen, by whom, on what evidence, and when last reviewed.
- List quality decisions. Whether you enriched your list data, and if not, why not. The Bank of Scotland case turned on precisely this.
- Consistency across programmes. Applying enriched data to PEP screening and not to sanctions screening is the kind of inconsistency that reads badly and is hard to explain.
- Alert backlogs. A queue growing faster than it is cleared means real matches are sitting unreviewed.
- Filing timeliness. In India the STR clock is seven working days from forming suspicion, and delay is counted per day.
- Whether the system was tested. Not whether it works — whether you can show you tested it against known variants and recorded the result.
- Change control. Every change to matching logic, normalisation or thresholds should be versioned, approved and dated.
Notice how much of that is documentation rather than technology. A competent system with thorough records survives examination better than an excellent system with none. Build the evidence trail as you build the system, because reconstructing it afterwards is both expensive and visibly reconstructed.
The audit record, and where this module ends
{
"screening_id": "scr_01HY7...",
"subject_ref": "cust_5521",
"timestamp": "2026-05-23T06:41:02Z",
"trigger": "onboarding",
"configuration": {
"list_sources": ["OFAC_SDN", "UN_CONSOLIDATED", "EU", "UK_OFSI",
"COMMERCIAL_ENRICHED"],
"list_versions": {"OFAC_SDN": "2026-05-22", "UN_CONSOLIDATED": "2026-05-19",
"COMMERCIAL_ENRICHED": "2026-05-23T00:00Z"},
"normalisation_version": "norm-v4",
"matching_config_version": "match-cfg-v12",
"thresholds": {"fuzzy": 0.86, "phonetic": 0.75, "token_overlap": 0.67},
"threshold_approved_by": "compliance_committee_2026-03-11"
},
"results": {
"entries_compared": 118442,
"alerts_raised": 1,
"alerts": [{
"list": "OFAC_SDN", "entry_id": "SDN-44219",
"scores": {"exact": 0.0, "fuzzy": 0.89, "phonetic": 1.0,
"token_overlap": 0.67},
"context": {"dob_match": false, "dob_year_match": false,
"nationality_match": true, "context_fields_checked": 2},
"priority": "medium", "reason": "name_hit_dob_mismatch"
}]
},
"adjudication": {
"assigned_to": "analyst_31", "assigned_at": "...",
"decision": "false_positive",
"rationale": "DOB differs by 14 years; nationality match is coincidental
for a common name; passport number does not correspond to
any listed identifier.",
"ai_assisted": true,
"ai_draft_edited": true,
"decided_at": "2026-05-23T09:12:44Z",
"reviewed_by": "supervisor_07"
},
"reporting": {"str_filed": false, "str_reference": null}
}The list_versions block is the field most often missing and most often asked for. "Was this customer screened against the list as it stood that day" is answerable only if you recorded which version you used.
Where this module ends
- Identity verification is Module 01. AML screening assumes you know who the customer claims to be.
- Fraud detection is Module 03. Related but distinct: fraud protects you from loss, AML protects the system from misuse. A mule account is both, and the two teams need a shared view of it.
- Payment rail specifics and cross-border wire mechanics are Payments & Reconciliation.
- Model governance for any model in this path is Governance — and AML models attract particular validation scrutiny.
- Jurisdictional obligations sit in the India and global regulatory spine pages.
Illustrative throughout. AML is an area where getting it wrong carries personal liability for named officers and criminal exposure for institutions. Nothing here substitutes for a qualified compliance function and legal advice in your jurisdiction.
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.
- officialPMLA, 2002 and the PML Rules — reporting-entity obligations, record retention and the s.13 penalty range. fiuindia.gov.in
- officialUAPA, 1967 s.51A and the Order of 2 Feb 2021 — the designated-list freeze procedure and the 24-hour reporting duty. www.mha.gov.in
- officialRBI Master Direction on KYC — Sections 51, 52 and 54 — UAPA, WMD Act and nodal officers. www.rbi.org.in
- officialUN Security Council Consolidated List — the designations India implements through s.51A. www.un.org
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.