Tort Experts runs a high-velocity intake business: marketing brings in claimants, an intake operation qualifies them, and every qualified case must be delivered — record, documents, and all — to one of eight or more partner law firms, each running its own case-management system with its own API, its own authentication scheme, and its own rules about how documents arrive. Salesforce is the hub. The partner systems are the spokes. And the entire revenue model lives in the handoff.
In a referral business, a case that qualifies but never lands in the partner's system isn't a data bug. It's revenue that was earned and then quietly thrown away.
This article walks through the integration architecture we built and operate for that distribution problem. The numbers come from our own audit of the codebase — roughly ten distinct integration service classes, the two largest at 943 and 836 lines, inside an org with 105 project Apex classes, 179 flows, and 88 custom objects. Partner firms are anonymized throughout; the class names in the samples are renamed for the same reason. Everything else is the real design.
The business shape: one hub, eight-plus destinations
From a sales-operations standpoint, this is a routing and fulfillment problem wearing an integration costume. Intake qualifies a case; campaign economics decide which firm it belongs to; and then the system has to fulfill that decision against a heterogeneous set of receivers. One firm exposes a modern REST API with OAuth. Another wants a legacy endpoint with static token auth. One accepts documents inline; another requires files to arrive via a separate upload channel. None of them coordinate with each other, and none of them will change their intake spec because your CRM would prefer it.
The naive build — one big "send to partner" method with a switch statement per firm — survives the first two destinations and then starts to rot. Every new firm adds branches to code that already works, which means every new firm risks breaking delivery to the firms that came before it. At 8+ partner-firm integrations, that approach isn't just ugly. It's an outage generator.
The waterfall design: per-destination services, shared discipline
The architecture that holds up is a waterfall: the qualified case cascades from the hub outward through a per-destination service layer, and every layer is explicit about its one job.
| Layer | Component | Job |
|---|---|---|
| Decision | Flows + routing logic on the case record | Decide which firm gets the case, and when it's ready |
| Translation | Per-firm DTO classes | Reshape the Salesforce record into that firm's exact payload |
| Transport | Per-firm Apex service classes + Named Credentials | Authenticate, call out, interpret that firm's responses |
| Documents | ContentDocumentLink triggers + signed-URL uploads | Move files through the channel each firm actually accepts |
| Recovery | Send/resend framework, resend flows, operator LWC | Make every failed delivery visible and re-runnable |
Each partner firm gets its own service class and its own DTO — a data-transfer object that does nothing but translate the Salesforce case into that firm's payload shape. The service class owns the callout, the response handling, and nothing else:
public with sharing class PartnerFirmAApiService {
public static SendResult sendMatter(Id caseId) {
// 1. Translate: one DTO per destination, nothing shared
PartnerFirmAMatterDTO dto = PartnerFirmAMatterDTO.fromCase(caseId);
// 2. Transport: Named Credential owns auth + endpoint
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:Partner_Firm_A/matters');
req.setMethod('POST');
req.setBody(JSON.serialize(dto));
// 3. Every attempt is logged; the caller decides retry
return SendResultLogger.record('PartnerFirmA', caseId,
new Http().send(req));
}
}Two configuration decisions carry a disproportionate share of the reliability. First, Named Credentials hold every endpoint and secret, so no API key ever appears in Apex, debug logs, or version control — and switching a firm between their sandbox and production intake is a setup change, not a deployment. Second, Custom Metadata records drive per-firm behavior: which auth flavor, which environment, which document rules, whether the integration is live or paused. When a partner firm changes an intake requirement — and they do — the answer is usually a metadata edit, not a code release.
Document fan-out: files are half the delivery
A mass-tort case isn't delivered when the JSON lands; the partner firm needs the retainer, the intake questionnaire, the supporting records. Files are where multi-system distribution usually gets sloppy, because every destination handles them differently and Salesforce's file model (ContentDocument/ContentVersion/ContentDocumentLink) is nobody's favorite API.
In this build, ContentDocumentLink triggers watch for files attached to a case in a deliverable state, and the document pipeline moves them through signed AWS URLs — the file goes to object storage, and the partner system receives a time-limited, pre-authorized URL rather than a raw attachment. That keeps large files out of callout payload limits, keeps documents out of email, and means an expired link fails closed instead of leaking.
Just as important: document delivery got its own operator tooling. A Lightning Web Component on the case record shows which files went to which firm, with what result, and lets an intake operator re-send a specific document without engineering help. The send/resend framework behind it is roughly ten classes in its own right — because in practice, "the firm says they never got the retainer" is a weekly event, and the difference between a two-minute fix and a two-day investigation is whether the system kept receipts.
Failure recovery is a feature, not an apology
Here is the uncomfortable truth about fan-out integrations: with 8+ independent destinations, something is always briefly broken. A partner's API deploys on a Friday. A token gets rotated without notice. A payload validation rule appears that wasn't in the spec. If your architecture treats failure as exceptional, each of those events silently strands cases — and in a referral business, stranded cases are unbilled revenue with a statute of limitations.
So recovery is designed in as a first-class feature:
- Every send attempt is recorded — destination, timestamp, outcome, response metadata — on objects an operator can see, not in debug logs a developer has to excavate.
- Idempotency keys ride with every payload. The receiving system sees a stable external identifier, so a retry updates the matter it already has instead of creating a twin. Retry without idempotency is just duplicate generation with extra steps.
- Resend is a button, not a ticket. A family of resend flows and the operator LWC let the intake team re-drive a failed delivery — the whole case or a single document — the moment a partner confirms their side is fixed.
- Fire-and-forget is banned. A callout whose result nobody stores is a delivery nobody can prove. Every path returns a result that lands somewhere a human can query.
This is the same philosophy we bring to Salesforce automation generally: the happy path is table stakes, and the system's real quality shows in what happens at 2 a.m. when a partner endpoint starts returning 503s.
What the codebase says about scale
Architecture articles are cheap; codebases don't lie. Our audit of this org found roughly ten distinct integration service classes — one per destination plus the shared send/resend framework — each with its own test class. The two largest partner services weigh in at 943 and 836 lines, which is what a real per-firm integration looks like once you've handled their auth dance, their error vocabulary, their document rules, and the edge cases their documentation forgot to mention. All of it lives inside a working org of 105 project Apex classes, 179 flows, and 88 custom objects that also runs the intake operation itself.
The honest limit worth naming: per-destination services cost more up front than a generic sender. You write more classes, more DTOs, more tests. For two stable destinations, that overhead may not pay for itself. The pattern earns its keep from roughly the third destination onward — exactly where the switch-statement approach starts charging interest.
The extensibility test: what does firm #9 cost?
The cleanest way to judge a distribution architecture is to price the next destination. In this design, onboarding firm #9 means: one new DTO class, one new service class implementing the shared contract, one Named Credential, a handful of Custom Metadata records, and a test class — with zero edits to the eight integrations already in production. The blast radius of the new work is the new work.
Compare that to the monolith, where firm #9 means modifying — and therefore re-verifying — the one class that all eight existing firms depend on. Same feature, radically different risk. If you're evaluating an existing org, this question is a fast diagnostic: ask the team what adding a destination touches. If the answer includes "the code that serves everyone else," the architecture has already told you where the next incident comes from.
Sensitive data in a PHI-adjacent domain
Mass-tort cases carry medical narratives, treatment records, and identity documents, so a legal-intake pipeline inherits healthcare-grade data discipline even though it isn't a covered clinical system. In this build that means minimum-necessary payloads per firm (each DTO sends only what that firm's intake requires), signed and expiring URLs instead of documents in transit as attachments, and delivery logs that capture outcomes and metadata — never the contents of a medical record. We describe this posture as HIPAA-conscious rather than claiming compliance, because compliance belongs to the whole program of agreements, policies, and training around the system — not to any single integration, however carefully built.
Frequently asked questions
What is a Salesforce integration waterfall?
It's our name for a hub-and-spoke distribution architecture: one Salesforce org acts as the system of record, and a family of per-destination integration services delivers records and documents downstream — each destination with its own DTO, auth, endpoint set, and retry behavior. The record cascades outward, but every step is tracked, idempotent, and recoverable, so nothing falls between systems.
Should each downstream system get its own Apex service class?
Yes, when the destinations genuinely differ — different auth schemes, payload shapes, and document rules. A shared generic sender ends up as a wall of if-statements that every new destination makes riskier to change. Separate service classes with a common contract keep each partner's quirks contained, independently testable, and safe to modify without regression-testing every other destination.
Are Named Credentials really better than storing API keys in custom settings?
Materially, yes. Named Credentials keep secrets out of Apex, out of debug logs, and out of version control; they handle the auth handshake for you; and swapping sandbox for production endpoints becomes a configuration change instead of a deployment. Storing bearer tokens in custom settings or, worse, hardcoding them, means every org copy and every debug log is a potential leak.
How do you retry a failed callout in Salesforce without creating duplicates?
Idempotency has to be designed in before retry is safe. Send a stable external identifier — the Salesforce record ID or a dedicated transaction key — with every payload, so the receiving system can treat a redelivery as an update rather than a new record. On the Salesforce side, record every send attempt with its outcome, and drive retries from that log so an operator or a scheduled job can resend deliberately instead of blindly.
Is this HIPAA compliant for cases that include medical records?
Compliance is a property of an organization's whole program — agreements, policies, training — not something any single integration can claim. What we build is HIPAA-conscious: signed, expiring URLs for document transfer instead of email attachments, minimum-necessary payloads per destination, and delivery logs that record outcomes and metadata rather than clinical content.
Bring us the systems your revenue has to cross
GAT Solutions builds revenue-generating systems for healthcare, accounting, and SaaS companies — fast, flawlessly, and powered by AI.
If your Salesforce org has to deliver records and documents to systems you don't control — partners, payers, providers, acquirers — the pattern above is where we'd start. Read more on our Salesforce integrations service and the case intake & management system it powers, or bring us your current distribution map and we'll walk the failure paths with you before you spend a dollar.