Duplicate opportunity records created after API sync in opportunity management

We’re experiencing a critical issue with our opportunity synchronization from our external sales platform to SAP CX via REST API. Despite implementing what we thought was proper external ID mapping, we’re consistently getting duplicate opportunity records created during sync operations.

Our integration runs every 15 minutes and uses the following upsert logic:


POST /sap/c4c/odata/v1/c4codataapi/OpportunityCollection
Header: x-http-method=MERGE
Body: {"ExternalID": "EXT-OPP-12345", "Name": "..."}

The duplicates seem random - sometimes the same opportunity syncs fine, other times it creates a new record instead of updating. This is causing major reporting inaccuracies and our sales team is losing trust in the data. We’ve checked that ExternalID values are unique in the source system, but something in our pre-sync validation or the API call itself must be wrong. Has anyone dealt with similar duplicate creation issues when using upsert operations with external IDs?

Looking at your code snippet, I can see multiple issues that need addressing. Let me walk through a comprehensive solution covering all three critical areas: external ID mapping, proper upsert API usage, and pre-sync validation.

External ID Mapping Configuration: First, verify your ExternalID field is properly configured as an alternative key in SAP CX. Navigate to Administrator → Business Configuration → Opportunity fields and ensure ExternalID has the “Alternative Key” checkbox enabled. Without this, the system won’t recognize it for upsert operations.

Correct Upsert API Implementation: Your current approach using x-http-method=MERGE is outdated for SAP CX 2105. Use this pattern instead:


PATCH /OpportunityCollection('ExternalID')
Content-Type: application/json
{"Name": "Updated Opportunity", "Amount": "50000"}

Or for true upsert behavior, query first then decide:


GET /OpportunityCollection?$filter=ExternalID eq 'EXT-OPP-12345'
// If exists: PATCH with ObjectID
// If not: POST with ExternalID

Pre-Sync Validation Layer: Implement this validation in your middleware before any API call:


// Validation pseudocode:
1. Check ExternalID is not null/empty/whitespace
2. Verify ExternalID matches pattern: ^[A-Z]+-[A-Z]+-[0-9]+$
3. Query SAP CX to check if record exists using ExternalID
4. Log validation results with timestamp and payload hash
5. Only proceed if all validations pass

Additional Recommendations:

  • Enable duplicate check rules in SAP CX for opportunities based on ExternalID
  • Implement idempotency keys in your API requests
  • Set up monitoring alerts when duplicate detection triggers
  • Use batch API operations if syncing multiple opportunities to reduce network overhead
  • Add a reconciliation job that runs daily to identify and merge duplicates based on ExternalID

The combination of proper alternative key configuration, correct API usage patterns, and robust pre-sync validation should eliminate your duplicate creation issues. Monitor your next few sync cycles closely and check that ExternalID is present in 100% of requests.


This draft is based on general SAP Customer Experience (SAP CX) knowledge. It has not been verified against your specific version and environment. Practitioners: verify the steps and share your experience below.

I’ve seen this exact behavior before. The issue is usually related to how SAP CX handles the MERGE operation with ExternalID. Are you checking if the ExternalID field is actually mapped correctly in your service definition? Sometimes the field doesn’t get recognized as the unique identifier for upsert operations.

“Confirmed this resolves duplicate opportunities in our SAP CX environment after enabling the Alternative Key checkbox on ExternalID and switching from POST to upsert API calls.”

We had a similar problem last year. The root cause was timing - our external system was generating ExternalIDs that included timestamps, and if two syncs happened close together, the IDs were different by milliseconds. Also, make sure you’re using the correct HTTP method. Instead of x-http-method=MERGE in the header, try using PATCH directly as the HTTP verb. SAP CX sometimes interprets these differently. Check your API logs to see if the ExternalID is actually being sent in each request.

Thanks for the suggestions. I checked our service definition and the ExternalID field is mapped, but I’m not 100% certain it’s configured as the unique key for upsert. Our ExternalIDs are consistent (no timestamps), so that’s not the issue. I did notice in the logs that occasionally the ExternalID field appears empty in the request payload even though our source data has it. Could this be a serialization problem in our middleware layer?

That empty ExternalID in the payload is your smoking gun. If even one request goes through without the ExternalID, SAP CX will treat it as a new record creation rather than an update. I’d recommend adding explicit pre-sync validation in your middleware to reject any payload where ExternalID is null or empty. Also, implement retry logic with exponential backoff - network hiccups during serialization could cause field truncation. Log every failed validation attempt so you can track patterns.

Beyond the technical fixes mentioned, you should also review your error handling. When an upsert fails or creates a duplicate, does your integration log the full request/response? Implementing comprehensive logging helped us identify that our issue was actually in how we were querying existing records before the upsert - we were using case-sensitive matching on ExternalID when SAP CX stores it case-insensitive in certain configurations.