Skip to main content

Insurance — claims & fraud rings

The problem. Most claims are honest and independent. Organised fraud isn't — it hides in the connections between claims that look unrelated one at a time: the same phone number on two policies, the same repair shop across a dozen claims, the same address shared by claimants who supposedly don't know each other. A flat claims table can't see a ring. A graph can: model claims, policies and the parties they share, and a single traversal lights up the cluster.

This scenario centres on graph entity resolution — turning a suspicious claim into the ring it belongs to.

Set up demo data

New workspace? Run this once (Python) to create two claims that share a repair shop — the hidden link a per-claim view misses. Safe to re-run.

import datahub_sdk

client = datahub_sdk.DataHubClient.from_env()

client.resources.create(
[datahub_sdk.Resource(external_id=x, name=x, labels=[lbl]) for x, lbl in
[("claim_88421", "Claim"), ("claim_88455", "Claim"), ("party_jdoe", "Party"),
("phone_47120099", "Phone"), ("shop_quickfix", "Shop")]],
[datahub_sdk.RelForm.by_external_ids("claim_88421", "party_jdoe", "filed_by"),
datahub_sdk.RelForm.by_external_ids("claim_88421", "shop_quickfix", "repaired_at"),
datahub_sdk.RelForm.by_external_ids("party_jdoe", "phone_47120099", "contact_phone"),
datahub_sdk.RelForm.by_external_ids("claim_88455", "shop_quickfix", "repaired_at")])

1. Model claims and the parties they share

Claims, policies and parties (people, phone numbers, addresses, repair shops) are all resources; the edges record who is connected to what. The shared parties are what make a ring visible.

List<RelForm> links = List.of(
rel("on_policy", "claim_88421", "policy_55righ"),
rel("filed_by", "claim_88421", "party_jdoe"),
rel("repaired_at", "claim_88421", "shop_quickfix"),
rel("contact_phone", "party_jdoe", "phone_47120099"));

client.resources().create(nodes, links);

2. Expand a suspicious claim into its ring

Walk out from the flagged claim. The returned sub-graph is everything connected to it — and any other claim that shares a party, phone or shop with it is part of the same cluster.

ResourceNetwork ring = client.resources().fetchRelated("claim_88421", 4);

// other claims reachable through shared parties/phones/shops
ring.nodes().stream()
.map(Resource::getExternalId)
.filter(id -> id.startsWith("claim_") && !id.equals("claim_88421"))
.forEach(c -> System.out.println("linked claim: " + c));

The more claims a single party, phone or shop connects, the stronger the signal — exactly the shared-node reasoning behind alarm correlation, applied to entities instead of assets.

3. Flag and watch the trend

Raise a fraud_review event on the cluster, and track claims_per_shop or claims_per_phone as series — a node whose claim count climbs unusually fast is a ring forming in real time.

See the result

Expanding the flagged claim surfaces the one it's secretly linked to:

linked claims: ['claim_88455'] ← shares shop_quickfix with claim_88421

See also