Exception Routing & Fraud-Pattern Detection for ACH/Wire Reconciliation

Every reconciliation engine eventually says the same thing about some fraction of its input: I cannot confidently pair this record with anything. That admission is not an error — it is the most important output the matcher produces. A break is a transaction that carries real money, a real regulatory clock, and a non-zero probability of fraud, and what your platform does in the seconds after the matcher gives up determines whether the item becomes a cleanly resolved exception or a Reg E liability, an OFAC blocking failure, or a duplicate debit that reaches a consumer account. This reference sits within the broader ACH and wire reconciliation practice and picks up exactly where the matching engine stops: it covers the triage, scoring, routing, and lifecycle machinery that turns an unmatched record into a governed, auditable, resolvable exception.

Breaks originate in the transaction matching and reconciliation algorithms layer — a record that clears no deterministic key, falls outside every date window, and exceeds every tolerance band arrives here as a structured envelope, not a log line. From that envelope the exception subsystem does four distinct jobs, each with its own guide below: it models the break's life as a finite-state machine so that no status change happens without an attributable actor; it runs fraud-pattern detection signals to score the item before a human ever sees it; it routes the scored break into prioritized work queues so the riskiest, oldest, and highest-value items surface first; and it runs OFAC sanctions screening on any unmatched party before a single dollar of provisional or final credit moves. Get this layer wrong and the same break lands in three queues, ages past its Reg E deadline unnoticed, or auto-resolves an item that should have been blocked and reported.

The journey of a reconciliation break after the matcher gives up The matching engine emits a record it cannot confidently pair as a structured exception envelope. The envelope enters a triage stage that runs two gates in parallel: fraud-pattern signals (duplicate debit, velocity, structuring, Benford) that produce a risk score, and an OFAC sanctions screen against the SDN list that produces a hit or clear. A blocking sanctions hit halts the item and files a report rather than routing it. Otherwise the scored break is routed by a rules engine into one of several prioritized work queues, and simultaneously enters an exception finite-state machine at NEW, advancing through PENDING_REVIEW, INVESTIGATING, and either PROVISIONAL_CREDIT_ISSUED then RESOLVED, or WRITTEN_OFF. A Reg E provisional-credit clock runs alongside the state machine. Every gate result, routing decision, and state transition is appended to an immutable audit trail spanning the full width of the diagram. After the matcher gives up: triage, screen, route, resolve No confident match ExceptionEnvelope from matcher Triage — fraud signals duplicate debit · velocity structuring · Benford risk score 0–100 fused, weighted, logged Triage — sanctions screen OFAC SDN · 50% rule fuzzy name match hit → BLOCK + report clear → continue routing Route Priority scoring amount · age · risk skills-based assign segregation of duties High-value wire Q Reg E dispute Q Standard ACH Q Exception FSM + Reg E clock NEW → PENDING_REVIEW → INVESTIGATING → PROVISIONAL_CREDIT_ISSUED → RESOLVED or → WRITTEN_OFF 10 / 45 / 90 business-day clocks · attributable actor clear Immutable append-only audit trail signals · scores · sanctions dispositions · routing · state transitions — Reg E / BSA / OFAC / SOX / FFIEC
An unmatched record leaves the matcher as a structured envelope, is triaged by fraud signals and an OFAC screen, routed into a prioritized queue and a lifecycle state machine with a Reg E clock, and resolved — with every step appended to an immutable audit trail.

Modeling the Exception Lifecycle

The moment a break exists, it needs a status — and every status it will ever hold, plus every legal path between them, should be defined before a single exception is created. Ad-hoc status strings scattered across a database are how a reconciliation program ends up with items stuck in states nobody remembers creating, provisional credits issued against already-resolved disputes, and audit questions no one can answer. The discipline that prevents this is modeling the break as an explicit finite-state machine: a closed set of states, a transition table naming which moves are legal, a guard on each transition, and an append-only log recording who moved the item and when.

