Fraud lives in the connections. One insurance claim looks fine. One transfer looks fine. The ring only shows up when you see the shared phone number, the same body shop, or the money moving on within hours. A fraud detection graph database stores those connections directly, so an AI agent can walk them. Graph Hacks, FalkorDB's online hackathon in October 2026, drew 7,899 developers and 1,213 projects, and some of the sharpest went after this problem. In Prabal Patra's test on 5,040 generated insurance claims, per-claim rules missed 120 of the 240 fraud claims and flagged 2,789 honest drivers. The graph agent froze 200 of the 240 fraud claims and no honest ones.
Is your agent still reading fraud alerts one record at a time? Start free on FalkorDB Cloud and load your accounts, devices and transfers as one graph.
What is a fraud detection graph database?
A fraud detection graph database stores accounts, people, devices and transfers as connected nodes and edges, so software can find rings and follow money by walking those links instead of reading one record at a time.
FalkorDB fits because the queries an agent needs are plain Cypher, and algorithms like WCC, label propagation and betweenness run from the same query language. One instance holds many isolated graphs, so each bank can have its own. And GRAPH.RO_QUERY makes the database itself refuse writes, so an agent gathering evidence can't change it.
Why do rules miss what a graph can see?
| Question | Usual approach | Graph approach |
|---|---|---|
| Do two claimants on separate claims share a phone number? | Per-claim rules read one claim and never see it | One path through a shared Phone node |
| Where did the stolen money go next? | Freeze every account the money touched | Follow it hop by hop, counting only transfers made after the money arrived |
| Does one ring span several banks? | Each bank sees its own slice, and each slice looks harmless | A shared graph of hashed account IDs shows one connected ring |
| How much evidence do we really have? | An AI agent counts ten paths to one device as ten pieces of evidence | Count distinct origins, not paths |
How did Graph Hacks teams catch fraud rings with a graph?
All four projects keep a person in charge of the money, but the timing differs, so I call it out for each one. All four are hackathon builds tested on generated or synthetic data.
1. Hold only the stolen part of an account
TraceHold · Shivani Ponturu · See the code · Use the app · Blog
A fraud victim in India reports a theft. By then the money has moved through several mule accounts, mixed with other people's money. Banks often freeze every account it touched. Someone who received a small part of it can lose access to everything they have.
TraceHold loads IBM's synthetic anti-money-laundering data as one graph of 518,581 accounts and 5,078,345 transfers, plus the devices, phones and IPs they use. Each hop only counts transfers made after the money arrived. Then it works out how much of each balance is still the victim's money. If a third of what came in was stolen, a third of what leaves is too. Three agents trace the money, judge each account and re-check every hold. They call fixed tools over MCP and never write queries. My favorite part is that every amount comes from code, never from the model.
A person approves or overrides every hold before any request reaches the bank, which in this project is a mock bank.
Here is the time rule. Shaping this query with GRAPH.EXPLAIN took one hop from 77 seconds to 2 milliseconds.
UNWIND $ids AS tid
MATCH (v:Account)-[f:TRANSFERRED {id: tid}]->(m:Account) WITH f, m
MATCH (m)-[n:TRANSFERRED]->(x:Account) WITH f, n WHERE n.ts >= f.ts
RETURN count(n)
Shivani tested it on 36 complaints against three other ways to freeze money. The table sets freezing every touched account at 100%.
| Method, on 36 test complaints | Innocent money frozen |
|---|---|
| Freeze every account the money touched | 100% |
| Greedy trace, holding up to the disputed amount at every hop | 1.37% |
| Fixed rules, no AI | 0.49% |
| TraceHold | 0.012% |
That is 99.99% less than freezing everything and 97.6% less than fixed rules. TraceHold also held 17% more stolen money than the rules, but it caught fewer mule accounts, 59% against 70%. A second test set showed a much smaller saving over the rules, and the saving against freezing everything fell to 32%. In that set, one complaint made up nearly all of the innocent money TraceHold froze.
In the builder's words. "Innocent customers then lose access to all their savings over a small disputed amount." Shivani Ponturu, TraceHold
Try it. Store a timestamp on every transfer edge and apply n.ts >= f.ts after a WITH on each hop. Start free on FalkorDB Cloud.
2. Rank a day of payments, then freeze what is left
FlowTrace · Genga K, Om Sharma · See the code · Use the app · Blog
Mule money often leaves within hours, long before an analyst opens the alert. One day in this team's test data has 654,467 payments, and only 0.08% of them are laundering.
FlowTrace scores every payment with a LightGBM model and groups the flagged ones into ranked cases. The full graph holds 515,088 accounts and 3,641,261 transfers from IBM's synthetic data, and cases, holds and human verdicts sit in the same graph as Case, Action and Audit nodes. An agent picks its next step from fixed Cypher tools, follows the money in time order, and looks for salary or rent that would explain it.
A person confirms or releases every hold with a written reason. The timing has one exception. When the agent is very sure, it places a provisional hold first, and that hold is released after 1 business day unless a person confirms it.
// A person decides: only an open action changes, and the audit entry is written in the same query
MATCH (a:Action {id: $id}) WHERE a.status IN ['PROPOSED', 'PROVISIONAL']
WITH a, a.status AS was
SET a.status = $to, a.decided_by = $by, a.decided_at = $now, a.reason = $reason
CREATE (:Audit {id: $audit_id, ts: $now, actor: $by, event: $to, action_id: a.id,
from_status: was, to_status: $to, reason: $reason})
Genga and Om measured it on a held-out test day. The dollar figures come from a 40-case slice of that day, and innocent money counts holds as if a person approved them all.
| Test | Result |
|---|---|
| Full day, 654,467 payments | 186 cases, and the top 15 are all laundering |
| Laundering money frozen on 21 cases, LLM agent | 97.1% ($5.51M of $5.67M still in the trail) |
| Laundering money frozen on 21 cases, rules alone | 98.6% ($5.59M of $5.67M) |
| Innocent money held on 19 legitimate cases, LLM agent | $47,796 |
| Innocent money held on 19 legitimate cases, rules alone | $98.3M |
Two cases with one huge payment each make up $98.2M of that rules figure. The full-day funnel runs offline from the scoring model, and the holds and investigations run on the graph. The team also published a limit. Choosing where to hold is no better than chance, 57% with the LLM against a 64% base rate, which is why a person decides.
In the builder's words. "The same tools run as an MCP server, so any AI agent can investigate and propose, but only a person can approve." Genga K and Om Sharma, FlowTrace
Try it. Write each hold decision and its audit node in one Cypher query, with a WHERE on the open status, so a second approval changes nothing. Start free on FalkorDB Cloud.
3. Find insurance rings that learned the rules
Diktiax · Prabal Patra · See the code · Use the app · Blog
In the project's seeded data, claim CLM-48291 is an ordinary $42,500 collision on its own. But two other claims had accidents at the same intersection with no police report. All three went to the same body shop, clinic and adjuster. Two of the claimants share a phone number. None of that lives in any single record.
Diktiax makes anything a ring reuses its own node. Phones, addresses and VINs merge on their value with MERGE (p:Phone {number: $phone}), so a new claim that reuses a ring's phone connects to the ring the moment it's filed. The agent explores three hops out, then runs Cypher detectors for four ring patterns. Plain code sets the score, never the LLM.
The agent freezes the payout and assigns an investigator from the graph. That person confirms the fraud or releases the money.
MATCH (c:Claim {id: $claim_id})<-[:FILED_CLAIM]-(me:Customer)-[link:HAS_PHONE|LIVES_AT]->(contact)
<-[:HAS_PHONE|LIVES_AT]-(peer:Customer)-[:FILED_CLAIM]->(other:Claim)
WHERE peer.id <> me.id
RETURN c.id AS claim_id, me.id AS customer_id, type(link) AS via,
coalesce(contact.number, contact.line) AS contact, peer.id AS peer_id,
peer.name AS peer_name, other.id AS linked_claim_id
ORDER BY peer_id, linked_claim_id
Prabal tested it on 5,040 generated claims with 30 hidden rings.
| Group | Claims | Per-claim rules flagged | Graph agent froze | Graph agent sent to audit |
|---|---|---|---|---|
| Tight rings | 120 | 120 | 120 | 0 |
| Evasive rings | 80 | 0 | 80 | 0 |
| Loose rings | 40 | 0 | 0 | 40 |
| Honest | 4,800 | 2,789 | 0 | 169 |
The rings are built the way the detectors look for them, so this shows the mechanism on generated data. Evasive rings file police reports and keep amounts small, which is exactly what per-claim rules can't flag. Loose rings share only a location and a shop. They are the blind spot, and they go to audit along with 3.5% of honest claims. A pre-registered re-run on a fresh seed gave the same picture, with rules flagging 2,753 honest drivers and the graph freezing 200 of 240 and no honest claims. An LLM reading a 50-claim sample one claim at a time caught 3 of 24 fraud claims.
In the builder's words. "The claims are connected, not similar." Prabal Patra, Diktiax
Try it. When you load claims, MERGE phones, addresses and VINs on their value, then run the shared contact query on one claim. Start free on FalkorDB Cloud.
4. See the ring no single bank can see
BankWeave · Arpit Singh · See the code · Use the app · Blog
Launderers spread one ring of accounts across several banks. Each bank clears its own alert. Banks can't pool raw customer data, so the ring stays invisible.
BankWeave gives each bank its own isolated graph and adds a consortium graph that links them through salted, hashed account IDs. One agent clears alerts with a stated reason, another builds the case, and a Skeptic argues the innocent explanation. Each piece of evidence stores its origin, so confidence counts independent sources instead of paths. Before a freeze, GRAPH.COPY makes a sandbox and algo.maxFlow runs again without the frozen accounts. A person approves every action by signing a Merkle root over the case.
The live demo runs on FalkorDB Cloud's free tier, with 36 graphs in about 81 MB holding a 6-bank slice of IBM's data.
MATCH (a:Account {id:$id})-[t:TRANSFERS]-(b:Account)
WHERE b.bank_id <> a.bank_id
RETURN b.id AS account, b.bank_id AS bank, count(t) AS transfers, sum(t.amount) AS volume
ORDER BY volume DESC LIMIT 25
Arpit measured these against IBM's labels. F1 balances precision and recall, and higher is better.
| Test | Result |
|---|---|
| Best-placed bank's share of a ring's accounts, 36 cross-bank rings | 43.8% on average |
| Rings any single bank held whole | 0 of 36 |
| Consortium graph's share of each ring | 100% |
| Alert triage F1 on 4,000 alerted accounts, 129 illicit | 0.205 for the graph score, 0.126 for rules without the graph, 0.062 for escalating everything |
| Agent evidence that repeated an origin already seen | 27.8% of 510 items |
| Reports that passed the provenance gate | 25 of 28 |
In the builder's words. "Each bank sees only its own slice, and each slice looks harmless, so each alert gets cleared." Arpit Singh, BankWeave
Try it. BankWeave's 36 graphs fit in about 81 MB on the free tier, so start there. Create one graph per tenant and one shared graph, link accounts through hashed IDs, then run the same query on both and compare what comes back. Start free on FalkorDB Cloud.
Which other fraud project should you look at?
Give a fraud agent memory with no vector store. LedgerLens, by Mihirkumar Rabari, keeps data, memory, policies and the audit trail in one FalkorDB graph per bank. Recall is a full-text query that took a median of 0.37 ms on a seeded graph of about 2,100 nodes, fast enough to run on every step of the agent loop. Every seeded bank includes a high-value, legitimate, isolated account, and the investigator refuses to freeze it, returning no_case. Watch the demo.
If you want to keep going, read 5 agent memory patterns from 647 Graph Hacks projects and how a company brain stays safe with fixed read-only queries.
Frequently asked questions
Why use a graph database for fraud detection?
Fraud rings hide in the links between records, and a graph database stores those links directly. In a Graph Hacks test on 5,040 generated insurance claims, per-claim rules flagged 2,789 honest drivers, while a graph agent froze 200 of 240 fraud claims and no honest ones.
How does a fraud detection graph database follow stolen money?
It walks transfers hop by hop and applies a time rule at each hop, so a transfer only counts if it happened after the money arrived. TraceHold and FlowTrace both write this rule in Cypher.
Can an AI agent freeze money on its own?
In these projects a person has the final say, and the timing varies. TraceHold waits for approval before any request reaches the bank. FlowTrace can place a provisional hold that is released after 1 business day unless a person confirms it. Diktiax freezes the payout, then an assigned investigator confirms or releases it. BankWeave needs a person to sign off on every action.
Can banks find a shared laundering ring without pooling customer data?
In BankWeave, each bank keeps its own isolated graph, and a consortium graph links accounts through salted, hashed IDs. On IBM's synthetic data, the best-placed bank held 43.8% of a ring's accounts on average, and the consortium graph held all of them.
Do graph agents replace fraud rules?
These tests show a trade-off. TraceHold froze far less innocent money than fixed rules on its first test set, but caught 59% of mule accounts against 70% for the rules. FlowTrace's rules froze slightly more laundering money, 98.6% against 97.1%, and proposed far more innocent money for holds.
Were these projects tested on real bank data?
No. TraceHold, FlowTrace and BankWeave use IBM's public anti-money-laundering dataset, which is synthetic. Diktiax and LedgerLens use data they generated themselves. None of their READMEs reports a deployment at a bank or insurer.
Follow your first money trail
Every project here runs on the same FalkorDB you can start as a free cloud instance, or with one Docker command, docker run -p 6379:6379 -p 3000:3000 -it --rm falkordb/falkordb:latest. Load one day of transfers with a timestamp on each edge, then run one time-ordered hop from a flagged account.
Not ready to build yet? Join the FalkorDB Discord to get help from our engineers, share what you build, and hear about the next Graph Hacks first.