Change Data Capture publishes every field change on a monitored object to an event bus the moment it happens, and those events are immutable once sent. Masking a sandbox rewrites the records sitting in your database, but it can't reach into the event bus and rewrite messages that already went out. If a subscriber connected between refresh completion and the moment your masking job finished, it likely received real names, emails, and phone numbers as change events, and no amount of after-the-fact masking pulls those back.
What Change Data Capture actually publishes
CDC isn't a report or a query. It's a standing subscription mechanism: once you enable it for an object in Setup, every insert, update, delete, and undelete on that object fires a change event onto the org's event bus in near real time. Any client authenticated to that org and subscribed to the relevant channel receives the event, typically within seconds.
The event payload includes the record's current field values along with a header describing what changed. For a Contact or Lead object, that means the email address, phone number, mailing address, and any custom PII fields ride along in the payload exactly as they existed at the moment of the DML operation. There's no filtering, no redaction, no awareness that the org is a sandbox rather than production.
Most Salesforce teams think of CDC as an integration tool, something MuleSoft or a custom Heroku app uses to sync data downstream. That's correct, but it's incomplete. CDC is also a live PII export mechanism running silently in parallel to whatever your masking tool is doing to the underlying tables.
The retention window masking can't touch
Change events don't disappear after they're delivered. Salesforce retains them on the event bus for a defined window, commonly up to 72 hours depending on event type and edition, so subscribers that were briefly offline can catch up by replaying from an earlier replay ID. That's a genuinely useful feature for integration reliability. It's also the reason masking a sandbox database doesn't make the PII problem go away.
Here's the sequence that trips teams up. A full sandbox refresh completes and copies production records, PII included, into the new sandbox. Some automated process, workflow, flow, or a scheduled Apex job fires DML on Contact or Account records before your masking tool has finished its pass. Each of those touches generates a change event, and that event carries the real, unmasked field values because masking hadn't run yet.
Your masking job then completes and every record in the database now shows a fake email and a fake phone number. Query the object through the UI or the API and everything looks clean. But the change events published during that earlier window are still sitting on the event bus, replayable for up to three days, carrying the original real values. Anything that connects to that channel and replays from an earlier point gets the leak, regardless of what the database looks like today.
Who's listening after your refresh
The subscriber list is usually longer than admins expect. Sandboxes get wired into CI/CD pipelines, integration middleware, and test harnesses for exactly the same reasons production does, and nobody disables those connections just because the org is non-production.
| Subscriber type | Why it's connected to sandbox CDC |
|---|---|
| Middleware platforms (MuleSoft, Boomi, Informatica) | Integration tests validate sync logic against sandbox data before promoting to production |
| Custom CometD clients | Developers debug event-driven Apex or LWC behavior against live event streams |
| External data warehouses | ETL jobs replicate sandbox changes to a staging schema for analytics testing |
| Other Salesforce orgs | Cross-org sync patterns subscribe one org's CDC channel from another for feature parity testing |
Every one of those connections is a legitimate business reason to keep CDC enabled in a sandbox. None of them care whether the payload they just received contains a real customer's phone number. That's the disconnect: CDC subscribers exist to move data reliably, not to enforce privacy policy, and they'll happily replicate whatever the event bus hands them.
Why standard masking tools miss this entirely
Most masking products, including plenty of well-regarded ones, operate exclusively against the persisted database state. They query records, generate synthetic replacements, and write them back through the API or bulk load. That approach is correct and necessary, but it treats the Salesforce org as a static data store rather than a platform that's also actively streaming events in the background.
Change Data Capture configuration lives under Setup, separate from object permissions or field-level security, and it's easy for a masking rollout to never touch it. Nobody on the security review checklist typically asks, is CDC enabled on this object, and if so, was it publishing events before the mask ran. It falls into the same category as debug logs and platform cache: technically part of the org, functionally invisible to a tool that only thinks in terms of records and fields.
I'd argue this is the bigger issue with CDC specifically, compared to something like debug logs. Logs get purged on a schedule and usually require someone to go looking. CDC events get actively pushed to external systems the moment they're created, which means the leak doesn't wait for anyone to find it. It ships itself.
Locking this down before you mask
The fix isn't complicated, but it has to happen in the right order. Disable Change Data Capture on any object carrying PII before the sandbox refresh completes and before masking begins, not after. If subscribers genuinely need CDC for sandbox testing, re-enable it only once masking has finished and verified, so the first events those subscribers ever see already contain fake data.
Second, treat CDC subscriptions as part of your masking scope document, not an afterthought. When you inventory which objects and fields need masking, add a column for whether CDC is enabled on that object and who's subscribed. That turns a platform feature nobody thinks about into a checklist item somebody owns.
Third, if a subscriber connected during the exposure window, that's an incident, not a shrug. Replay IDs make it straightforward to confirm whether a given client actually pulled events during the gap between refresh and masking completion. Assume it did until you've checked, and notify whoever owns that downstream system.
MaskEzee's sandbox jobs check CDC configuration on every masked object as part of the pre-refresh scope check, and we flag any object where change events were live before the mask completed. It's a small check against a platform feature most teams never think to audit, but it's the difference between a clean masking log and an unnoticed export of real customer data to three external systems you forgot were subscribed.
Frequently Asked Questions
Does masking a Salesforce sandbox also clean up Change Data Capture events?
No. Masking rewrites the field values stored in the object's records, but change events already published to the event bus are immutable and stay there for their retention window. If an event went out before masking ran, it still carries the original real value regardless of what the database shows afterward.
How long are Change Data Capture events retained on the event bus?
Retention windows vary by event type and Salesforce edition, but standard and high-volume change events can be retained and replayable for up to 72 hours. Any subscriber that reconnects within that window and replays from an earlier replay ID can pull events that were published before masking completed.
Which Salesforce objects are most at risk from this CDC gap?
Any object with Change Data Capture enabled that also carries PII, most commonly Contact, Lead, Account, Case, and custom objects storing customer identifiers. If those objects have active CDC subscriptions and any DML runs against them before masking finishes, real values go out through the event stream.
Should I disable Change Data Capture on every sandbox permanently?
Not necessarily, since some teams rely on it for integration and event-driven testing. The safer approach is disabling CDC on PII-bearing objects before a refresh, letting masking complete, then re-enabling it once the underlying data is fake.
Can external integration platforms like MuleSoft receive real PII from a masked sandbox?
Yes, if they were subscribed to a Change Data Capture channel and connected during the window between sandbox refresh and masking completion. The platform will have received and potentially cached or forwarded the original field values before the mask ever ran.