The canonical path — NEW → PENDING_REVIEW → INVESTIGATING → PROVISIONAL_CREDIT_ISSUED → RESOLVED, with a WRITTEN_OFF terminal for uncollectable items — reads simply, but the value is in the transitions the machine refuses. You cannot resolve an item that was never investigated; you cannot issue provisional credit twice; you cannot reopen a written-off break without an explicit, logged reversal. Each guard is where a regulatory rule becomes code: the transition into PROVISIONAL_CREDIT_ISSUED is precisely where the provisional-credit state transition logic checks Reg E eligibility and stamps a reversal deadline, and the clock that governs how long an item may sit in INVESTIGATING before it breaches is the concern of the exception aging and SLA timer machinery.

Because every state change is attributable and append-only, the state machine doubles as the spine of the audit trail. An examiner asking "why did this consumer receive provisional credit on the eleventh business day" gets a deterministic answer: the transition log names the dispute-received date, the actor, the guard that fired, and the deadline computed at that instant. That property — reconstructability — is what separates a governed exception system from a status column somebody updates by hand.

Scoring the Break for Fraud

A break is not just a matching failure; it is a candidate for something worse. Duplicate ACH debits, wire-transfer velocity spikes, deposit structuring below the $10,000 CTR threshold, and amount distributions that violate Benford's Law all surface disproportionately in the unmatched population, because the same operational chaos that breaks a match — re-presentments, re-keyed references, split transactions — is what fraud uses for cover. Running fraud-pattern detection signals across every break before it reaches a human turns the exception queue from a first-in-first-out backlog into a risk-ranked worklist.

The individual signals are cheap and specific. A duplicate-debit check hashes a composite idempotency key over a rolling window; a wire velocity check maintains per-beneficiary sliding counters; a Benford's Law check tests the first-digit distribution of a batch against the expected logarithmic curve with a chi-square statistic. None of them is a verdict on its own — a single duplicate hash collision or one over-threshold velocity count is a signal, not proof. The engineering work is signal fusion: combining weighted signals into a single risk score that survives an audit, so that the score routes the item without any one noisy feature triggering a false accusation.

Crucially, fraud scoring runs before routing and alongside the state machine, not after a human has already spent twenty minutes on the item. A break that scores in the top risk decile should jump the queue and carry its contributing signals into the analyst's view as pre-populated context, so the investigator starts with "three identical debits to this account in six days" rather than a bare amount mismatch.

Routing Into Prioritized Work Queues

Once a break is scored and screened, it has to reach the right person in the right order. A flat, undifferentiated exception queue guarantees that a $4.2 million Fedwire break waits behind three hundred $12 ACH rounding differences, and that a Reg E dispute with a ten-business-day provisional-credit deadline ages next to items with no clock at all. Routing reconciliation exceptions into prioritized work queues replaces first-in-first-out with a deliberate topology: rules that classify each break by rail, amount, risk score, and regulatory clock, then place it in a queue whose ordering reflects genuine business urgency.

The ordering itself comes from an explicit priority score — a weighted combination of dollar exposure, exception age against its SLA, and fraud risk — recomputed as items age so that a low-value break creeping toward a deadline eventually outranks a fresh high-value one. On top of ordering sits assignment: auto-assignment rules match items to analysts by skill (wire investigators, ACH-return specialists, sanctions reviewers), balance load so no one analyst hoards the easy items, and — critically — enforce segregation of duties so the person who created or approved a payment can never be the person who clears its exception.

Routing is also where the exception system connects back to the matcher's own confidence signals. A break that failed only a date window carries different risk than one that failed on amount and beneficiary name together, and the routing rules should read the matcher's break reason rather than treating every unmatched item as equivalent. This is the practical payoff of emitting a rich ExceptionEnvelope instead of a bare identifier: the routing layer can make specific decisions because the matcher told it specifically why the match failed.

Screening Unmatched Payments Against Sanctions Lists

Some breaks must never route to a normal queue at all. A payment to or from a party on the OFAC Specially Designated Nationals (SDN) list is not an operational exception to be worked down at leisure — it is a blocking obligation that can carry strict-liability civil penalties if the funds move. OFAC sanctions screening on unmatched payments therefore runs as a gate inside triage, not as a downstream review: before a scored break is eligible for routing, resolution, or provisional credit, the unmatched party names are screened against the SDN and consolidated sanctions lists, and any hit halts the item pending disposition.

