Salesforce Module
Overview
SalesforceModule connects a ValkyrAI workflow to one Salesforce tenant through REST API v62.0. Version 2 replaces the legacy raw auth object and password-grant flow with a READY IntegrationAccount, validates the tenant origin, bounds every request and response, and verifies the exact provider status before reporting success.
The module supports one read page or one record mutation per execution:
- run a bounded SOQL
SELECTquery; - continue that query with an opaque Salesforce cursor;
- create one SObject record;
- update one record by Salesforce ID;
- upsert one record by an external-ID field; or
- delete one record after a separate explicit confirmation.
It does not accept a raw access token, client secret, username, password, security token, arbitrary endpoint, or provider URL in workflow input.
Usage
- Create or select a least-privilege Salesforce OAuth integration.
- Store the tenant HTTPS origin and OAuth access token in an
IntegrationAccount. - Bind that account through the normalized
ExecModuleConfig.authConfig.integrationAccountrelationship. - Select one documented operation and provide its required inputs.
- Set
confirmMutation: truefor create, update, or upsert. SetconfirmDelete: trueonly for an intentional delete. - Inspect
status,attempts,httpStatus, and either the query-page or mutation outputs.
Queries may be retried after bounded transient failures. Mutations are sent at most once because a timeout can occur after Salesforce commits the change.
Inputs
| Input | Type | Required | Bound | Purpose |
|---|---|---|---|---|
operation | string | Yes | query, create, update, upsert, or delete | Selects one fixed Salesforce REST operation. |
soql | string | For an initial query | Starts with SELECT; at most 20,000 characters | Defines the read-only SOQL query. |
nextCursor | string | For a later query page | Relative /services/data/vXX.0/query/... path only | Continues an existing Salesforce query without accepting an absolute URL. |
limit | integer | No | 200 through 2,000; default 2,000 | Requests the provider query batch size. |
sobject | string | For mutations | Salesforce API-name characters; at most 128 | Names the standard or custom SObject. |
recordId | string | Update/delete | Exactly 15 or 18 Salesforce ID characters | Selects the record to mutate. |
externalIdField | string | Upsert | Salesforce field API-name characters | Names the configured external-ID field. |
externalId | string | Upsert | Non-empty; at most 255 characters | Selects the external identity; path encoding is automatic. |
record | object | Create/update/upsert | Non-empty; at most 100 fields and 256 KiB | Contains Salesforce field API names and values. |
confirmMutation | boolean | Create/update/upsert | Must be true | Confirms one outbound record mutation. |
confirmDelete | boolean | Delete | Must be true | Separately confirms an irreversible record deletion. |
timeoutMs | integer | No | 100 through 300,000; default 30,000 | Bounds connection and response time. |
record rejects Id, attributes, credential-like field names at every nesting level, oversized arrays, excessive nesting, and oversized text values. Salesforce still validates object-specific required fields, field types, picklists, validation rules, and permissions.
Outputs
| Output | Type | When present | Meaning |
|---|---|---|---|
status | string | Always | success or error. |
operation | string | Always | Normalized operation name. |
items | array | Query success | One bounded Salesforce query page with attributes metadata removed. |
count | integer | Query success | Number of records in items. |
totalSize | integer | Query success | Provider-reported total matching records. |
hasMore | boolean | Query success | Whether Salesforce returned another query locator. |
nextCursor | string | Incomplete query | Opaque relative cursor for a later execution. |
resourceId | string | Mutation success when known | Verified Salesforce record ID. |
created | boolean | Upsert success | true for HTTP 201 create; false for HTTP 204 update. |
providerAccepted | boolean | Mutation success | true only after the operation-specific HTTP status is verified. |
httpStatus | integer | Provider response | Exact Salesforce HTTP status. |
attempts | integer | Always | Provider attempts consumed; mutations are at most one. |
error | object | Failure | Safe code, redacted message, and retryable flag. |
OAuth tokens, authorization headers, raw provider bodies, and record payloads are never returned.
IntegrationAccount Requirements
| Setting | Requirement |
|---|---|
| Provider | Salesforce OAuth 2.0 connected app or external client app |
accountName | Human-readable automation identity |
accountId | Credential-free tenant origin such as https://acme.my.salesforce.com |
apiKey | Encrypted current OAuth access token |
status | Must be READY |
The tenant origin must be HTTPS, have no user info, port, query, fragment, or non-root path, and end in a Salesforce-controlled salesforce.com or force.com domain. The module never authenticates against login.salesforce.com, refreshes a token, or reconstructs a password grant from workflow data. Token acquisition and refresh belong to the platform integration-account lifecycle.
Grant only the API and object/field permissions needed by the selected workflow. Salesforce sharing rules, CRUD/FLS, restriction rules, and connected-app policies remain authoritative.
Configuration
{
"version": "2.0.0",
"authConfig": {
"authStrategy": 1,
"integrationAccount": "integration-account:salesforce-sales-ops"
},
"executionConfig": {
"timeoutMs": 30000
},
"payloadConfig": {
"parameters": "{\"operation\":\"query\",\"limit\":500}"
}
}
The relationship value is symbolic. Persisted workflows bind an actual IntegrationAccount; they never place the access token in payloadConfig.parameters or module input.
Operations
| Operation | Salesforce resource | Required data | Side effect and retry policy |
|---|---|---|---|
query | /services/data/v62.0/query or a validated locator | soql or nextCursor | Read-only; transient 408, 429, 500, 502, 503, and 504 responses may be retried up to three attempts. |
create | /services/data/v62.0/sobjects/{sobject} | sobject, record, confirmMutation:true | Creates one record; exactly one attempt; requires HTTP 201 plus success:true and a valid ID. |
update | /services/data/v62.0/sobjects/{sobject}/{recordId} | sobject, recordId, record, confirmMutation:true | Updates one record; exactly one attempt; requires HTTP 204. |
upsert | /services/data/v62.0/sobjects/{sobject}/{externalIdField}/{externalId} | SObject, external identity, record, confirmation | Creates or updates one record; exactly one attempt; requires HTTP 201 or 204. |
delete | /services/data/v62.0/sobjects/{sobject}/{recordId} | sobject, recordId, confirmDelete:true | Deletes one record; exactly one attempt; requires HTTP 204. |
Errors and Failure Modes
| Code | Typical cause | Retryable | Recovery |
|---|---|---|---|
VALIDATION_ERROR | Invalid query, cursor, tenant, record, identifier, bound, confirmation, or provider success shape. | No | Correct the named input or account configuration. |
UNSUPPORTED_OPERATION | Unknown operation name. | No | Select one of the five documented operations. |
INTEGRATION_ACCOUNT_REQUIRED | No account relationship is bound. | No | Bind a Salesforce IntegrationAccount. |
INTEGRATION_ACCOUNT_NOT_READY | Bound account is not READY. | No | Repair or reconnect the account. |
SALESFORCE_HTTP_400 | Invalid SOQL, field, object, value, or provider rule. | No | Correct the bounded provider message and request. |
SALESFORCE_HTTP_401 | OAuth access token is invalid or expired. | No | Refresh or reconnect the IntegrationAccount. |
SALESFORCE_HTTP_403 | Missing API, object, field, or sharing permission. | No | Grant the minimum required authority. |
SALESFORCE_HTTP_404 | Record, object, or external identity is missing or invisible. | No | Reconcile under the same Salesforce identity. |
SALESFORCE_HTTP_429 | Salesforce API limit was reached. | Yes for query only | Respect the retry window or resume later. |
SALESFORCE_HTTP_5xx | Provider-side transient failure. | Yes for query only | Retry reads; reconcile mutations before manual re-execution. |
NETWORK_ERROR | DNS, TLS, timeout, or connectivity failure. | Yes for query only | Verify connectivity; inspect Salesforce before repeating a mutation. |
RESPONSE_TOO_LARGE | Response exceeded 5 MiB. | No | Narrow the SOQL projection or result set. |
EXECUTION_ERROR | Unexpected local runtime failure. | No | Inspect sanitized logs and module validation evidence. |
Salesforce error messages are limited, redacted through the shared sensitive-data policy, and never logged with exception payloads or bearer credentials.
Example
Query the most recently modified open opportunities:
{
"operation": "query",
"soql": "SELECT Id, Name, StageName, Amount, CloseDate FROM Opportunity WHERE IsClosed = false ORDER BY LastModifiedDate DESC",
"limit": 500
}
Expected result shape:
{
"status": "success",
"operation": "query",
"items": [
{
"Id": "006A0000008abcQIAQ",
"Name": "Enterprise renewal",
"StageName": "Proposal/Price Quote",
"Amount": 125000,
"CloseDate": "2026-09-30"
}
],
"count": 1,
"totalSize": 1,
"hasMore": false,
"httpStatus": 200,
"attempts": 1
}
To update a record, a workflow must provide the exact SObject and record ID and set confirmMutation:true. To delete it, the workflow must instead set the distinct confirmDelete:true guard.
Notes
- Pagination: each execution returns one query page. When
done:false, preservenextCursorand pass it to a later execution withoutsoql. Absolute and cross-host cursor URLs are rejected. - Batch size: Salesforce accepts query batch-size requests from 200 through 2,000 but may return fewer records. The module rejects a provider page that exceeds the requested bound.
- API limits: calls consume the bound integration user's Salesforce API allocation. The module does not hide limits with an unbounded loop.
- Idempotency: queries are safe to retry. Create, update, upsert, and delete are single-attempt because transport failure can be ambiguous. Use stable external IDs for upsert and perform read-after-write reconciliation before a manual retry.
- Rate limits: query retries honor a bounded numeric
Retry-Aftervalue and otherwise use bounded local backoff. Mutations never retry automatically. - Destructive behavior: delete permanently changes Salesforce state and requires
confirmDelete:true. Salesforce recycle-bin and retention behavior depends on the tenant and object. - Consistency: automation, flows, triggers, duplicate rules, validation rules, and asynchronous processing can change records after a successful response.
- Data classification: selected CRM fields leave ValkyrAI for Salesforce. Choose inputs and downstream outputs consistent with tenant policy.
- External verification: local tests cover request construction, origin and cursor confinement, confirmations, response mapping, transient read retry, single-attempt writes, redaction, and catalog discovery. Real OAuth scopes, object permissions, tenant automation, API limits, and provider behavior require a Salesforce test tenant and are not exercised by repository tests.
- Deferred operations: Bulk API 2.0, Composite API, queryAll, search, object describe, file/blob operations, relationship traversal endpoints, OAuth refresh, CDC subscriptions, and arbitrary REST calls are intentionally outside this bounded release.
- Runtime boundary: merged source and published documentation do not update Workflow Studio until the backend serving
/v1/modules/metadatais separately deployed. The live endpoint remains authoritative for deployed version and configuration. - Provider references: Salesforce REST object-data overview, REST query pagination guidance, and Composite authentication example.