Merge API
Merge API

Merge API

Merge duplicate records in DealCloud. The SDK exposes merge_entries() (POST .../data/merge/{entryTypeId}). HTTP is via intapp-rest-client.

Overview

The Merge API allows you to:

  • Merge duplicate records into a single record
  • Control which values are preserved
  • Automatically update references
⚠️

Merging is permanent. The source record is deleted after merge. Test carefully before production use.

merge_entries()

Merge multiple records into one:

from dealcloud_sdk import DealCloud, DealCloudConfig
 
config = DealCloudConfig(
    siteUrl="yoursite.dealcloud.com",
    clientId=12345,
    clientSecret="your-secret",
)
dc = DealCloud.from_config_object(config)
 
# Merge losers into winner (API max 10 loser IDs per request item)
result = dc.merge_entries(
    object_id="Company",
    winner_entry_id=12345,
    loser_entry_ids=[12346, 12347],
)

Parameters

ParameterTypeDescription
object_idstr | intObject API name or ID
winner_entry_idintEntry that survives
loser_entry_idsList[int]Entries merged into the winner (max 10 per call)
field_overridesdict | NoneOptional fieldOverrides payload (see your site’s API contract)
delete_losersboolSent as deleteLoserEntries in the request body
transfer_relationshipsboolSent as transferRelationships in the request body
overwrite_empty_valuesbool | NoneQuery flag overwriteEmptyValues when merging two entries (per API docs)

How merge works (typical)

  1. Winner preserved — the surviving entryId stays canonical.
  2. Losers — merged into the winner; API behavior for references/deletes follows DealCloud’s merge rules for your site.
  3. Batching — if you have more than 10 losers, split into multiple merge_entries() calls.

Field overrides

Optional JSON fieldOverrides can be passed through when your API contract defines them:

result = dc.merge_entries(
    object_id="Company",
    winner_entry_id=12345,
    loser_entry_ids=[12346],
    field_overrides={"Revenue": 1500000},
)

Find Duplicates

Before merging, identify duplicates:

def find_duplicates(dc, object_id: str, match_field: str):
    """Find records with duplicate values in a field."""
    
    data = dc.read_data(object_id, output="pandas", fields=["EntryId", match_field])
    
    # Group by match field
    duplicates = data[data.duplicated(match_field, keep=False)]
    duplicates = duplicates.sort_values(match_field)
    
    # Group into sets
    groups = duplicates.groupby(match_field)["EntryId"].apply(list).to_dict()
    
    return {k: v for k, v in groups.items() if len(v) > 1}
 
# Find companies with same name
dupes = find_duplicates(dc, "Company", "CompanyName")
 
for name, entry_ids in dupes.items():
    print(f"'{name}': {entry_ids}")

Automated Merge Workflow

def merge_duplicates_by_field(
    dc,
    object_id: str,
    match_field: str,
    keep_strategy: str = "oldest"  # "oldest", "newest", "highest_value"
):
    """
    Automatically merge records with duplicate values.
    
    Args:
        dc: DealCloud client
        object_id: Object to process
        match_field: Field to identify duplicates
        keep_strategy: Which record to keep as target
    """
    
    # Find duplicates
    data = dc.read_data(
        object_id,
        output="list",
        fields=["EntryId", match_field, "CreatedDate", "Revenue"]
    )
    
    # Group by match field
    groups = {}
    for record in data:
        key = record.get(match_field)
        if key:
            groups.setdefault(key, []).append(record)
    
    merged_count = 0
    
    for key, records in groups.items():
        if len(records) < 2:
            continue
        
        # Select target based on strategy
        if keep_strategy == "oldest":
            records.sort(key=lambda r: r.get("CreatedDate", ""))
        elif keep_strategy == "newest":
            records.sort(key=lambda r: r.get("CreatedDate", ""), reverse=True)
        elif keep_strategy == "highest_value":
            records.sort(key=lambda r: r.get("Revenue", 0) or 0, reverse=True)
        
        winner = records[0]["EntryId"]
        losers = [r["EntryId"] for r in records[1:]]
 
        # API allows up to 10 loser IDs per request
        for i in range(0, len(losers), 10):
            batch = losers[i : i + 10]
            dc.merge_entries(
                object_id=object_id,
                winner_entry_id=winner,
                loser_entry_ids=batch,
            )
 
        merged_count += len(losers)
        print(f"Merged {len(losers)} duplicates of '{key}' into {winner}")
    
    return merged_count
 
count = merge_duplicates_by_field(dc, "Company", "CompanyName", "oldest")
print(f"Total merged: {count}")

Safe Merge with Preview

def merge_with_preview(dc, object_id, target_id, source_ids):
    """Preview merge before executing."""
    
    # Load all records
    all_ids = [target_id] + source_ids
    ids_str = ",".join(map(str, all_ids))
    
    records = dc.read_data(
        object_id,
        output="list",
        query=f"{{EntryId: {{$in: [{ids_str}]}}}}"
    )
    
    # Identify target and sources
    target = next(r for r in records if r["EntryId"] == target_id)
    sources = [r for r in records if r["EntryId"] in source_ids]
    
    print("=== MERGE PREVIEW ===")
    print(f"\nTarget (keeping): Entry {target_id}")
    for k, v in target.items():
        if v is not None:
            print(f"  {k}: {v}")
    
    print(f"\nSources (merging {len(sources)}):")
    for s in sources:
        print(f"  Entry {s['EntryId']}")
    
    confirm = input("\nProceed with merge? (yes/no): ")
    
    if confirm.lower() == "yes":
        dc.merge_entries(
            object_id=object_id,
            winner_entry_id=target_id,
            loser_entry_ids=source_ids,
        )
        print("Merge completed!")
    else:
        print("Merge cancelled.")
 
merge_with_preview(dc, "Company", 12345, [12346, 12347])

Error Handling

def safe_merge(dc, object_id, target_id, source_ids):
    """Merge with error handling."""
    
    # Validate records exist
    all_ids = [target_id] + source_ids
    ids_str = ",".join(map(str, all_ids))
    
    records = dc.read_data(
        object_id,
        output="list",
        fields=["EntryId"],
        query=f"{{EntryId: {{$in: [{ids_str}]}}}}"
    )
    
    found_ids = {r["EntryId"] for r in records}
    missing = set(all_ids) - found_ids
    
    if missing:
        raise ValueError(f"Records not found: {missing}")
    
    try:
        dc.merge_entries(
            object_id=object_id,
            winner_entry_id=target_id,
            loser_entry_ids=source_ids,
        )
        return {"success": True, "merged": len(source_ids)}
    except Exception as e:
        return {"success": False, "error": str(e)}
 
result = safe_merge(dc, "Company", 12345, [12346])

Best Practices

  1. Preview before merge - Review what will be merged
  2. Backup first - Use Backups API before large merge operations
  3. Test with few records - Validate logic before bulk merges
  4. Log merge operations - Keep record of what was merged
  5. Handle references carefully - Understand relationship implications

Related