Ensure the target object has an External ID field (custom field with 'External ID' checkbox enabled) and note its API name (e.g., External_Id__c).
Send PATCH to /services/data/vXX.0/sobjects/{SObjectType}/{ExternalIdFieldName}/{ExternalIdValue} with the record body as JSON — PATCH is required for upsert, not POST.
A 201 response means a new record was created; a 204 means an existing record was updated — use the Location header in the 201 case to retrieve the new record ID.
For multi-record upsert at scale, prefer the Bulk API 2.0 upsert operation with the externalIdFieldName parameter rather than individual REST upsert calls.
To upsert by a standard field (e.g., Id), send a PUT/PATCH with the ID in the URL — but for true upsert semantics based on a business key, always use an External ID field.
Handle 300 MULTIPLE_CHOICES responses: they occur when more than one existing record matches the External ID value, indicating a data quality issue that must be resolved before upserting.
Known gotchas
External ID fields are case-insensitive for matching by default — two records with 'ABC' and 'abc' will produce a 300 collision error.
A PATCH to an External ID URL with a body containing the Id field will cause a 400 INVALID_FIELD_FOR_INSERT_UPDATE if the provided Id conflicts with the matched record's Id.
Relationships can be specified by External ID rather than Salesforce ID using the nested object syntax (e.g., 'Account': {'External_Id__c': 'value'}) — verify the related object also has the External ID field indexed.
Give your agent this knowledge — and 200+ more routes
One MCP install gives any agent live access to the full route map, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp