# Triggering a fraud alert (demo/test guide) The model learned one dominant signature from PaySim: **a `TRANSFER` or `CASH_OUT` that drains an account to exactly zero** — `amount == old balance` and `new balance == 0`. That pattern alone (regardless of the actual amount, RWF 181 or RWF 275,000) is what pushes the fraud probability toward 1.0 and the risk tier to **high**. `PAYMENT`, `CASH_IN`, and `DEBIT` transactions, and any partial (non-draining) transfer, score **low** almost every time — those are the two fraud-prone types identified in `reports/EDA_REPORT.md`. ## 1. Trigger it through the client UI (easiest, no raw PaySim fields needed) The client-facing form (`/client/transactions/new`) only asks for type, amount, destination account, and date — balances are derived server-side from the account's real running balance. To reproduce the fraud pattern: 1. Log in as a client (or have an officer create one via `/officer/clients` first). 2. Note the account's **current balance** shown on `/client` (e.g. RWF 5,000). 3. Go to `/client/transactions/new`. 4. Type: **Transfer** (or **Cash out**) 5. Amount: **the exact current balance** (e.g. `5000.00`) — this drains the account to zero, the same signature real PaySim fraud rows have. 6. Destination: anything, e.g. `C_MULE_1` 7. Submit. Expected result: `risk_tier: "high"`, and the UI shows *"This transaction is being reviewed for your security."* An alert appears immediately on `/officer/alerts` and on the client's own `/client/alerts`. A **partial** transfer (e.g. RWF 2,000 out of a RWF 5,000 balance) stays low-risk — it's the *full drain to zero*, not the size of the transaction, that the model keys on. ## 2. Trigger it directly against `/api/predict` (officer-only, raw PaySim fields) Useful for testing the model in isolation without going through the account-balance flow. Requires an officer session. ```bash curl -X POST http://127.0.0.1:8811/api/predict \ -H "Content-Type: application/json" \ -b "session_token=" \ -d '{ "step": 5, "type": "TRANSFER", "amount": 181.0, "nameOrig": "CFXFRAUD000A", "oldbalanceOrg": 181.0, "newbalanceOrig": 0.0, "nameDest": "CFXFRAUD000B", "oldbalanceDest": 0.0, "newbalanceDest": 0.0 }' ``` Expect `"risk_tier": "high"` with `probability` close to 1.0, and the top SHAP features dominated by the balance-drain signal. ### More fraud-triggering examples (any amount works, as long as it fully drains) | type | amount | oldbalanceOrg | newbalanceOrig | oldbalanceDest | newbalanceDest | |---|---|---|---|---|---| | TRANSFER | 181.00 | 181.00 | 0.00 | 0.00 | 0.00 | | TRANSFER | 2,500.00 | 2,500.00 | 0.00 | 0.00 | 0.00 | | TRANSFER | 50,000.00 | 50,000.00 | 0.00 | 0.00 | 0.00 | | TRANSFER | 275,000.50 | 275,000.50 | 0.00 | 0.00 | 0.00 | | CASH_OUT | 300.00 | 300.00 | 0.00 | 0.00 | 0.00 | | CASH_OUT | 15,000.00 | 15,000.00 | 0.00 | 0.00 | 0.00 | These are drawn straight from `tests/fixtures/labeled_transactions.json` (28 hand-labeled fraud fixtures in total — search for `"expected_label": "fraud"`). ## 3. What does *not* trigger fraud (for contrast) | type | amount | oldbalanceOrg | newbalanceOrig | Expected | |---|---|---|---|---| | PAYMENT | 45.00 | 5,000.00 | 4,955.00 | low | | TRANSFER | 2,000.00 | 5,000.00 | 3,000.00 | low (partial, not a drain) | | CASH_IN | 1,000.00 | 3,000.00 | 4,000.00 | low | | DEBIT | 60.00 | 1,200.00 | 1,140.00 | low | ## Reference - Alert threshold: `FRAUD_ALERT_THRESHOLD=0.5` (probability above this becomes an alert) - Display risk tiers (independent of the alert threshold): **low** < 0.30, **medium** 0.30–0.70, **high** ≥ 0.70 - Full model methodology: `reports/MODEL_COMPARISON_conservative.md`, `reports/EDA_REPORT.md` - Regenerate/inspect the full fixture set: `python -m scripts.generate_fixtures`, then `tests/fixtures/labeled_transactions.json`