Screening unmatched payments is harder than screening clean ones precisely because the data is degraded — the same truncated, re-keyed, transliterated names that broke the match are what a sanctions screen has to work with. A brittle exact-match screen misses "MOHAMMED" spelled "MUHAMMAD" and lets a blocked party through; an over-loose one buries the desk in false hits. Effective fuzzy name matching against the OFAC SDN list normalizes and tokenizes names, applies a scorer such as Jaro-Winkler, and tunes the threshold against the real cost asymmetry: a missed true hit is a regulatory event, a false hit is an analyst-minute. The OFAC 50% rule adds a second dimension — entities owned 50 percent or more by blocked parties are themselves blocked even when not individually listed — so screening must reach beyond a literal name lookup.

Regulatory & Compliance Boundary

The exception layer is where the payment pipeline's legal obligations become concrete deadlines and irreversible actions, which is why its compliance boundary is treated as architecture rather than paperwork. These same constraints are catalogued in depth in the regulatory compliance and audit controls reference; this section maps how each one lands on an exception.

Reg E provisional-credit clock. For consumer accounts, Regulation E (12 CFR 1005.11) sets a hard investigation timeline: a financial institution generally has ten business days to resolve an alleged error or issue provisional credit, extendable to forty-five (or ninety for new accounts, point-of-sale, and foreign-initiated transactions). That clock starts when the consumer's notice is received, not when the exception is created, and the state machine's transition into PROVISIONAL_CREDIT_ISSUED is the code that enforces it. The dispute-timeline mechanics are detailed under Reg E error resolution.

BSA/AML SAR obligations. Fraud signals that survive investigation do not simply close the break. Under the Bank Secrecy Act, patterns consistent with structuring, money laundering, or other suspicious activity trigger a Suspicious Activity Report, generally within thirty calendar days of initial detection. The fraud-scoring layer therefore has to preserve its contributing signals as evidence, not discard them once the item routes — a SAR narrative is reconstructed from exactly the duplicate-debit hashes and velocity counts the triage stage computed.

OFAC blocking. A confirmed sanctions hit inverts the normal resolution flow: the funds are frozen into a blocked account, the transaction is reported to OFAC (generally within ten business days), and the item is emphatically not resolved by paying it out. This is strict liability — intent is irrelevant — which is why screening is a pre-routing gate and why a blocking disposition must be a terminal, non-overridable state in the machine rather than one status among many.

SOX/FFIEC audit. Sarbanes-Oxley and FFIEC examination standards require that the controls over these decisions be demonstrable, not asserted. Every state transition, every provisional-credit issuance, every sanctions disposition, and every manual override must land in an append-only, tamper-evident log with an attributable actor and timestamp. The immutable audit trail that backs the whole exception subsystem is what converts an examiner request from a reconstruction project into a query.

The ExceptionEnvelope and Router

The representative pattern below is the contract between the matcher and everything downstream. The matcher emits an ExceptionEnvelope — a validated pydantic v2 model carrying the break reason, the parties, the amount as integer cents, and the matcher's own confidence — and a router dispatches it to the correct handler based on a sanctions gate, a fraud score, and the rail. Notice that the sanctions gate short-circuits everything: a blocking hit never reaches the fraud-and-route path.

python
from __future__ import annotations

from decimal import Decimal
from datetime import datetime, timezone
from enum import Enum
from typing import Callable, Optional
from pydantic import BaseModel, Field, field_validator


class BreakReason(str, Enum):
    NO_DETERMINISTIC_KEY = "no_deterministic_key"
    OUTSIDE_DATE_WINDOW = "outside_date_window"
    AMOUNT_OVER_TOLERANCE = "amount_over_tolerance"
    MULTI_FIELD_LOW_CONFIDENCE = "multi_field_low_confidence"


