POST to /services/data/vXX.0/jobs/ingest with 'operation' (insert/update/upsert/delete), 'object', 'contentType': 'CSV', and optionally 'externalIdFieldName' for upsert.
Upload the CSV data by sending a PUT request to /services/data/vXX.0/jobs/ingest/{jobId}/batches with the raw CSV body and Content-Type: text/csv.
Close the job by PATCHing the job resource with 'state': 'UploadComplete'; this signals Salesforce to begin processing.
Poll GET /services/data/vXX.0/jobs/ingest/{jobId} until 'state' is 'JobComplete' or 'Failed'; respect the Retry-After header if present.
Download successful and failed results: GET /services/data/vXX.0/jobs/ingest/{jobId}/successfulResults and /failedResults — parse CSV rows for sf__Id and sf__Error columns.
For large files, chunk uploads so each PUT contains at most 150 MB of data; create multiple jobs if needed.
Known gotchas
Bulk API 2.0 jobs are asynchronous — do not assume completion after the UploadComplete PATCH; always poll state before acting on results.
CSV must use UTF-8 encoding and include a header row matching Salesforce field API names exactly; mismatched headers cause silent row-level failures visible only in failedResults.
Bulk API 2.0 does not support all objects (e.g., ContentNote) and does not support relationship fields in header notation (Account.ExternalId__c) — use the externalIdFieldName parameter for upsert instead.
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