A cross-product fraud/risk detection platform on Salesforce Data Cloud, Apex, LWC, and Agentforce
Unifying banking and insurance data for a single customer to surface risk signals that neither dataset reveals alone.
Note
This is a portfolio project built to demonstrate real, working Salesforce architecture โ not a tutorial walkthrough. Every number, threshold, and finding documented below was derived from actually running the pipeline against real (anonymized, public) data. See Known Limitations for an equally honest account of what isn't finished.
- What This Demonstrates
- Architecture
- Tech Stack
- Real Engineering Stories
- Screenshots
- Getting Started
- Testing
- Adapting This Project to Your Own Dataset
- Known Limitations
|
๐ง Data Cloud Engineering Data Streams, DLOs, DMOs, Identity Resolution, Calculated Insights (Visual Builder), real statistical thresholds derived via Query Editor โ๏ธ Apex Bulk-safe service classes, Invocable Actions, dependency-injection test seams for external-object dependencies, 100+ tests ๐ฅ๏ธ Lightning Web Components
Five components across three personas, real-time CDC subscription via |
๐ค Agentforce A genuinely grounded AI agent โ every fact it states comes from a custom Apex action, never invented โ including a confirmation-gated write action ๐ Enterprise Access Architecture Three fully separate Lightning Apps, Profiles, and Permission Sets, each verified with an independent, non-impersonated login ๐ CI/CD Two independent, real pipelines (GitHub Actions + Copado), both enforcing test coverage gates |
flowchart LR
subgraph Sources["๐ฅ Data Sources"]
A[Banking Transactions CSV]
B[Insurance Claims CSV]
C[Party Identification CSVs]
end
subgraph DataCloud["โ๏ธ Salesforce Data Cloud"]
D[Data Streams]
E[DLOs]
F[DMOs]
G[Identity Resolution]
H[Calculated Insight<br/>4 risk-flag rules]
end
subgraph Automation["โก Automation Layer"]
I[Data Cloud-Triggered Flow]
J[RiskCaseService.cls]
K[Risk_Signal_Change__e<br/>Platform Event]
L[Change Data Capture]
end
subgraph UI["๐ฅ๏ธ Real-Time UI โ 3 Personas"]
M[Risk Case Queue]
N[Customer Profile]
O[Branch Agent Indicator]
P[Compliance Dashboard]
Q[Risk Signal Feed<br/>lightning/empApi]
end
subgraph AI["๐ค Agentforce Agent"]
R[Explain a Case]
S[Query the Queue]
T[Resolve a Case<br/>confirmation-gated]
end
A --> D
B --> D
C --> D
D --> E --> F --> G --> H
H --> I --> J --> K
J -.-> L -.-> Q
J --> M
J --> N
J --> O
J --> P
J -.-> R
J -.-> S
J -.-> T
| Layer | Technology |
|---|---|
| Data ingestion & unification | Salesforce Data Cloud (Data Streams, DLOs, DMOs, Identity Resolution) |
| Business logic | Data Cloud Calculated Insight (Visual Builder), Apex |
| Automation | Data Cloud-Triggered Flow, Platform Events, Change Data Capture |
| UI | Lightning Web Components (LWC), Lightning App Builder |
| AI | Agentforce (custom Invocable Apex Actions as grounding) |
| Access control | Lightning Apps, Profiles, Permission Sets |
| CI/CD | GitHub Actions (JWT auth), Copado |
| Planned | MuleSoft Anypoint (ingestion automation โ status below) |
This project surfaced several genuine, non-trivial problems โ documented honestly rather than glossed over. Click any story to expand.
๐ฏ A risk rule with zero real hits
Rule 4 (repeat-claims + banking flag) was independently confirmed via raw SQL to occur 0 times in the actual data โ a genuine finding from real data, not a bug. Kept in the model and documented as such, rather than quietly removed to hide an inconvenient result.
๐งฎ A platform SQL dialect restriction, found mid-build
MAX couldn't nest inside CASE in Data Cloud's Calculated Insight engine ("Aggregation function MAX cannot be nested with CASE function"). Switched to a SUM + post-aggregate cap pattern instead โ same logical result, different mechanism.
๐ช A Windows long-path failure
DevOps Data Kit's auto-generated field-mapping filenames exceeded Windows' 260-character path limit on first retrieve attempt. Resolved via git config --global core.longpaths true (user-level, no admin rights needed).
๐ A five-attempt debugging journey to a working Data Cloud-Triggered Flow
Calculated Insight custom output fields are invisible to a generic Record-Triggered Flow โ confirmed across a generic trigger object, Get Records on sync AND async paths, and direct $Record access, all dead ends. The fix: a dedicated "Data Cloud Triggered Flow" type exposes the real fields directly โ not a workaround, the actual correct flow type.
๐ A real architecture bug: the wrong customer key
Risk_Case__c.Customer_Key__c was silently populated with a Data Cloud-internal Unified Individual Id instead of the real, raw source key โ two genuinely different identifiers. Found while scoping a UI feature, root-caused via the Identity Resolution bridge table, and fixed with a resolver class plus a one-time data-migration script for already-created records.
๐ A genuine Apex coverage crisis (96% โ 47%)
Adding Data Cloud-integrated classes (which can't be unit-tested the normal way โ no seeding external objects in tests) tanked org-wide coverage below Copado's 75% deployment gate. Fixed by separating untestable external-object queries into thin wrapper methods and extracting all real business logic into pure, fully-testable functions โ not by lowering the bar or writing shallow tests.
๐ค A three-bug Agentforce debugging saga
- A generic, undiagnosable activation failure with zero detail in the Problems panel or browser console
- A corrupted action registration that silently refused to save field descriptions โ required a full delete-and-rebuild
- A schema failure specific to returning a nested
List<CustomApexClass>from an Invocable Action โ fixed by flattening to plain primitives
All three found, root-caused, and fixed โ including confirming the third one also silently broke Salesforce's own built-in AnswerQuestionsWithKnowledge action, proving it was a platform quirk, not project-specific code.
๐ A documented Salesforce platform requirement, confirmed via Salesforce Help
Change Data Capture subscription needs View All Records on the object โ plain Read access is not sufficient, even though Read is sufficient for normal SOQL. Diagnosed by temporarily instrumenting the LWC's error handler to reveal the real CometD rejection (403: User not allowed to subscribe CDC without required permissions), since the default error surfaced was generic and unhelpful.
๐ซ A license-type discovery
A read-only persona (Compliance Officer) is better served by a Salesforce Platform license than a full Salesforce license โ discovered while working around an exhausted Developer Edition license pool, but genuinely the more architecturally correct choice for a custom-object-only, read-only role, not just a workaround.
Tip
Full technical detail on all of these โ including the exact debugging steps, error messages, and code โ is in the project's internal Build Guide.
|
Identity Resolution โ 16,410 Unified Profiles Real Identity Resolution output: 16,410 unified customer profiles matched from 22,920 source records across banking and insurance โ the foundation everything downstream is built on. |
|
|
Risk Case Queue Prioritized, color-coded severity badges โ the automation pipeline's output, ready for an analyst to act on. |
Customer Profile Unified banking + insurance view on a single Risk Case โ real account balance, real policy detail, real claim date. |
|
Agentforce Agent โ Live Answer A real, unedited conversation โ every fact grounded in actual data, nothing invented. |
Compliance Dashboard Real status breakdown and audit trail โ the reporting layer, working against live data. |
|
Risk Signal Feed โ Live CDC Event
The moment a real |
|
|
Copado CI/CD โ Successful Deployment A second, independent CI/CD pipeline (alongside GitHub Actions), triggered automatically via a Git webhook and deploying successfully. |
|
- Salesforce Developer Edition org with Data Cloud (Data 360) enabled
- Salesforce CLI (
sf) - Node.js (for the CI/CD tooling)
- Git
git clone https://github.com/saikrishnaalley/bancassurance-risk-platform.git
cd bancassurance-risk-platform
sf org login web --alias riskorg
sf project deploy start --source-dir force-appWarning
Data Cloud components (Calculated Insight, Data Cloud-Triggered Flow, Agentforce agent metadata) are excluded from automated deploy via .forceignore, since these are UI-managed, already-active components that can't be redeployed via a generic Metadata API push once live. See the Build Guide for the manual setup steps for these pieces.
sf apex run test --test-level RunLocalTests --code-coverage --result-format human --wait 20| Metric | Result |
|---|---|
| Tests | 100+ |
| Pass Rate | 100% |
| Org-Wide Coverage | 88%+ |
This project is built around two specific real, anonymized public datasets (Kaggle Credit Card Fraud Detection and Vehicle Insurance Claims Fraud Detection). If you want to fork this repo and adapt it to your own data, here's an honest breakdown of what's reusable as-is versus what you'll need to rebuild.
These pieces are dataset-agnostic and will work unchanged regardless of what data you bring:
- Custom objects โ
Household__c,Financial_Account__c,Insurance_Policy__c,Risk_Case__c RiskCaseService.clsโ the Apex service that upserts aRisk_Case__cand publishes aRisk_Signal_Change__eevent. It only expects a customer key, a numeric score, and an optional reason string โ it has no knowledge of what generated the score.Risk_Signal_Change__ePlatform Event- GitHub Actions CI/CD workflow (
.github/workflows/deploy.yml) โ just needs your own org's JWT credentials wired up as repo secrets (SF_CONSUMER_KEY,SF_JWT_KEY,SF_USERNAME,SF_INSTANCE_URL) - The overall architecture pattern: Data Streams โ DLOs โ DMOs โ Identity Resolution โ Calculated Insight โ Apex service โ Platform Event โ CDC
Be upfront with yourself that this is the real work, not a find-and-replace exercise:
- Your own source files. Obviously, you'll bring your own CSVs (or connect a live source).
- Data Stream field mappings. The mappings from source columns to DMO fields are tied to this project's exact column names (
Amount,Days_Policy_Claim,AddressChange_Claim, etc.). You'll remap every field to whatever your dataset actually has. - DMO structure.
Banking_Transaction_DMOandInsurance_Claim_DMOreflect this project's specific schemas. Expect to redesign your DMOs around your own data's shape. - A shared join key across your data sources. This project uses an injected synthetic
Customer_Key__cto link banking and insurance records for the same fictional customer, since the two source datasets don't share a natural key. If your real data already has a genuine shared identifier (a real customer ID, SSN, email, etc.), you likely don't need this workaround โ use Identity Resolution's native matching instead. If it doesn't, you'll need an equivalent preprocessing step to establish one. - The four risk-flag rules themselves. This is the part most likely to be underestimated. The rules in this project (
Amount โฅ 1065.67,PastNumberOfClaims = 'more than 4', etc.) are semantically specific to fraud patterns in banking + auto insurance. If your domain is different (e.g., healthcare claims, retail returns fraud, loan default risk), you need to design new rules from scratch based on what risk actually looks like in your data โ not just relabel these ones. - The actual threshold values. Even if your domain is conceptually similar (e.g., also banking + insurance fraud), don't reuse these specific numbers. They were derived by running real percentile and frequency queries (via Data Cloud's Query Editor) against this project's specific ingested data โ see the Build Guide for the exact queries used. Re-run that same calibration process against your own data; don't assume these numbers transfer.
- Set up your own Salesforce Developer Edition org and Data Cloud instance.
- Ingest your own data as Data Streams, and map to your own DMOs.
- Set up Identity Resolution using whichever join key strategy fits your data (native match or a synthetic key, per point 4 above).
- Verify unification worked (check unified profile counts, spot-check a few records) before building anything downstream.
- Design your own risk-flag rules based on what your data and domain actually indicate as risk โ don't skip straight to copying this project's Calculated Insight logic.
- Calibrate real thresholds against your own data using percentile/frequency queries, the same way this project did.
- Build the Calculated Insight in Visual Builder using your new rules and thresholds.
- Deploy the reusable pieces (custom objects,
RiskCaseService.cls, the Platform Event, the CI/CD workflow) largely as-is, adjusting only field-level references if your object model differs.
Don't reuse the raw thresholds, the specific rule logic, or the DMO field mappings verbatim โ these are load-bearing for this dataset's actual statistical distribution and domain meaning. Treating them as universal constants would produce a Calculated Insight that looks like it works but isn't grounded in your actual data.
Documented honestly, not hidden:
- Banking-side chronology:
Financial_Account__crecords are intentionally not date-ordered. The source dataset's only time field is seconds-elapsed-since-first-transaction, not a real calendar timestamp โ adding a fabricated date would misrepresent the data. The insurance side genuinely is chronologically ordered, using a real date derived from the source data's month/week/year fields. - Product-line reporting: not built. Every
Risk_Case__cin this platform is inherently cross-product by design โ each of the four risk rules requires both a banking AND an insurance signal to fire โ so a single-value "product line" field would carry no real reporting signal. - CDC event history: the live Risk Signal Feed component intentionally does not persist history across a page refresh โ it's a real-time proof-of-delivery tool, not an audit log (that role belongs to the Compliance Dashboard, which does persist real data).
๐ MuleSoft status โ genuine partial progress
Anypoint Platform trial signed up, Anypoint Studio installed, a Mule project created with the Salesforce Data Cloud Connector added, and a Connected App built with working, tested OAuth Client Credentials authentication. A Bulk Ingestion flow was started (Scheduler configured) but not finished before development moved to other priorities. See the Build Guide for the exact resume point if this is picked back up.
This is a personal portfolio project. Feel free to fork and adapt โ see Adapting This Project to Your Own Dataset above for an honest breakdown of what transfers and what doesn't.
Built by Sai Krishna Alley as a demonstration of production-grade Salesforce architecture.






