Duplicate rules exist to stop bad data from entering production, but they cause a specific and predictable problem for test data generation: bulk inserts that get silently blocked, partially rejected, or logged as duplicate matches when they shouldn't be. If your org has an Account or Contact duplicate rule set to block, generating a few thousand synthetic records with realistic names, emails, and phone numbers will trigger matches you never intended, because "realistic" and "unique" are not the same thing. Solving this means treating duplicate rules as a schema constraint, not an obstacle to disable and forget.
Why Duplicate Rules and Synthetic Data Collide
Most Salesforce duplicate rules match on a narrow set of fields: Email, Phone, Company plus Last Name, or a fuzzy match on Account Name. Test data generators that pull from name and address pools tend to reuse the same limited set of first names, last names, and area codes across thousands of records. That's fine for volume testing, but it's exactly the pattern a matching rule is built to catch.
The result is a job that looks like it failed for no reason. Records error out with a duplicate rule message instead of a validation rule message, and the stack trace doesn't point to a field mapping problem. Admins who haven't hit this before usually assume the load tool is broken. It isn't. The org is doing exactly what it was configured to do.
I'd argue this is actually a useful signal, not just an annoyance. If your duplicate rule blocks a synthetic data load, it will also block real users entering similar records, and that's worth knowing before go-live rather than after.
Block, Alert, and Report: Three Different Failure Modes
Salesforce duplicate rules run in one of three modes, and each behaves differently under a bulk load.
- Block: the record is rejected outright. In Bulk API v2 this shows up as a row-level error on the job results file, not a hard job failure, so a naive script might not notice thousands of silent rejections.
- Alert: the record saves but a duplicate warning is logged and shown in the UI. Bulk API loads bypass the UI warning, so alerts often go unnoticed until someone opens the record later.
- Report-only (no action): the record saves and the duplicate is tracked in the background for reporting. This mode is nearly invisible to a data generation job, which is exactly why it's the safest default for a test environment that still needs realistic duplicate detection metrics.
Knowing which mode is active on which object is step one. Skipping this check and assuming every rejected batch member is a schema problem wastes hours chasing the wrong root cause.
Designing the Generator Around Matching Fields
The fix isn't turning duplicate rules off during data loads and hoping someone remembers to turn them back on. That approach has burned enough orgs that it deserves to be retired. The better fix is generating data that respects the matching criteria the same way real user input would.
For Contact and Lead records matched on Email, append a job-run identifier or a monotonically increasing counter to the local part of the address: jane.smith+run482@testdata.example. This keeps the domain and format realistic while guaranteeing uniqueness against both the matching rule and any downstream integration that keys off email as a unique identifier.
For Account records matched on fuzzy name similarity, avoid pulling from a name pool of fewer than a few hundred entries when generating thousands of rows. A schema-aware generator that reads the actual matching rule definition, rather than guessing at which fields matter, can widen or salt the generated values specifically on those fields and leave every other field free to repeat. That's a more targeted fix than randomizing every field, which just trades a duplicate problem for a referential integrity problem.
Phone number matching deserves the same treatment. Sequential or randomly generated phone numbers frequently collide on area code plus a short digit range, especially when a generator only cycles through a handful of North American area codes. Widening the number pool, or salting the last four digits with the batch sequence number, removes the collision without making the data look obviously synthetic.
How Bulk API v2 Actually Behaves Here
Bulk API v2 enforces duplicate rules the same way Bulk API 1.0 and the standard UI do; there's no bulk-specific exemption. What differs is visibility. A UI save shows the duplicate warning inline and lets a user decide whether to proceed. A Bulk API v2 job has no interactive step, so a blocked record simply appears as a failed row in the job's error CSV with a DUPLICATES_DETECTED status code.
Salesforce does support an override header, the same allowSave mechanism used by the SOAP and REST APIs, which lets a client explicitly bypass block-mode rules for a specific request while still logging the match for reporting. Using it for test data loads is reasonable in a sandbox specifically built for volume or performance testing, where duplicate detection isn't part of what's being validated. It is a bad idea in a QA or UAT sandbox where duplicate rule behavior is itself part of the test plan, because bypassing the rule there defeats the point of testing against it.
The practical takeaway: decide, per sandbox, whether duplicate rules are a thing you're testing or a thing you're testing around. Those are two different jobs and they call for two different data strategies.
Practical Patterns That Hold Up at Scale
A few patterns consistently work across orgs with active duplicate rules:
| Pattern | When to use it |
|---|---|
| Salt matching fields with a run ID | Repeated load jobs into the same sandbox, where prior runs' data still exists |
| Read the duplicate rule definition before generating | Any org with custom matching rules on Account, Contact, or Lead |
| Switch rule action to Report-only in dedicated data sandboxes | Sandboxes used exclusively for volume, load, or integration testing |
| Use the allowSave override header | One-off large loads where duplicate detection isn't the test target |
| Keep block mode active with widened data pools | QA and UAT sandboxes validating duplicate rule behavior itself |
Notice that only one of these five patterns involves changing the rule's configuration. That's deliberate. Reconfiguring duplicate rules for the sake of a data load introduces drift between sandbox and production metadata, and that drift is the kind of thing that gets missed during a release audit six months later.
Checking Your Data Before You Load It
A schema-aware generation tool that reads org metadata, including active duplicate rules and their matching criteria, can flag likely collisions before the job ever hits Bulk API v2. This matters more as record volume grows: a 500-row test load might get lucky and avoid collisions by chance, but a 200,000-row load into an org with a tight Contact matching rule almost certainly will not.
The cheapest fix is always the one applied before generation, not after failure. Reading the rule, understanding its action mode, and salting the two or three fields it actually cares about takes less time than debugging a job results CSV full of DUPLICATES_DETECTED rows at two in the morning before a release window closes.
Frequently Asked Questions
Do duplicate rules block Bulk API v2 inserts the same way they block manual saves?
Yes. Bulk API v2 respects the same duplicate rule configuration as the standard UI and other APIs, including block, alert, and report-only actions. The difference is visibility: a blocked record in a bulk job shows up as a row-level error in the job's results file rather than an inline warning, so it is easy to miss without checking the CSV output.
Should I disable duplicate rules before loading test data?
Generally no, unless the sandbox is dedicated purely to volume or performance testing where duplicate behavior isn't part of what you're validating. Disabling rules and forgetting to re-enable them is a common source of configuration drift between sandbox and production. A better approach is generating data that respects the matching fields, or switching the rule to report-only for that specific sandbox.
What is the allowSave override header and can I use it with Bulk API v2?
The allowSave override header, also used in SOAP and REST API calls, lets a request bypass a block-mode duplicate rule while still logging the match for reporting purposes. It can be applied to Bulk API v2 jobs and is reasonable for large one-off loads where duplicate detection isn't the focus of the test, but it should be avoided in sandboxes where duplicate rule behavior is itself under test.
Which fields most commonly trigger duplicate rule failures in test data?
Email and Phone on Contact and Lead objects, and fuzzy name matching on Account, are the most common triggers. Generators that pull from small pools of first names, last names, or area codes across thousands of rows will produce matches on these fields even though the underlying data looks realistic.
How can I tell whether a failed bulk load row was rejected by a duplicate rule or a validation rule?
Check the status code in the Bulk API v2 job's error results file. Duplicate rule rejections return a DUPLICATES_DETECTED status, while validation rule failures return a FIELD_CUSTOM_VALIDATION_EXCEPTION or similar message tied to the specific rule. Reviewing the error file before assuming a schema or mapping problem saves significant troubleshooting time.