Money Laundering Detection
Money laundering moves illicit funds through a network of accounts and transactions to disguise where they came from. This example looks ahead from each account and predicts whether it will be reported for money laundering in the coming weeks, so an investigations team can review the riskiest accounts before the funds move on. Kumo Engine learns this from each account’s own attributes and from the money it has sent and received across the transaction network in the graph, which is the pattern a plain rule on any single account misses.
Data and relationships
The example assumes a prepared Kumo Engine graph, a matching snapshot, and these tables:
Connect inputs.transaction_id and outputs.transaction_id to transactions.transaction_id, connect inputs.account_id, outputs.account_id, and reports.account_id to accounts.account_id, and mark each table’s primary key and its timestamp time column. A transaction touches an account only through the inputs (sender) and outputs (receiver) tables, so there is no direct foreign key from transactions to accounts, and those two bridge tables carry the money flow in both directions. The reports table does have a direct foreign key to accounts, which is what lets the queries below count an account’s future reports. The transaction network is the signal the model learns from, even though the query text only counts reports: the funds an account has sent through inputs and received through outputs shape its prediction.
Score each account for money-laundering risk
This query looks ahead from each account and predicts whether it will be reported for money laundering in the next 30 days. COUNT(reports.*, 0, 30, days) counts the reports filed against the account over the 30 days that follow the scoring time, > 0 turns that into a yes or no label, and FOR EACH accounts.account_id produces one prediction per account. The forward window is anchored at the scoring time you run the query, not at each account’s own history, and it is the half-open interval (scoring time, scoring time + 30 days] shared across every account scored in the same run. Every transaction and any earlier report is dated before that anchor, so the model is trained and scored using only what was known before the window it looks ahead into. Count the reports with COUNT(reports.*, 0, 30, days) > 0 rather than predicting a raw report count, which Kumo Engine reads as a numeric target and scores as a regression.
Focus on accounts that just moved funds
Scoring every account on a fixed schedule works for a periodic sweep, but a monitor that reacts as money moves wants to review an account the moment funds arrive. This query makes the same 30-day prediction and adds a WHERE filter that keeps an account in the training and scoring population only when it received funds in the 7 days up to the scoring time. COUNT(outputs.*, -7, 0, days) > 0 counts the account’s incoming transactions over the 7 days before the anchor, so the model concentrates on accounts that are currently active. The prediction target is unchanged, so this still answers whether the account will be reported in the next 30 days.
Reading the predictions
Both queries return a probability that an account will be reported for money laundering within the next 30 days. An investigations team can rank accounts by that probability and open a review, file a suspicious-activity report, or place a temporary hold on the highest-risk accounts before the funds are layered away. The first query scores every account, which suits a periodic sweep of the whole book; the second narrows scoring to accounts that just received funds, which fits a monitor that reacts as money moves.
These are estimates from past behavior, not proof of wrongdoing, and every flagged account still needs a human investigation. The 30-day window is a starting point: widen or narrow the number of days to match how quickly your team can act, and shift the window forward, for example COUNT(reports.*, 10, 30, days) > 0, to separate accounts that need review this week from those to keep on a longer watchlist. The forward window is anchored at the time you run the query, so the model always uses only the transaction and report history recorded before that point and never sees the outcome it predicts. Refresh the predictions as new transactions arrive so each decision reflects the account’s latest activity.