The gap between collecting and using

Our platform sits on a goldmine of behavioral context that arrives with every transaction: the sender's IP address (with VPN, Tor, and datacenter detection from our behavioral engine), device fingerprints, and GPS coordinates. We were collecting all of it and scoring none of it.

The enrichment data showed up in the dashboard, so investigators could see "Connection via VPN" on a case, but it never influenced the verdict. The behavioral engine's trust score was the only behavioral signal in the risk calculation, and it entered as a coarse step function.

The signals we scored

Four new signal families, all deterministic and explainable:

Signal Points Source
Connection via Tor +20 Engine enrichment on sender's latest IP
Connection via VPN / datacenter / relay +10 each Same
First transaction from an unseen device +10 Device memory on the customer profile
Impossible travel (>900 km/h effective speed) +20 Consecutive GPS coordinates (stored, never used before)
Switch back to a different known device +10 Device memory (account-sharing signal)

All additive, capped at 40 points, escalate-only (they can raise a verdict, never lower it), with human-readable drivers:

Behavioral Intelligence  +30
• First transaction from a previously unseen device
• Connection via VPN
• Impossible travel: 5003 km in 18 min

The stolen-account pattern

These signals were designed to catch a specific attack: account takeover.

A customer, let's call her Adaeze, always sends money from her iPhone in Lagos. One evening, a fraudster who stole her credentials sends a transaction from a new Android device, connected through a VPN, and the GPS location jumps from Lagos to London within 20 minutes.

Before: all three clues were stored and displayed. The verdict didn't care.

After: +30 points from Behavioral Intelligence, potentially escalating the outcome.

One clue alone won't block a transaction. The cap and the small point values prevent that. But the pattern together pushes the score, and it can escalate the verdict.

Why not ML?

CBN's baseline standards for automated AML solutions include an AI/ML governance section. The expectation is not "don't use ML". It is "if you use it, be able to explain it."

Our position is stronger: no opaque ML in the decision path at all. Every point comes from a rule that a compliance officer can read:

if (nf?.tor) add(20, 'Connection via Tor network');
if (input.deviceId && !known.includes(input.deviceId)) {
  add(10, 'First transaction from a previously unseen device');
}

Design rule: every point in the risk score comes from a deterministic rule a compliance officer can read and defend in front of a regulator. No opaque ML in the decision path.

This is a competitive advantage in procurement conversations. When a bank's risk team asks "how does your scoring work?", the answer is a table of signals with point values and plain-English explanations, not "the model learned it from training data."

The feedback loop

We also closed the loop between analyst decisions and the behavioral engine:

  • True positive (investigator confirms fraud) → the fraudster's latest IP is automatically blacklisted → future transactions from that IP are flagged immediately
  • Any resolution → the account is marked reviewed → it leaves the engine's review queue

False positives deliberately do NOT auto-clear blacklist entries. Removal is an explicit, audited human action from the dashboard. Silently erasing fraud evidence on a single officer's judgment is exactly what a regulator would challenge.

The engineering

The signals live in a pure TypeScript utility with 15 unit tests covering: haversine distance calculations, time-window edge cases, cold-start learning mode, the tie-breaking rotation in device memory, and the convergence-dedup invariant. The service injects a Prisma-backed loader, so the algorithm is fully testable in isolation.

Device memory is bounded (10 known devices per profile, most-recent-first) and stored as a JSON array on the customer behavior profile. No new tables, no schema changes to the transaction pipeline.

See a live trace on your own data

We'll run the tracer against a scenario from your institution in a 30-minute demo: blocked transaction, full fund trace, frozen destination, packaged case.