A payment system that approves every transaction is useless. One that declines too many legitimate transactions loses customers. Fraud detection is a latency-sensitive classification problem: decide in under 100ms whether a transaction is fraudulent, using signals that fraudsters will actively try to evade.

The Signal Stack#

Fraud signals come from multiple layers:

Velocity checks: has this card been used more than 5 times in the last minute? Has this device initiated more than $500 in the last hour? These are simple counters in Redis with TTL. Fast, cheap, catches a large fraction of card-testing attacks.

Behavioral fingerprint: does this transaction pattern match the user’s history? A user who always shops in California making a purchase in Romania at 3 AM is anomalous. These signals require more computation but can be precomputed and cached per user.

Graph signals: is the recipient account newly created? Is it connected to accounts flagged for previous fraud? Fraud rings operate through networks of accounts; graph traversal surfaces these connections.

ML score: a model trained on historical transactions returns a fraud probability. Slow relative to velocity checks (10-30ms) but catches complex patterns the rules miss.

graph TD A[Transaction request: $200 at 3 AM Romania] --> B[Velocity check: Redis counter lookup, 1ms] B --> C[Behavioral fingerprint: user history cache, 5ms] C --> D[ML score: fraud probability model, 20ms] D --> E[Rule engine: combine signals] E --> F{Score above threshold?} F --> |Yes| G[Decline or flag for review] F --> |No| H[Approve transaction] style A fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style B fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style C fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style D fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style E fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style F fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style G fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style H fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff

Synchronous vs Asynchronous Checks#

Not all fraud checks can run synchronously within the payment approval window. Run fast checks (velocity, cached fingerprint) synchronously. Run slow checks (graph traversal, deep ML models) asynchronously: approve the transaction, then run the deep checks and flag or reverse if they fire.

This creates a window where fraudulent transactions are approved and then reversed. The business decision: accepting some fraud now in exchange for not blocking legitimate transactions. The reversal rate and associated loss are acceptable if they’re below the cost of false positives (declined legitimate transactions and lost customers).

Feature Stores for Fraud#

Feature stores matter a lot for fraud. The ML model needs features computed consistently between training and serving. “User’s 30-day average transaction amount” computed differently in training vs serving causes training-serving skew that degrades model accuracy. Precompute and cache fraud-relevant features for every user; the serving path reads from the cache.

At Oracle#

Oracle has a license fraud detection system: customers occasionally misreport their usage to underpay. We built velocity and pattern checks: a customer whose reported usage dropped 80% month-over-month while their headcount grew triggered a review flag. Not ML, just rules — but they caught 90% of the clear cases before they reached the audit team. The audit team focused on the 10% that required human judgment.

What I’m Learning#

Fraud detection is a layered system: cheap fast rules first, expensive slow models last. The architecture mirrors the cost structure of the signals. The hardest part isn’t the model — it’s the feature pipeline that keeps signals fresh and consistent between training and serving.

Have you worked on fraud or anomaly detection systems, and what signal turned out to be most predictive?