KYC / AML / Sanctions Screening Pipeline
You generate a complete screening pipeline for customer onboarding and continuous monitoring. The 2026 regulatory floor: customer identification (CIP), customer due diligence (CDD), enhanced due diligence (EDD) for high-risk, sanctions screening against multiple lists, PEP screening, and transaction monitoring. The dominant cost driver is false positives — sloppy matching buries compliance teams in noise and slows real-money revenue.
============================================================ === PRE-FLIGHT ===
Verify:
- Regulatory regime: US (BSA / FinCEN / OFAC), EU (AMLD6), UK (MLR 2017), Singapore (MAS), or multi-jurisdiction.
- Customer type: retail individuals, small business (KYB), institutions, fintech (regulated by OCC / Fed / state).
- Onboarding volume: < 100/day → in-house tooling viable. > 1k/day → must use specialist vendor (ComplyAdvantage, Dow Jones, LSEG World-Check, dilisense, Sayari, Sanctions.io).
- Risk appetite: tighter thresholds = fewer false negatives but more analyst review. Calibrate per regulator expectations.
- Existing stack: identity verification vendor (Persona, Plaid Identity, Veriff, Onfido, Jumio) feeds this pipeline; if absent, route there first.
Recovery:
- For sub-100/day startups, generate the in-house pipeline with OpenSanctions (free, comprehensive, daily updates) as primary data source.
- For higher volume, generate adapter scaffolds for ComplyAdvantage + Dow Jones APIs.
============================================================ === PHASE 1: LIST INGESTION + REFRESH ===
Source the watchlists. Frequency: nightly refresh for sanctions (lists update daily), weekly for PEP.
| List | Source | Format | Frequency |
|---|---|---|---|
| OFAC SDN | sanctionssearch.ofac.treas.gov + Treasury data files | XML/CSV/JSON | Daily |
| OFAC Consolidated (non-SDN) | OFAC | XML/CSV | Daily |
| EU Consolidated Sanctions | data.europa.eu | XML | Daily |
| UK HMT Sanctions | gov.uk OFSI | CSV | Daily |
| UN Security Council Consolidated | scsanctions.un.org | XML | Daily |
| Australian DFAT | dfat.gov.au | XLS | Weekly |
| Canada (OSFI) | osfi-bsif.gc.ca | XML | Daily |
| OpenSanctions (aggregator) | opensanctions.org | JSON / Statements | Daily |
| PEP | Dow Jones Risk & Compliance or OpenSanctions PEP | Various | Weekly |
Generate list_refresh.py that pulls each list, normalizes to a common schema, and diffs against the previous version. Newly listed entities trigger an alert against your customer base.
VALIDATION: After refresh, list count is plausible (SDN ~16k entries in 2026). Diff produces both additions and removals.
============================================================ === PHASE 2: CANONICAL ENTITY SCHEMA ===
Normalize every list entry into:
{
"list_id": "OFAC-SDN-12345",
"source_list": "OFAC_SDN",
"first_listed_date": "2024-03-15",
"last_updated": "2026-02-08",
"entity_type": "individual" | "vessel" | "aircraft" | "entity",
"primary_name": "Smith, John",
"alt_names": ["Johnny Smith", "Иван Смит", "...transliterations..."],
"dob": ["1965-06-12"],
"place_of_birth": "Damascus, Syria",
"nationalities": ["SY", "LB"],
"addresses": [...],
"identifications": [{"type": "passport", "country": "SY", "number": "..."}],
"linked_entities": [...],
"programs": ["SDGT", "SYRIA"],
"remarks": "..."
}
Same schema applies to PEP entries (with positions, parties, etc.) and adverse media (with article references).
VALIDATION: Schema validates with strict types. Cross-list dedup catches the same entity present on multiple lists.
============================================================ === PHASE 3: NAME MATCHING ENGINE ===
This is the highest-leverage component. Match quality = (recall) × (1 / false positives).
Layered approach:
- Exact + alias (deterministic): full match against primary_name and any alt_name.
- Phonetic (Soundex, Metaphone, BMPM): handles transliteration variants (Mohammed/Muhammad/Mohamed).
- Edit distance (Damerau-Levenshtein): handles typos. Threshold: ≥ 0.85 similarity.
- Token-based (Jaccard, Jaro-Winkler): handles word reorderings ("John Smith" vs "Smith, John"). JW threshold ≥ 0.92.
- Substring + n-gram: catches partial matches in long entity names.
- Cross-script: transliterate Cyrillic/Arabic/Greek/Chinese to Latin before comparing.
For each candidate match, compute a match score:
match_score = w_name * name_sim
+ w_dob * dob_match
+ w_country * country_match
+ w_id * id_match
- w_disambig * disambiguation_signal
Default weights: name 0.5, dob 0.2, country 0.15, id 0.15. Tune per false-positive rate.
Filter cascade:
- Score < 0.6 → drop (likely noise).
- 0.6-0.79 → secondary review queue.
- 0.80-0.95 → analyst review.
0.95 → block + analyst review.
VALIDATION: Benchmark against the FuzzyWuzzy test corpus (sanction-name pairs with known matches/non-matches). Precision ≥ 95% and recall ≥ 90%.
============================================================ === PHASE 4: ONBOARDING SCREENING API ===
Generate screen.py exposing:
def screen_customer(customer: CustomerProfile) -> ScreeningResult:
"""
Returns ScreeningResult with:
- status: CLEAR | HOLD | ESCALATE | BLOCK
- matches: list of MatchCandidate with scores + list_id refs
- pep_matches, sanctions_matches, adverse_media_matches
- risk_score: 0-100
- explainer: human-readable reasoning
"""
Key behavior:
- Hard block on sanctions match > 0.95 (compliance failure to block = personal liability for the MLRO).
- Hold on PEP match — most jurisdictions require EDD before opening, not a hard block.
- Risk score combines screening hits + geography (high-risk country FATF lists) + transaction type + occupation.
Auditability: every screening decision persists who, when, what, why in tamper-evident storage (append-only, S3 Object Lock or equivalent).
VALIDATION: Round-trip an onboarding with a known-OFAC-listed test name; system blocks and records the decision.
============================================================ === PHASE 5: ONGOING MONITORING + WATCHLIST DIFF ===
After a customer is onboarded, screening doesn't stop. Generate:
- Nightly rescreen of every active customer against the latest lists.
- Watchlist diff alert: when a name is newly added that fuzzy-matches an existing customer, raise a HIGH priority case.
- Address / occupation / nationality change triggers re-screen.
- Transaction-trigger rescreen: any tx ≥ threshold (e.g., $10k cash, $3k wire) auto-screens counterparty.
Generate monitor_cron.py + alert dispatcher (Slack, PagerDuty, Jira).
VALIDATION: Test by adding a sanctioned name to the diff list and confirming alert fires for any pre-onboarded customer matching that name.
============================================================ === PHASE 6: CASE MANAGEMENT UI SCAFFOLD ===
Compliance analysts need to disposition cases. Generate a minimal UI (Next.js app router or Streamlit) with:
- Case queue (priority sorted)
- Customer profile + screening hits side-by-side
- Disposition: CLEAR (false positive), ESCALATE (to MLRO), FILE SAR (suspicious activity), BLOCK
- Required fields per disposition (justification, supporting docs)
- Two-eyes review for SAR / BLOCK actions
- Audit trail (who clicked what when)
VALIDATION: Audit trail captures every state change with user, timestamp, IP.
============================================================ === PHASE 7: SAR (FORM 111) HELPER ===
When a case escalates to SAR, generate a draft of FinCEN Form 111 (CTR is Form 112):
- Filing institution info (pre-filled from org config)
- Subject info (from customer record)
- Suspicious activity type codes (FinCEN list — terrorism financing, structuring, money laundering, etc.)
- Activity dates and dollar amounts
- Narrative (5W+H — who, what, when, where, why, how) with the screening evidence summarized
Compliance officer reviews + files via BSA E-Filing System. Skill does NOT auto-file — narrative drafting only.
VALIDATION: Narrative covers all 5W+H. Suspicious activity codes correctly selected from FinCEN's controlled vocabulary.
============================================================ === SELF-REVIEW ===
Score 1–5:
- Complete: All 7 phases delivered? Lists ingest? Matching tunable? UI scaffolded?
- Robust: Matching handles transliteration + aliases + typos? False positive rate measurable?
- Clean: Decisions auditable, append-only? Auto-block ONLY on confirmed sanctions match (not PEP)?
- Compliance-credible: Would an MLRO at a regulated fintech (bank, broker-dealer, crypto exchange) sign off?
Common gap: blocking PEPs by default instead of EDD-ing them. PEP ≠ sanctioned; flag for review, don't auto-block.
============================================================ === LEARNINGS CAPTURE ===
Append to ~/.claude/skills/kyc-aml-screener/LEARNINGS.md:
— <regime, volume, customer type>
- What worked:
- What was awkward:
- Suggested patch:
- Verdict: [Smooth / Minor friction / Major friction]
============================================================ === STRICT RULES ===
- Never auto-block on PEP match alone. PEPs are legal customers requiring EDD, not prohibited.
- Never silently drop a sanctions match. False negative = compliance violation = personal liability + bank-wide fines.
- Never store screening results without an audit trail. Regulators reconstruct the decision chain in exams.
- Never compute match score without dob/country disambiguation when available. Name alone produces too many false positives.
- Never deploy without nightly list refresh. Stale OFAC = the day after a designation lands you're exposed.
- Always document the matching threshold rationale. "Why 0.85?" comes up in every BSA exam.