D. Mule and network patterns
A money mule is a person who lets their account be used to receive and pass on money that is not theirs — sometimes knowingly for a fee, sometimes tricked by a fake job or romance offer. Criminals rarely move proceeds through one account. They use networks: many accounts collect small amounts, pass them on, mix them, and eventually deliver them somewhere they can be cashed out.
Each individual transfer looks ordinary. The pattern only shows when you look at who is sending to whom across many accounts. That is what these rules do.
Rules in this category
Section titled “Rules in this category”| ID | Rule | Mode | Status |
|---|---|---|---|
| D1 | Fan-in: many senders to one account | PE/SCH | Ships (core, post-event) |
| D2 | Fan-out: one account to many recipients | PE/SCH | Ships (core, post-event) |
| D3 | Fan-in followed by fan-out (mule) | SCH | Ships (core, scheduled daily) |
| D4 | Circular flow (A → B → C → A) | SCH | Ships (core, scheduled daily) |
| D5 | Shared device, phone or ID across many customers | RT/SCH | Ships for devices (fraud, real-time); phones and IDs planned |
| D6 | Counterparty linked to prior confirmed case | RT/PE | Ships (core, post-event) |
| D7 | Many of our customers paying one counterparty | PE | Ships (core, post-event) |
D1 Fan-in: many senders to one account
Section titled “D1 Fan-in: many senders to one account”What it looks for: one account receiving money from an unusually large number of different senders in a short period. Collection accounts in fraud schemes (fake investment clubs, online shopping scams, “loan processing fees”) look exactly like this.
Example
With min_distinct_senders 20 and window 7 days:
- ✅ Fires: a personal wallet receives payments from 46 different people in five days, most of them GHS 200–500.
- ❌ Does not fire: a wallet that receives from 6 family members.
Parameters
| Parameter | Meaning | How to tune |
|---|---|---|
min_distinct_senders |
Number of different senders that triggers the rule | Much higher (or excluded) for merchants, churches, schools and other collection accounts |
window |
The period | Days to weeks |
Mode and pack: post-event or scheduled.
False positives: small traders who accept mobile money from customers on a personal wallet, group contributions for funerals or weddings. A personal account behaving like a business is worth a conversation anyway — see I3.
Data it needs: counterparty for each incoming transaction. Distinct counts use an efficient estimate, so very large numbers are approximate but reliable for thresholds.
D2 Fan-out: one account to many recipients
Section titled “D2 Fan-out: one account to many recipients”What it looks for: one account sending money to an unusually large number of different recipients — distributing proceeds, or paying a mule network.
Example
With min_distinct_recipients 15 and window 3 days:
- ✅ Fires: a wallet that received GHS 40,000 yesterday sends between GHS 1,500 and GHS 3,000 to 19 different wallets.
- ❌ Does not fire: a customer paying 4 different bills.
Parameters
| Parameter | Meaning | How to tune |
|---|---|---|
min_distinct_recipients |
Number of different recipients that triggers the rule | Higher for businesses paying staff or suppliers |
window |
The period | Hours to days |
Mode and pack: post-event or scheduled.
False positives: payroll, bulk disbursements by NGOs and businesses. Exclude business segments or suppress known disbursement accounts with a documented reason.
Data it needs: counterparty for each outgoing transaction.
D3 Fan-in followed by fan-out (mule)
Section titled “D3 Fan-in followed by fan-out (mule)”What it looks for: the full mule pattern: an account that collects from many senders and then pays out to many recipients within days, keeping little. D1 and D2 each see half of the picture; D3 sees both, in that order.
Example
With the defaults (window_hours 72, in_senders 10, out_recipients 10, min_in_amount 5,000,
min_out_ratio 0.7):
- ✅ Fires: a wallet receives GHS 18,000 from 23 senders on Monday and Tuesday, then sends GHS 16,500 to 14 different wallets on Wednesday: 92% of the inflow.
- ❌ Does not fire: the same inflow, followed by payments to 3 people (fewer than
out_recipients). - ❌ Does not fire: payments out that happened before the first payment in: only payments out after the money started arriving count.
Parameters
| Parameter | Meaning | How to tune |
|---|---|---|
window_hours |
The period covering both, in hours (1 to 720) | 72 hours catches fast mules; longer catches slower ones but with more noise |
in_senders |
Different senders, at least | Lower than D1’s threshold, because combined with fan-out it is far more suspicious |
out_recipients |
Different recipients afterwards, at least | As above |
min_in_amount |
Money in over the window at or above (base currency) | |
min_out_ratio |
Money out as a share of money in (0.7 = 70%); up to 2 |
Mode and pack: scheduled, every day at 02:00 (daily 02:00), in core 1.1.0. Once it has
flagged an account, it doesn’t flag it again until a whole window has passed.
False positives: collection and disbursement accounts that work this way by design, such as a church or an association collecting dues and paying suppliers. Use a documented suppression.
Data it needs: completed transactions with an account and a counterparty, in both directions.
D4 Circular flow
Section titled “D4 Circular flow”What it looks for: money that travels through a chain of accounts and comes back to where it started (A → B → C → A) with roughly the same amount. Circular flows are used to create fake turnover (to make a business look bigger, or to qualify for credit) and to blur the origin of money.
Example
With the defaults (window_hours 168, min_hops 3, max_hops 5, amount_tolerance 0.1,
min_amount 5,000):
- ✅ Fires: account A sends GHS 80,000 to B on Monday; B sends GHS 78,000 to C on Tuesday; C sends GHS 75,000 back to A on Thursday. Three hops, each within 10% of GHS 80,000, all within a week.
- ❌ Does not fire: the same loop over three weeks (outside
window_hours). - ❌ Does not fire: A sends GHS 80,000 to B and B sends GHS 20,000 back (outside the tolerance, and only 2 hops).
Parameters
| Parameter | Meaning | How to tune |
|---|---|---|
window_hours |
The whole loop must complete within, in hours (1 to 336) | 168 is a week |
min_hops |
Hops, at least: 2 means straight back (A → B → A) | 3 leaves out simple refunds and repayments |
max_hops |
Hops, at most, up to 6 | Longer chains find more but cost more to search |
amount_tolerance |
Each leg within this share of the first (0.1 = 10%) | Allows for fees and skimming |
min_amount |
Each leg at or above (base currency) | Small payments are left out of the search entirely |
min_hops can’t be more than max_hops: saving such a version is refused with
params.min_hops can’t be more than params.max_hops.
How the search works. Each completed payment of at least min_amount is a step from one party
to another. When the counterparty’s account number is one of your own accounts, the step leads
to that account, so a loop through several of your customers joins up. Each leg must be later than
the last, within window_hours of the first, and within amount_tolerance of the first leg’s
amount. To keep the search fast, accounts that paid more than 25 different counterparties in
the window (billers, merchants, settlement and payroll accounts) aren’t walked through, and a
path never visits the same account twice. The alert shows the shortest loop found, its path, and
how many loops start from the account.
Mode and pack: scheduled, every day at 03:00 (daily 03:00), in core 1.1.0.
False positives: treasury sweeps between a group’s own companies, and loans repaid through intermediaries. Check who owns the accounts in the loop.
Data it needs: completed transactions with an account and a counterparty, and counterparty account numbers that can be matched to your own accounts.
D5 Shared device, phone or ID across many customers
Section titled “D5 Shared device, phone or ID across many customers”What it looks for: one phone, device or identity document linked to many different customers. A single handset logging into 15 wallets usually means one person is operating them all — a mule herder, an account farm, or synthetic identities.
Example
With max_customers_per_device 3 and window 30 days:
- ✅ Fires: the same device fingerprint is used to sign in to 9 different customers’ wallets in a month.
- ❌ Does not fire: a couple sharing one phone (2 customers).
Parameters
| Parameter | Meaning | How to tune |
|---|---|---|
max_customers_per_device |
How many customers may share one device or identifier | Agents’ and shop devices legitimately serve many customers — exclude agent and POS devices |
window |
The period | Weeks |
Mode and pack: real-time (to stop a new link as it happens) or scheduled. The device
part ships in the fraud pack; shared phone and ID numbers are planned.
Data it needs: device_fingerprint on transactions and account events, the same value
for the same device on both. The customer’s page shows each device’s customer count under
Digital footprint.
D6 Counterparty linked to prior confirmed case
Section titled “D6 Counterparty linked to prior confirmed case”What it looks for: a transaction with a counterparty that appeared in a previous case closed as a confirmed problem, for example the account that collected money in a confirmed scam. Once you have caught one part of a network, D6 helps you find the rest.
Example
With the defaults (lookback_days 365, dispositions true_positive_no_report, report_filed and
customer_exited, min_amount 0):
- ✅ Fires: a customer sends GHS 3,000 to a wallet that was the subject of a case closed as report filed four months ago.
- ✅ Fires: a customer receives money from an account number that belongs to one of your own customers whose case was closed as customer exited last month.
- ❌ Does not fire: a payment to a counterparty whose case was closed as a false positive.
Parameters
| Parameter | Meaning | How to tune |
|---|---|---|
lookback_days |
How recently the case must have been closed, in days (1 to 3,650) | Months to years |
dispositions |
Which case outcomes count as confirmed: true_positive_no_report, report_filed, customer_exited |
Usually all three |
min_amount |
Amount at or above (base currency) | 0 catches every contact |
Which cases count. The latest case closed with one of those outcomes about the counterparty itself, or about your own account or customer behind the same account number. The explanation says which outcome and how many days ago.
Mode and pack: post-event, in core 1.1.0. It doesn’t work in real-time decisions: there,
the case history isn’t looked up and the rule never fires.
False positives: a victim’s legitimate dealings with a counterparty that was itself a victim. Read the earlier case before deciding.
Data it needs: counterparties on transactions, and well-recorded case outcomes. This rule is only as good as how carefully cases are closed; see Choosing a disposition.
D7 Many of our customers paying one counterparty
Section titled “D7 Many of our customers paying one counterparty”What it looks for: one outside party (a wallet or account at another provider) being paid by an unusual number of your customers in a short time. This is the shape of a scam collection point: a fake investment scheme, a “promo winnings” fee, or a mule wallet collecting from many victims. Each victim’s payment looks ordinary on its own account; only by looking from the recipient’s side does the pattern appear.
Unlike most rules, the alert is about the counterparty, not your customer, so all the payments land in one alert your analyst can investigate as a whole.
Example
With min_payers set to 15:
- ✅ Fires: over five days, 17 of your customers each send between GHS 200 and GHS 1,500 to the same Telecel Cash wallet, which none of them had paid before.
- ❌ Does not fire: 40 of your customers pay the ECG prepaid account or a utility company’s collection wallet. Popular, legitimate payees trip it, so see False positives below.
- ❌ Does not fire: the “counterparty” is one of your own monitored accounts (same number). Internal transfers are excluded.
Parameters
| Parameter | Meaning | How to tune |
|---|---|---|
min_payers |
How many different accounts of yours must have paid the same counterparty in 7 days | Look at how many of your customers pay your most popular legitimate payees, and start above that for ordinary wallets |
Mode and pack: post-event, core pack. Ships today.
False positives: utilities, schools, churches, large merchants and popular bill-payment
accounts are paid by many customers for good reasons. D7’s alerts are about the counterparty,
so a customer’s suppression never covers them. For a payee you’ve checked, propose a suppression
with the counterparty as the subject, through the
Suppressions API (see The subject must be the one the rule is about).
Raise min_payers only if your popular legitimate payees make the rule unworkable.
Data it needs: counterparty identifiers on outgoing payments (wallet or account numbers), sent consistently so the same payee is recognised every time. See Sending transactions.
Related
Section titled “Related”- C. Velocity and behaviour change — C5 pass-through, the single-account view of a mule
- B. Structuring and smurfing
- Alerts, cases and escalation — dispositions that feed D6
- Tuning and calibration