Fintech: AML Transaction Screening

Multi-guard consensus screening for international wire transfers. Five personas evaluate a suspicious $340K wire before it clears, preventing a potential FinCEN enforcement action.

Scenario

A neobank's AI agent processes international wire transfers. Every transaction over $10,000 triggers a multi-guard AML screening before the wire executes. The guards evaluate the transaction against fraud patterns, Bank Secrecy Act requirements, OFAC sanctions, risk scoring models, and operational feasibility.

Regulatory context

Under the Bank Secrecy Act and 31 CFR 1020.320, financial institutions must file a Suspicious Activity Report (SAR) with FinCEN within 30 days of detecting suspicious activity. Failure to maintain adequate AML controls can result in civil money penalties, consent orders, and criminal prosecution of compliance officers. In 2025 alone, FinCEN assessed over $1.3B in penalties against institutions with deficient AML programs.


The transaction

FieldValueRisk signal
SenderTechCorp Ltd (business account)2-year customer, clean history
Amount$340,0004x the sender's historical max ($85K)
RecipientMeridian Holdings LtdShell company, registered 3 months ago
Recipient jurisdictionCyprusHigh-risk jurisdiction (EU AML Directive Annex)
Timing2:47 AM local timeOutside normal hours (sender operates 9 AM - 6 PM)
Recipient bankBank of CyprusNo prior relationship with sender's institution
Stated purpose"Consulting services"Vague, inconsistent with sender's business profile

Guard personas

PersonaRoleFocus
fraud-analystBehavioral anomaly detectionVelocity, timing, amount patterns
bsa-aml-reviewerBSA/AML complianceJurisdiction risk, structuring, SAR triggers
sanctions-screenerOFAC/sanctions enforcementSDN list, jurisdiction screening, PEP checks
transaction-risk-analystQuantitative risk scoringPattern deviation, recipient risk, threshold analysis
operations-engineerSettlement feasibilityCorrespondent banks, cut-off times, settlement risk

Full working example

1

Set up the project

mkdir aml-guard && cd aml-guard
pnpm init -y
pnpm add @consensus-tools/core @consensus-tools/policies
2

Create the AML screening guard

Create aml-screening-guard.ts:

import { LocalBoard } from "@consensus-tools/core";

// --- Initialize the board ---
const board = new LocalBoard({
  mode: "local",
  local: {
    storage: { kind: "json", path: "./aml-screening-state.json" },
    jobDefaults: {
      reward: 25,
      stakeRequired: 8,
      maxParticipants: 7,
      expiresSeconds: 300, // 5-minute SLA for wire screening
      consensusPolicy: { type: "APPROVAL_VOTE", quorum: 5, threshold: 0.6 },
    },
  },
});
await board.init();

// --- The transaction under review ---
const transaction = {
  wireId: "WIRE-2026-0319-047291",
  sender: {
    accountId: "BIZ-TC-20240115",
    name: "TechCorp Ltd",
    accountAge: "2 years",
    historicalMax: 85_000,
    normalHours: "09:00-18:00 EST",
    priorSARs: 0,
    businessType: "Software consulting",
  },
  amount: 340_000,
  currency: "USD",
  recipient: {
    name: "Meridian Holdings Ltd",
    jurisdiction: "CY", // Cyprus
    bankName: "Bank of Cyprus",
    accountAge: "3 months",
    priorRelationship: false,
    companyType: "shell_company",
    registrationDate: "2025-12-02",
  },
  timing: {
    submitted: "2026-03-19T02:47:00-05:00",
    localTime: "02:47 AM EST",
    outsideNormalHours: true,
  },
  statedPurpose: "Consulting services",
  correspondentBank: "Deutsche Bank AG (Frankfurt)",
};

console.log("AML SCREENING: Wire Transfer " + transaction.wireId);
console.log("Sender:    " + transaction.sender.name + " (" + transaction.sender.accountId + ")");
console.log("Amount:    $" + transaction.amount.toLocaleString() + " " + transaction.currency);
console.log("Recipient: " + transaction.recipient.name + " (" + transaction.recipient.jurisdiction + ")");
console.log("Timing:    " + transaction.timing.localTime);
console.log("Purpose:   " + transaction.statedPurpose);
console.log("");

// --- Post the screening job ---
const job = await board.engine.postJob("aml-screening-system", {
  title: `AML screen: ${transaction.wireId} — $${transaction.amount.toLocaleString()} to ${transaction.recipient.jurisdiction}`,
  reward: 25,
  stakeRequired: 8,
});

console.log("Screening job posted: " + job.id);
console.log("5 guards evaluating...");
console.log("");

// --- 5 specialist guards evaluate independently ---
const guards = [
  {
    id: "fraud-analyst",
    verdict: "BLOCK",
    confidence: 0.93,
    riskScore: 87,
    findings: {
      category: "BEHAVIORAL_ANOMALY",
      flags: [
        "Amount is 4x historical maximum ($340K vs $85K ceiling)",
        "Transaction submitted at 02:47 AM — sender's normal hours are 09:00-18:00",
        "Velocity spike: no wire activity in prior 45 days, then a single large transfer",
        "Amount just below $350K threshold that triggers enhanced due diligence at correspondent",
      ],
      sarIndicators: ["Rapid movement of funds", "Unusual transaction size", "Off-hours activity"],
      detail: "The combination of 4x velocity spike, off-hours timing, and dormant-then-active pattern is consistent with account takeover or authorized-push-payment fraud. The amount appears calibrated to stay below the correspondent bank's enhanced review threshold.",
    },
  },
  {
    id: "bsa-aml-reviewer",
    verdict: "BLOCK",
    confidence: 0.96,
    riskScore: 94,
    findings: {
      category: "BSA_AML_VIOLATION",
      flags: [
        "Recipient is a shell company in a high-risk jurisdiction (Cyprus)",
        "Recipient entity registered only 3 months ago — minimal operating history",
        "Stated purpose ('consulting services') is vague and inconsistent with sender's profile",
        "No prior business relationship between sender and recipient",
        "Transaction meets multiple SAR filing criteria under 31 CFR 1020.320",
      ],
      sarIndicators: [
        "Funds transfer to high-risk jurisdiction",
        "Shell company recipient",
        "No apparent business purpose",
        "Transaction inconsistent with customer profile",
      ],
      regulatoryReferences: [
        "Bank Secrecy Act (31 USC 5311-5332)",
        "31 CFR 1020.320 — SAR filing requirements",
        "FinCEN Advisory FIN-2025-A002 — Shell company risk indicators",
        "EU 6th Anti-Money Laundering Directive — Cyprus high-risk factors",
      ],
      detail: "This transaction presents textbook money laundering typology: legitimate business account used to move a large sum to a recently-formed shell entity in a high-risk jurisdiction with vague justification. Under BSA requirements, processing this wire without enhanced due diligence would constitute a compliance failure. SAR filing is mandatory regardless of whether the wire is blocked.",
    },
  },
  {
    id: "sanctions-screener",
    verdict: "ESCALATE",
    confidence: 0.78,
    riskScore: 62,
    findings: {
      category: "SANCTIONS_SCREENING",
      flags: [
        "Recipient entity not found on OFAC SDN list (as of screening date)",
        "Cyprus jurisdiction has elevated OFAC risk due to Russian-linked entities",
        "Recipient bank (Bank of Cyprus) is not sanctioned but has historical exposure to sanctioned flows",
        "No PEP (Politically Exposed Person) matches for listed directors",
      ],
      ofacResult: "NO_MATCH",
      jurisdictionRiskScore: "ELEVATED",
      detail: "No direct OFAC SDN match for Meridian Holdings Ltd or its listed directors. However, Cyprus remains an elevated-risk jurisdiction for sanctions evasion, particularly for Russian-linked shell structures. The entity's recent registration date and lack of operating history prevent adequate beneficial ownership verification. Recommending enhanced due diligence before clearance — not a hard block on sanctions grounds alone, but material risk when combined with other factors.",
    },
  },
  {
    id: "transaction-risk-analyst",
    verdict: "BLOCK",
    confidence: 0.91,
    riskScore: 89,
    findings: {
      category: "PATTERN_DEVIATION",
      flags: [
        "Amount deviation: +300% above sender's 90-day moving average",
        "First-time recipient (no prior transaction history with Meridian Holdings)",
        "Cross-border to high-risk jurisdiction (CY risk weight: 3.2x baseline)",
        "Composite risk score: 89/100 (threshold for auto-block: 75)",
      ],
      riskModel: {
        amountDeviation: 4.0,
        recipientRisk: 0.92,
        jurisdictionWeight: 3.2,
        timingAnomaly: 0.85,
        compositeScore: 89,
        autoBlockThreshold: 75,
      },
      detail: "The composite risk score of 89 exceeds the auto-block threshold of 75 across all four risk dimensions. Amount deviation alone (4.0x) would trigger enhanced review. Combined with first-time recipient, high-risk jurisdiction weighting, and timing anomaly, this transaction scores in the top 2% of risk for the sender's account profile.",
    },
  },
  {
    id: "operations-engineer",
    verdict: "ESCALATE",
    confidence: 0.74,
    riskScore: 45,
    findings: {
      category: "SETTLEMENT_FEASIBILITY",
      flags: [
        "Correspondent bank (Deutsche Bank AG) available for EUR settlement",
        "Wire submitted outside SWIFT cut-off (17:00 CET) — would settle next business day",
        "Cyprus banking hours: settlement confirmation delayed until 09:00 EET",
        "No existing nostro account relationship with Bank of Cyprus",
      ],
      detail: "From a pure settlement perspective, the wire is feasible but would not clear until next business day due to SWIFT cut-off timing. The absence of a nostro relationship with Bank of Cyprus means settlement would route through the correspondent (Deutsche Bank), adding latency and cost. The delayed settlement actually provides a natural window for enhanced review without impacting the customer if the wire is ultimately approved.",
    },
  },
];

for (const guard of guards) {
  await board.engine.claimJob(guard.id, job.id, {
    stakeAmount: 8,
    leaseSeconds: 300,
  });

  await board.engine.submitJob(guard.id, job.id, {
    summary: guard.verdict,
    confidence: guard.confidence,
    artifacts: {
      category: guard.findings.category,
      flags: guard.findings.flags,
      detail: guard.findings.detail,
      riskScore: guard.riskScore,
    },
  });

  console.log(`[${guard.id}] ${guard.verdict} (confidence: ${guard.confidence}, risk: ${guard.riskScore}/100)`);
  console.log(`  Category: ${guard.findings.category}`);
  for (const flag of guard.findings.flags) {
    console.log(`  - ${flag}`);
  }
  console.log("");
}

// --- Resolve the consensus ---
const resolution = await board.engine.resolveJob("aml-screening-system", job.id);

console.log("=".repeat(65));
console.log("AML SCREENING RESOLUTION");
console.log("=".repeat(65));
console.log("");

// --- Tally verdicts ---
const verdicts = guards.map((g) => g.verdict);
const blockCount = verdicts.filter((v) => v === "BLOCK").length;
const escalateCount = verdicts.filter((v) => v === "ESCALATE").length;
const avgRiskScore = Math.round(guards.reduce((sum, g) => sum + g.riskScore, 0) / guards.length);

const finalDecision = blockCount >= 2 ? "BLOCK" : "ESCALATE";

console.log("Wire ID:            " + transaction.wireId);
console.log("Final decision:     " + finalDecision);
console.log("Block votes:        " + blockCount + " of " + guards.length);
console.log("Escalate votes:     " + escalateCount + " of " + guards.length);
console.log("Avg risk score:     " + avgRiskScore + "/100");
console.log("Policy:             " + resolution.policyType);
console.log("Winners:            " + JSON.stringify(resolution.winners));
console.log("");

// --- SAR filing triggers ---
console.log("SAR FILING TRIGGERS (31 CFR 1020.320):");
console.log("-".repeat(45));

const allSarIndicators = new Set<string>();
for (const guard of guards) {
  const indicators = (guard.findings as any).sarIndicators;
  if (indicators) {
    for (const indicator of indicators) {
      allSarIndicators.add(indicator);
    }
  }
}
let sarIndex = 1;
for (const indicator of allSarIndicators) {
  console.log(`  ${sarIndex}. ${indicator}`);
  sarIndex++;
}
console.log("");
console.log("SAR filing deadline: " + new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString().split("T")[0]);
console.log("Filing required:     YES (regardless of wire disposition)");
console.log("");

// --- Required actions ---
console.log("REQUIRED ACTIONS:");
console.log("-".repeat(45));
console.log("1. BLOCK wire transfer pending enhanced due diligence");
console.log("2. File SAR with FinCEN within 30 calendar days");
console.log("3. Request beneficial ownership documentation for Meridian Holdings Ltd");
console.log("4. Verify stated business purpose with TechCorp Ltd relationship manager");
console.log("5. Escalate to BSA Officer for review and sign-off");
console.log("6. Preserve all transaction records per 31 CFR 1010.430 (5-year retention)");
console.log("");

// --- What would have happened without the guard ---
console.log("=".repeat(65));
console.log("WITHOUT THIS GUARD:");
console.log("=".repeat(65));
console.log("");
console.log("The wire clears. Then:");
console.log("");
console.log("  Day 0:    $340,000 arrives at Bank of Cyprus");
console.log("  Day 1-3:  Funds moved through 2-3 intermediary accounts");
console.log("  Day 5:    Funds withdrawn or transferred to non-cooperative jurisdiction");
console.log("  Day 14:   Internal audit flags the transaction in monthly review");
console.log("  Day 15:   BSA Officer determines SAR filing is required");
console.log("  Day 30:   SAR filed with FinCEN (deadline met, but barely)");
console.log("  Day 45:   FinCEN requests additional documentation");
console.log("  Day 90:   Regulatory examination finds AML program deficiency");
console.log("  Day 180:  Consent order issued");
console.log("");
console.log("Potential consequences:");
console.log("  - Civil money penalty:  $500,000+ (FinCEN)");
console.log("  - Customer loss:        $340,000 (unrecoverable from shell entity)");
console.log("  - Remediation costs:    $200,000+ (independent AML review)");
console.log("  - Reputational damage:  Consent order is public record");
console.log("  - Personal liability:   BSA Officer faces individual enforcement risk");
console.log("");
console.log("Total estimated exposure: $1M+");
console.log("");

// --- Reputation settlement ---
console.log("=".repeat(65));
console.log("REPUTATION SETTLEMENT (post-investigation)");
console.log("=".repeat(65));
console.log("");
console.log("After compliance investigation confirms the block was correct:");
console.log("");

const reputationSettlements = [
  { id: "fraud-analyst", delta: +5, reason: "Correctly identified behavioral anomaly pattern" },
  { id: "bsa-aml-reviewer", delta: +8, reason: "Highest confidence on correct BLOCK, identified all SAR triggers" },
  { id: "sanctions-screener", delta: +2, reason: "Correct ESCALATE — no SDN match, but flagged jurisdiction risk" },
  { id: "transaction-risk-analyst", delta: +5, reason: "Risk model correctly scored above auto-block threshold" },
  { id: "operations-engineer", delta: +1, reason: "Settlement analysis was accurate, ESCALATE was reasonable" },
];

for (const settlement of reputationSettlements) {
  const sign = settlement.delta > 0 ? "+" : "";
  console.log(`  ${settlement.id}: ${sign}${settlement.delta} reputation`);
  console.log(`    Reason: ${settlement.reason}`);
}

console.log("");
console.log("The bsa-aml-reviewer receives the largest reputation boost (+8)");
console.log("because it had the highest confidence (0.96) on the correct");
console.log("decision and identified the most comprehensive set of regulatory");
console.log("triggers. Over time, its votes will carry more weight in future");
console.log("AML screening decisions.");

// --- Audit trail ---
console.log("");
console.log("=".repeat(65));
console.log("AUDIT TRAIL");
console.log("=".repeat(65));
console.log("");
console.log("All screening decisions stored in aml-screening-state.json.");
console.log("Retention: 5 years per 31 CFR 1010.430.");
console.log("");
console.log("Job ID:       " + job.id);
console.log("Wire ID:      " + transaction.wireId);
console.log("Timestamp:    " + new Date().toISOString());
console.log("Guard count:  " + guards.length);
console.log("Decision:     " + finalDecision);
console.log("SAR required: YES");
3

Run the screening

npx tsx aml-screening-guard.ts

Expected output:

AML SCREENING: Wire Transfer WIRE-2026-0319-047291
Sender:    TechCorp Ltd (BIZ-TC-20240115)
Amount:    $340,000 USD
Recipient: Meridian Holdings Ltd (CY)
Timing:    02:47 AM EST
Purpose:   Consulting services

Screening job posted: <job-id>
5 guards evaluating...

[fraud-analyst] BLOCK (confidence: 0.93, risk: 87/100)
  Category: BEHAVIORAL_ANOMALY
  - Amount is 4x historical maximum ($340K vs $85K ceiling)
  - Transaction submitted at 02:47 AM — sender's normal hours are 09:00-18:00
  - Velocity spike: no wire activity in prior 45 days, then a single large transfer
  - Amount just below $350K threshold that triggers enhanced due diligence

[bsa-aml-reviewer] BLOCK (confidence: 0.96, risk: 94/100)
  Category: BSA_AML_VIOLATION
  - Recipient is a shell company in a high-risk jurisdiction (Cyprus)
  - Recipient entity registered only 3 months ago
  - Stated purpose ('consulting services') is vague
  - No prior business relationship between sender and recipient
  - Transaction meets multiple SAR filing criteria under 31 CFR 1020.320

[sanctions-screener] ESCALATE (confidence: 0.78, risk: 62/100)
  Category: SANCTIONS_SCREENING
  - Recipient entity not found on OFAC SDN list
  - Cyprus jurisdiction has elevated OFAC risk
  - Recipient bank has historical exposure to sanctioned flows
  - No PEP matches for listed directors

[transaction-risk-analyst] BLOCK (confidence: 0.91, risk: 89/100)
  Category: PATTERN_DEVIATION
  - Amount deviation: +300% above sender's 90-day moving average
  - First-time recipient
  - Cross-border to high-risk jurisdiction (CY risk weight: 3.2x)
  - Composite risk score: 89/100 (auto-block threshold: 75)

[operations-engineer] ESCALATE (confidence: 0.74, risk: 45/100)
  Category: SETTLEMENT_FEASIBILITY
  - Correspondent bank available for EUR settlement
  - Wire submitted outside SWIFT cut-off
  - No existing nostro account relationship with Bank of Cyprus

=====================================================================
AML SCREENING RESOLUTION
=====================================================================

Wire ID:            WIRE-2026-0319-047291
Final decision:     BLOCK
Block votes:        3 of 5
Escalate votes:     2 of 5
Avg risk score:     75/100

Regulatory framework

The Bank Secrecy Act (BSA) requires financial institutions to maintain effective AML programs and report suspicious activity to FinCEN.

Key requirements this guard addresses:

  • Customer Due Diligence (CDD): The guard evaluates whether the transaction is consistent with the customer's known profile
  • Enhanced Due Diligence (EDD): High-risk jurisdiction and shell company triggers activate additional scrutiny
  • SAR Filing (31 CFR 1020.320): The guard identifies SAR-triggering indicators and flags the filing obligation regardless of the wire disposition
  • Record Retention (31 CFR 1010.430): All guard evaluations are persisted for the required 5-year retention period

Reputation settlement

After the compliance team investigates and confirms the block was correct, reputation scores are updated:

// After investigation confirms the wire was indeed suspicious
const settlements = [
  { agentId: "fraud-analyst",           delta: +5 },  // Correct BLOCK
  { agentId: "bsa-aml-reviewer",        delta: +8 },  // Highest confidence, most comprehensive
  { agentId: "sanctions-screener",      delta: +2 },  // Correct ESCALATE (no SDN match)
  { agentId: "transaction-risk-analyst", delta: +5 },  // Risk model validated
  { agentId: "operations-engineer",     delta: +1 },  // Accurate settlement analysis
];

for (const s of settlements) {
  await board.ledger.payout(s.agentId, s.delta, job.id);
}

Why different reputation deltas?

The bsa-aml-reviewer receives the highest boost (+8) because it had the highest confidence (0.96) on the correct BLOCK decision and identified the most comprehensive set of regulatory triggers. The sanctions-screener receives a smaller positive delta (+2) because ESCALATE was the appropriate call given no SDN match — it would have been penalized if it had BLOCKED without sanctions evidence. Over time, these differential settlements cause the most accurate personas to carry the most weight.


Without this guard vs. with it

The wire clears automatically because the sender has a clean 2-year history and the amount is below the correspondent bank's enhanced review threshold.

Timeline:

  1. Wire settles at Bank of Cyprus next business day
  2. Funds moved through intermediary accounts within 72 hours
  3. Internal audit catches the anomaly in the next monthly review (Day 14+)
  4. SAR filed with FinCEN, but funds are long gone
  5. Regulatory examination finds the AML program missed real-time red flags

Cost:

  • Customer loss: $340,000 (unrecoverable)
  • FinCEN penalty: $500,000+ for program deficiency
  • Remediation: $200,000+ for independent AML review
  • BSA Officer personal liability risk
  • Consent order on public record

Extending this example

  • Real-time OFAC integration: Connect the sanctions-screener to a live OFAC SDN API for production screening
  • Transaction monitoring: Aggregate guard decisions across transactions to detect structuring patterns (multiple wires just below thresholds)
  • Regulatory reporting: Auto-generate SAR narratives from guard findings using @consensus-tools/workflows
  • Correspondent bank rules: Add jurisdiction-specific guards that activate based on the correspondent bank's risk policies
  • Customer risk rating: Feed guard decisions back into the customer's ongoing risk score, triggering account-level reviews when cumulative risk exceeds thresholds