class ExceptionEnvelope(BaseModel):
    """Immutable hand-off from the matcher to the exception subsystem."""
    model_config = {"frozen": True, "extra": "forbid"}

    exception_id: str
    rail: str                         # "ACH" | "FEDWIRE" | "SWIFT"
    break_reason: BreakReason
    amount_cents: int = Field(gt=0)   # money as integer cents, never float
    originator_name: str
    beneficiary_name: str
    match_confidence: Decimal         # matcher's best score, 0..1
    is_consumer: bool                 # gates the Reg E clock
    created_at: datetime

    @field_validator("match_confidence")
    @classmethod
    def bounded(cls, v: Decimal) -> Decimal:
        if not (Decimal("0") <= v <= Decimal("1")):
            raise ValueError("match_confidence must be within [0, 1]")
        return v


def route(
    env: ExceptionEnvelope,
    sanctions_gate: Callable[[ExceptionEnvelope], bool],   # True = blocking hit
    fraud_score: Callable[[ExceptionEnvelope], int],       # 0..100
    handlers: dict[str, Callable[[ExceptionEnvelope, int], str]],
) -> str:
    """Dispatch a break: sanctions block short-circuits fraud + routing."""
    if sanctions_gate(env):
        return handlers["sanctions_block"](env, 100)       # freeze + report; terminal
    score = fraud_score(env)
    if score >= 80 or env.amount_cents >= 1_000_000_00:
        return handlers["high_priority"](env, score)       # jump the queue
    return handlers[env.rail.lower()](env, score)

Scaling & Memory Considerations

The exception subsystem never touches the whole matched population — by construction it processes only the residue the matcher could not pair, which in a healthy pipeline is a small percentage of throughput. That asymmetry shapes the cost model. If the matcher processes records and a break rate applies, the exception layer handles envelopes, and each envelope's triage is dominated by its two gates. The fraud signals are individually amortized against pre-built rolling indexes (a duplicate-hash set, per-beneficiary velocity counters), so scoring a batch is . Sanctions screening is the expensive gate: fuzzy-matching each of the unmatched parties against an SDN list of entries is naively , which is why production screens block on a token index and only score the small candidate set a blocking filter returns, collapsing the practical cost toward for a small candidate count .

Because breaks arrive as a stream of independent envelopes with no shared mutable match state, the layer parallelizes cleanly: partition by a stable key such as beneficiary_name hash so that velocity counters for a given party stay on one worker, and a pool scales linearly until the sanctions list or the audit sink becomes the bottleneck. Hold envelopes as frozen, slotted models and stream them through generators rather than materializing the day's break population — the append-only audit writer, not the CPU, is usually the constraint, so batch its writes and never let a synchronous log call sit in the hot path of a routing decision.

Engineering Takeaways

  1. A break is a first-class object, not a log line. Emit a validated ExceptionEnvelope carrying the break reason, parties, integer-cent amount, and the matcher's confidence, so every downstream gate can make a specific decision instead of guessing.
  2. Screen sanctions before you route, resolve, or credit. OFAC blocking is strict liability; the screen belongs as a pre-routing gate that short-circuits the fraud-and-route path, and a blocking hit must be a terminal, non-overridable state.
  3. Score fraud before a human sees the item. Fused, weighted signals turn a first-in-first-out backlog into a risk-ranked worklist and hand the analyst pre-populated context instead of a bare mismatch.
  4. Start the Reg E clock from the consumer's notice, not the exception's creation. The two dates differ, and computing the provisional-credit deadline from the wrong one is how a ten-business-day obligation silently breaches.
  5. Make every state transition attributable and append-only. The transition log is simultaneously the workflow spine and the audit spine; reconstructability is what turns an examiner request into a query.
  6. Preserve fraud signals as evidence. A SAR narrative is rebuilt from the duplicate-debit hashes and velocity counts computed at triage, so scoring must retain its contributing features, not just its output score.
  7. Recompute priority as items age. A weighted score that never re-ranks lets a low-value break slide past its SLA behind fresh high-value items; age has to be able to overtake dollars.
  8. Enforce segregation of duties in the router, not the runbook. The person who created or approved a payment must be structurally barred from clearing its exception, which means assignment rules — not policy documents — carry the control.