Data API
Write Operations
Upsert Data

Upsert Data

The upsert_data() method inserts new records or updates existing ones based on a match field. It composes insert_data / update_data behavior; same concurrency and HTTP stack as other writes (Advanced configuration, intapp-rest-client).

When to Use Upsert

ScenarioMethod
Creating new recordsinsert_data()
Updating known recordsupdate_data()
Syncing from external systemupsert_data()
Unknown if record existsupsert_data()

Basic Upsert

from dealcloud_sdk import DealCloud, DealCloudConfig
 
config = DealCloudConfig(
    siteUrl="yoursite.dealcloud.com",
    clientId=12345,
    clientSecret="your-secret",
)
dc = DealCloud.from_config_object(config)
 
# Upsert based on ExternalId field
records = [
    {
        "ExternalId": "CRM-001",
        "CompanyName": "Acme Corporation",
        "Revenue": 1000000
    },
    {
        "ExternalId": "CRM-002",
        "CompanyName": "Beta Industries",
        "Revenue": 500000
    }
]
 
result = dc.upsert_data(
    "Company",
    records,
    match_field="ExternalId"  # Field to match on
)
đź’ˇ

If a record with matching ExternalId exists, it's updated. If not, a new record is created.

Match Field Requirements

The match_field must be:

  • A unique field (or have unique values in your data)
  • Present in every record you're upserting
  • An existing field in DealCloud
# Common match fields
dc.upsert_data("Company", records, match_field="ExternalId")
dc.upsert_data("Contact", records, match_field="Email")
dc.upsert_data("Deal", records, match_field="DealNumber")

Upsert with EntryId

You can also include EntryId for known records:

records = [
    {
        "EntryId": 12345,  # Known record - will update
        "CompanyName": "Acme Corp Updated"
    },
    {
        # No EntryId - will match on field or create new
        "ExternalId": "CRM-003",
        "CompanyName": "New Company"
    }
]
 
result = dc.upsert_data("Company", records, match_field="ExternalId")

Input Formats

records = [
    {"ExternalId": "CRM-001", "CompanyName": "Acme Corp", "Revenue": 1000000},
    {"ExternalId": "CRM-002", "CompanyName": "Beta Inc", "Revenue": 500000},
]
dc.upsert_data("Company", records, match_field="ExternalId")

Using Lookup Values

# Default: Use IDs for references
records = [
    {
        "ExternalId": "CRM-001",
        "CompanyName": "Acme Corp",
        "Industry": 101,  # Choice ID
        "PrimaryContact": 67890  # Contact EntryId
    }
]
 
dc.upsert_data("Company", records, match_field="ExternalId")

Parameters

ParameterTypeDefaultDescription
object_idstr | intRequiredObject API name or ID
dataList[dict] | DataFrameRequiredRecords to upsert (accepts pandas or Polars)
match_fieldstrRequiredField to match existing records
use_dealcloud_idsboolTrueUse IDs vs lookup values
lookup_columnstrNoneField for reference lookups
outputstr"list"Output format: "list", "pandas", or "polars"

Output Formats

# List (default)
result = dc.upsert_data("Company", records, match_field="ExternalId")
 
# Pandas DataFrame
result = dc.upsert_data("Company", records, match_field="ExternalId", output="pandas")
 
# Polars DataFrame
result = dc.upsert_data("Company", records, match_field="ExternalId", output="polars")

Return Value

result = dc.upsert_data("Company", records, match_field="ExternalId")
 
# Result includes EntryId for all records
for record in result:
    print(f"{record['CompanyName']}: EntryId={record['EntryId']}")

Typed Upsert

Using Pydantic models:

from pydantic import BaseModel
from typing import Optional
 
class Company(BaseModel):
    EntryId: Optional[int] = None
    ExternalId: str
    CompanyName: str
    Revenue: Optional[float] = None
 
# From external system
external_data = [
    Company(ExternalId="CRM-001", CompanyName="Acme Corp", Revenue=1000000),
    Company(ExternalId="CRM-002", CompanyName="Beta Inc", Revenue=500000),
]
 
result = dc.typed_upsert_data(
    "Company",
    external_data,
    Company,
    match_field="ExternalId"
)
 
for company in result:
    print(f"Upserted {company.CompanyName} (ID: {company.EntryId})")

Sync Workflow Example

Complete external system sync:

from dealcloud_sdk import DealCloud, DealCloudConfig
import pandas as pd
 
config = DealCloudConfig(
    siteUrl="yoursite.dealcloud.com",
    clientId=12345,
    clientSecret="your-secret",
)
dc = DealCloud.from_config_object(config)
 
def sync_from_crm(crm_export_file: str):
    """Sync companies from CRM export."""
    
    # 1. Load external data
    crm_data = pd.read_csv(crm_export_file)
    
    # 2. Rename columns to match DealCloud API names
    column_mapping = {
        "CRM ID": "ExternalId",
        "Company": "CompanyName",
        "Annual Revenue": "Revenue",
        "Type": "CompanyType"
    }
    crm_data = crm_data.rename(columns=column_mapping)
    
    # 3. Upsert to DealCloud
    result = dc.upsert_data(
        "Company",
        crm_data,
        match_field="ExternalId",
        use_dealcloud_ids=False  # Use names for choices
    )
    
    # 4. Report results
    print(f"Synced {len(result)} companies")
    return result
 
sync_from_crm("crm_companies.csv")

Handling Duplicates

If your data has duplicate match values:

# Check for duplicates first
import pandas as pd
 
df = pd.DataFrame(records)
duplicates = df[df.duplicated("ExternalId", keep=False)]
 
if not duplicates.empty:
    print(f"Warning: {len(duplicates)} duplicate ExternalIds")
    # Remove duplicates (keep first)
    df = df.drop_duplicates("ExternalId", keep="first")
 
dc.upsert_data("Company", df, match_field="ExternalId")

Performance Considerations

  1. Use specific fields - Only include fields you need to update
  2. Batch appropriately - SDK handles batching, but consider logical groupings
  3. Unique match field - Non-unique fields cause multiple updates
# Good: Specific fields
records = [
    {"ExternalId": "CRM-001", "Revenue": 1000000},  # Only updating Revenue
]
 
# Less efficient: All fields
records = [
    {
        "ExternalId": "CRM-001",
        "CompanyName": "Acme",  # Unchanged
        "Industry": 101,  # Unchanged
        "Revenue": 1000000,  # Changed
        "Status": 201,  # Unchanged
        # ... many more unchanged fields
    }
]

Error Handling

upsert_data delegates to insert_data / update_data and supports the same error surface: error_handling, output="write_result", raise_on_row_errors, and BatchResult on COLLECT.

from dealcloud_sdk import ErrorHandling, split_row_results, DealCloudValidationError
 
result = dc.upsert_data("Company", records, match_field="ExternalId")
 
ok_rows, row_errors = split_row_results(result)
print(f"OK: {len(ok_rows)}, row errors: {len(row_errors)}")
 
batch = dc.upsert_data(
    "Company",
    records,
    match_field="ExternalId",
    error_handling=ErrorHandling.COLLECT,
)
print(batch.row_errors, batch.errors)
 
try:
    dc.upsert_data("Company", records, raise_on_row_errors=True)
except DealCloudValidationError as e:
    for row in e.row_errors:
        print(row["EntryId"], row["Errors"])

Best Practices

  1. Choose match field carefully - Should be unique and stable
  2. Validate data before upsert - Check for duplicates and required fields
  3. Log sync results - Track what was created vs updated
  4. Handle errors gracefully - Use COLLECT for batch operations
  5. Use typed upserts - Better validation and code clarity

Related