bulk/upsert
Insert or update a large batch of records using an External ID field for matching. Records matching on the External ID are updated; non-matching records are inserted.
Idempotent Sync: bulk/upsert is safe to re-run. Records matching on ExternalIdField are updated in place. Non-matching records are inserted. This makes it the ideal operation for recurring nightly or hourly sync workflows where you cannot predict which records are new vs changed.
When to Use
- Sync contacts from HubSpot to Salesforce using email as the external ID field.
- Update account records from an ERP system using the ERP account ID as external ID.
- Run a nightly idempotent data load that inserts new records and updates changed ones.
Configuration
Connection
| Field | Type | Required | Description |
InstanceUrl | string | required | Salesforce org URL. |
AccessToken | string | required | Valid OAuth2 access token. |
Operation Fields
| Field | Type | Required | Description |
SObjectType | string | required | Salesforce object API name, e.g. Contact, Account. |
ExternalIdField | string | required | API name of the field used to match existing records. Must be marked as an External ID field in Salesforce setup. Common values: Email, ERP_Account_ID__c, HubSpot_Contact_ID__c. |
Records | array | required | Array of objects to upsert. Each object must include the ExternalIdField value for matching. |
Sample Configuration
{
"Resource": "bulk",
"Operation": "upsert",
"InstanceUrl": "https://myorg.my.salesforce.com",
"AccessToken": "{{credentials.salesforce.accessToken}}",
"SObjectType": "Contact",
"ExternalIdField": "Email",
"Records": [
{ "Email": "alice@acme.com", "FirstName": "Alice", "LastName": "Chen", "Phone": "+1-555-0401" },
{ "Email": "bob@beta.com", "FirstName": "Bob", "LastName": "Park", "Title": "CTO" }
]
}
Validation Errors
| Error Code | Condition |
CFG_MISSING_INSTANCE | InstanceUrl is missing. |
CFG_MISSING_TOKEN | AccessToken is missing. |
VAL_MISSING_FIELD | SObjectType, ExternalIdField, or Records is missing. |
UNEXPECTED_ERROR | Unexpected exception or invalid ExternalIdField configuration. |
Output
Success Port
| Field | Type | Description |
jobId | string | Salesforce Bulk API Job ID. |
state | string | Final job state: JobComplete. |
recordsProcessed | int | Number of records successfully upserted. |
recordsFailed | int | Number of records that failed. |
results | array | Per-record results with id, success, created (bool — true if inserted, false if updated), and errors. |
resource | string | Always bulk. |
operation | string | Always upsert. |
Error Port
| Field | Type | Description |
errorCode | string | Machine-readable error code. |
message | string | Error description. |
Sample Output
{
"jobId": "7505g000003AbcCAAQ",
"state": "JobComplete",
"recordsProcessed": 9998,
"recordsFailed": 2,
"results": [
{ "id": "0035g000006LmnOPQ", "success": true, "created": false, "errors": [] },
{ "id": "0035g000006NewXYZ", "success": true, "created": true, "errors": [] },
{ "id": null, "success": false, "created": false, "errors": [{ "message": "INVALID_EMAIL_ADDRESS" }] }
],
"resource": "bulk",
"operation": "upsert"
}
Expression Reference
| Expression | Description |
{{nodes.BulkUpsert.jobId}} | Bulk API Job ID. |
{{nodes.BulkUpsert.recordsProcessed}} | Count of successfully upserted records. |
{{nodes.BulkUpsert.recordsFailed}} | Count of failed records. |
{{nodes.BulkUpsert.results}} | Full per-record results array including insert/update distinction. |
Node Policies & GuardRails
| Policy | Detail |
| External ID field requirement | The ExternalIdField must be explicitly marked as an External ID field in Salesforce Setup. Standard fields like Email are already external-ID capable on Contact; custom fields must be configured. |
| Idempotency | This operation is safe to re-run. Records are matched by ExternalIdField, so running the same payload twice updates rather than duplicates. |
| Partial failure handling | Check recordsFailed after completion. Log failed records and their error messages for retry. |
| Field API names | All field keys must use Salesforce API field names, not labels. |
| Credential storage | Store AccessToken in BizFirst Credentials Manager. |
| Sandbox testing | Always test in sandbox before running against production data. |
Examples
Nightly HubSpot Contact Sync
{
"SObjectType": "Contact",
"ExternalIdField": "Email",
"Records": "{{nodes.HubSpotExport.contacts}}"
// Each: { "Email": ..., "FirstName": ..., "LastName": ..., "Phone": ... }
}
ERP Account Sync by External ID
{
"SObjectType": "Account",
"ExternalIdField": "ERP_Account_ID__c",
"Records": "{{nodes.ERPExport.accounts}}"
// Each: { "ERP_Account_ID__c": "ERP-12345", "Name": ..., "AnnualRevenue": ... }
}
Daily Product Catalog Refresh
{
"SObjectType": "Product2",
"ExternalIdField": "ProductCode",
"Records": "{{nodes.CatalogAPI.products}}"
// Each: { "ProductCode": "SKU-001", "Name": "Enterprise License", "IsActive": true }
}