We've been operating with a multi-CDP strategy for the past year—primarily Segment for data collection and a secondary platform, mParticle, for specific mobile app activation. While this gives us flexibility, it has created a significant identity resolution challenge. Our downstream data warehouse and activation channels are seeing conflicting user profiles.
The core issue is maintaining a **single source of truth** for user identity across platforms that each have their own internal ID graphs. Blindly forwarding raw events between CDPs creates duplicate, unmerged identities.
Our current architecture uses a central, internal identity service to act as the orchestrator. Here’s the simplified flow:
1. **First-party ID assignment:** All web and mobile properties generate a persistent UUID (`internal_user_id`) stored in a first-party cookie or local storage.
2. **Event enrichment:** Every event sent to *any* CDP is enriched server-side with this `internal_user_id` before forwarding.
3. **Orchestration layer:** Our identity service listens for `identify` calls from both CDPs. It maintains the mapping:
```json
{
"internal_user_id": "123e4567-e89b-12d3-a456-426614174000",
"cdp_profiles": {
"segment": "seg_u_abc123",
"mparticle": "mp_xyz789"
},
"external_ids": {
"email": "user@domain.com",
"google_gaid": "38400000-8cf0-11bd-b23e-10b96e40000d"
}
}
```
4. **Reverse sync:** The identity service can write resolved identity mappings back to each CDP's user profile via their APIs, ensuring some consistency.
Key metrics we monitor in this setup:
* **Identity match rate:** Percentage of events across CDPs containing the valid `internal_user_id`.
* **Graph synchronization latency:** Delay between an identity update in one CDP and its propagation to the other.
* **Conflict rate:** Instances where the same `email` maps to different `internal_user_id`s.
The main trade-off is operational overhead. You're essentially building and maintaining a lightweight identity graph service. The alternative—picking one CDP as the master and having it push identities to the other—often just shifts the problem unless you have a very clear primary/secondary relationship.
I'm interested in hearing from other teams running multi-CDP: how do you handle conflict resolution (e.g., same email, different first-party IDs from different devices) and what's your strategy for handling anonymous-to-known user transitions across these separate systems?
- shift lead out
Your approach with a central orchestrator is the correct foundational move, but the critical vulnerability lies in the mapping's persistence and the sync cadence. That JSON mapping must be a durable, versioned store, not just an in-memory cache, as you've implied.
You mention listening for identify calls, but the real challenge is the bidirectional sync latency between your internal graph and each CDP's native graph. Each CDP will still create its own merged profile, and activation channels consuming directly from mParticle will use its potentially stale or conflicting graph. Your model requires a strict policy that all downstream systems must *only* consume the identity-resolved data from your warehouse, never directly from the CDP's native exports.
Consider formalizing a "golden record" merge process in your warehouse that uses your internal_user_id as the primary key, then pushes *that* consolidated profile back to the CDPs as a source-of-truth update. This closes the loop, though it adds complexity to your renewal negotiations, as you'll need API call commitments for these reverse writes.
PPro
That initial UUID enrichment is the right first step, but you're still relying on the CDPs' own identity merging to behave, which they won't. Your orchestration layer needs to intercept and *rewrite* the calls, not just listen.
The problem is in step 3. If you're just listening, Segment and mParticle are still receiving raw identifiers like email or device IDs and building their own graphs. Your service needs to act as a proxy. It should consume the identify call, use its mapping to find the canonical internal ID, and then send a *new* identify call to each CDP with that internal ID as the primary key, suppressing the original external identifiers. This forces your ID into their systems as the master.
Also, store that mapping in something like a Redis cluster with persistence, not just in the service's memory. You'll need to backfill and reconcile when mappings change.
Automate everything. Twice.