If there’s one thing fintech firms have learned the hard way, it’s this:
when your advisors live across three different CRMs, chaos is just one update away.
One team logs notes in Wealthbox. Another tracks deals in Salesforce. Someone still swears by Redtail “because it just works.” Each has its own login, data model, and API quirks — and no matter how disciplined your operations are, the data never truly matches up.
So when INSART’s engineering leadership was asked a hypothetical question — “How would you unify all three systems into a single, self-healing ecosystem?” — we knew the answer wouldn’t be simple. But it would be beautiful.
Here’s exactly how we would do it.
Seeing the Integration Problem for What It Really Is
To most people, integrating CRMs sounds like connecting pipes.
To INSART, it’s closer to diplomacy.
Each CRM speaks its own language, enforces its own laws, and has its own idea of truth. Wealthbox loves simplicity — its REST API is human-readable and friendly, but limited in webhooks. Salesforce is the empire — powerful but bureaucratic, with strict rate limits and authentication gymnastics. Redtail, the veteran, holds years of data but speaks in an older dialect of REST that demands careful polling and session tokens.
Our first goal isn’t writing code — it’s understanding the political landscape of these APIs, because integrations fail not from syntax errors, but from assumptions.
Building the Concept: The Synchronization Fabric
INSART wouldn’t build three point-to-point integrations.
We would build a Synchronization Fabric — a layer that sits above all CRMs and mediates between them.
This fabric behaves like air traffic control for data: every “plane” (update) announces itself — who it is, where it came from, and what it’s carrying. The fabric listens, logs the event, normalizes it into a standard schema, and sends it to every other “airport” that needs it.
A new contact in Wealthbox? That triggers an update in Salesforce and Redtail.
A note edited in Salesforce? It appears in Wealthbox and Redtail moments later.
No duplication, no collisions, no “who overwrote what.”
At its heart, this architecture relies on one thing: identity.
The Unified Identity Graph
Every CRM calls the same client by a different ID.
To solve this, INSART creates a Unified Identity Graph — a database that stores all cross-system references.
U_ID | Wealthbox_ID | Salesforce_ID | Redtail_ID
-------- | ------------- | --------------- | ------------
U1001 | 1723 | 0031x0000ABCDEF | 8410
When Wealthbox reports that contact 1723 changed their phone number, the engine checks the graph: “Ah, that’s U1001.”
It fetches the unified record, applies the update, and propagates it to the other systems using their respective IDs.
No match? The engine attempts to link the new record using heuristic matches — email, name similarity, or external keys. When still unmatched, it generates a new unified ID and logs it for manual review.
This system makes data portable and traceable — something that’s not just “synced,” but governed.
Wealthbox: The Friendly Minimalist
Wealthbox is designed for advisors, not developers. Its API is straightforward but limited — and depending on the customer’s plan, webhooks may or may not be available.
INSART would approach it pragmatically: use webhooks where possible, fall back to polling when necessary.
Here’s a simplified webhook handler:
@app.route('/webhook/wealthbox', methods=['POST'])
def webhook_wealthbox():
event = request.get_json()
signature = request.headers.get("X-WBX-Signature")
verify_signature(event, signature)
normalized = {
"source": "Wealthbox",
"object": event["object"],
"crm_id": event["id"],
"changes": event["changes"],
"timestamp": event["updated_at"]
}
publish_to_stream(normalized)
return {"status": "ok"}
And when webhooks aren’t available:
def poll_wealthbox(last_sync, token):
url = f"https://api.crmworkspace.com/v1/contacts?updated_after={last_sync.isoformat()}"
headers = {"ACCESS_TOKEN": token}
r = requests.get(url, headers=headers)
for c in r.json().get("contacts", []):
publish_to_stream({
"source": "Wealthbox",
"object": "Contact",
"crm_id": c["id"],
"changes": {"email": c.get("email")},
"timestamp": c.get("updated_at")
})
Simple, robust, repeatable.
The goal isn’t fancy code — it’s trustworthy repetition.
Salesforce: The Powerhouse
Salesforce offers something neither Wealthbox nor Redtail does: Change Data Capture.
This means our engine doesn’t have to guess what changed — Salesforce tells us.
INSART would subscribe to the /data/ContactChangeEvent channel, listen via WebSockets, and translate those events into the same normalized event schema used elsewhere.
const client = new faye.Client(process.env.SFCDC_URL);
client.addExtension({
outgoing: (message, callback) => {
message.ext = { replay: -1, authToken: process.env.SF_TOKEN };
callback(message);
}
});
client.subscribe('/data/ContactChangeEvent', (msg) => {
const normalized = {
source: 'Salesforce',
object: 'Contact',
crm_id: msg.ChangeEventHeader.recordIds[0],
changes: msg.ChangeEventHeader.changedFields,
timestamp: msg.ChangeEventHeader.commitTimestamp
};
publish_to_stream(normalized);
});
This gives us real-time accuracy and no wasted polling bandwidth. Salesforce becomes the “source of gravity” for enterprise data, and our system treats it accordingly.
Redtail: The Veteran with Secrets
Redtail’s API isn’t young, but it’s surprisingly deep. The catch: its webhook support is limited, and most production environments rely on polled deltas.
INSART would implement adaptive polling — smart enough not to overload, yet responsive to new changes.
def fetch_redtail_updates(last_sync):
url = f"https://api.redtailtechnology.com/v1/contacts?updatedSince={last_sync.isoformat()}"
headers = {"Authorization": f"Bearer {API_KEY}", "User-Token": USER_TOKEN}
response = requests.get(url, headers=headers)
for contact in response.json()["contacts"]:
normalized = {
"source": "Redtail",
"object": "Contact",
"crm_id": contact["id"],
"changes": {
"first_name": contact["FirstName"],
"last_name": contact["LastName"],
"email": contact["EmailAddress"]
},
"timestamp": contact["ModifiedDate"]
}
publish_to_stream(normalized)
Even when polling, Redtail remains predictable — and INSART’s engineers would make sure it’s treated as a first-class participant in the sync network.
Conflict Resolution: The Art of Keeping the Truth Intact
Now comes the tricky part.
What happens when Wealthbox says the phone number is “555-0199” and Salesforce says “555-0200”?
INSART’s philosophy: data is a conversation, not a dictatorship.
Each field in the unified record keeps a memory — its last update timestamp and source. When a new change arrives, the system compares times. The latest one wins. If timestamps are equal, it defers to system priority (Salesforce over Wealthbox, Wealthbox over Redtail, unless business logic says otherwise).
def merge_field(unified_field, inbound_value, inbound_ts, inbound_source):
if inbound_ts > unified_field["updated_at"]:
unified_field["value"] = inbound_value
unified_field["updated_at"] = inbound_ts
unified_field["source"] = inbound_source
return unified_field
This logic ensures data consistency without overwriting valid truth.
Writing Back: Completing the Circle
When all systems agree on the unified record, the fabric writes updates outward.
def update_salesforce(sf_id, data, token):
url = f"{SF_BASE}/services/data/v60.0/sobjects/Contact/{sf_id}"
headers = {"Authorization": f"Bearer {token}"}
r = requests.patch(url, headers=headers, json=data)
if r.status_code == 404:
requests.post(f"{SF_BASE}/services/data/v60.0/sobjects/Contact/", headers=headers, json=data)
def update_wealthbox(contact_id, data, token):
requests.patch(
f"https://api.crmworkspace.com/v1/contacts/{contact_id}",
headers={"ACCESS_TOKEN": token},
json=data
)
def update_redtail(contact_id, data, api_key, user_token):
requests.patch(
f"https://api.redtailtechnology.com/v1/contacts/{contact_id}",
headers={"Authorization": f"Bearer {api_key}", "User-Token": user_token},
json=data
)
Each outbound call carries an idempotency key to prevent loops.
Each change carries a sync_origin flag to ensure a write from Wealthbox doesn’t echo back into itself.
At scale, this pattern behaves almost organically — systems talk, listen, and adjust in harmony.
Monitoring the Orchestra
Even the most elegant architecture can’t be trusted blindly. INSART would wrap this entire system with observability: Prometheus metrics, Grafana dashboards, structured logs with full payload diffs, and an audit trail database where every event (before and after merge) is archived.
That way, compliance teams can ask, “Who changed this field, and where did it come from?” — and we can answer in milliseconds.

The Result: A Symphony of Systems
In the end, the integration wouldn’t just make CRMs talk — it would make them sing together.
Salesforce’s analytics would use Wealthbox’s immediacy.
Wealthbox’s notes would enrich Redtail’s compliance records.
And Redtail’s legacy data would give Salesforce the historical depth it never had.
INSART’s philosophy isn’t to glue systems together.
It’s to orchestrate them.
That’s what true fintech integration looks like — not just data movement, but data harmony.




