How to handle multiple accounts in the workflow

Hi team,

We are using below workflow code and it is working for single account but throwing error for multiple accounts when iterating and does anyone know how to fix this workflow code?

{
“definition”: {
“start”: “Get Identity”,
“steps”: {
“Define Variable”: {
“actionId”: “sp:define-variable”,
“attributes”: {
“id”: “sp:define-variable”,
“variables”: [
{
“description”: “Building dynamic URL to fetch userlogin.Id from userlogin table”,
“name”: “URL”,
“transforms”: [
{
“id”: “sp:transform:concatenate:string”,
“input”: {
“variableB.$”: “$.getAccounts.accounts[?(@.sourceId == ‘1234’)].nativeIdentity”
}
},
{
“id”: “sp:transform:concatenate:string”,
“input”: {
“variableB”: “'”
}
}
],
“variableA”: “https://test.my.salesforce.com/services/data/v61.0/query/?q=select+ID+FROM+UserLogin+WHERE+USERId='”
}
]
},
“displayName”: “”,
“nextStep”: “HTTP Request”,
“type”: “Mutation”
},
“Define Variable 1”: {
“actionId”: “sp:define-variable”,
“attributes”: {
“id”: “sp:define-variable”,
“variables”: [
{
“description”: “The userlogin patch URL.”,
“name”: “UserLoginPatchURL”,
“transforms”: [
{
“id”: “sp:transform:concatenate:string”,
“input”: {
“variableB.$”: “$.hTTPRequest.body.records[0].Id”
}
}
],
“variableA”: “https://test.my.salesforce.com/services/data/v61.0/sobjects/Userlogin/”
}
]
},
“displayName”: “”,
“nextStep”: “HTTP Request 1”,
“type”: “Mutation”
},
“End Step - Failure”: {
“actionId”: “sp:operator-failure”,
“description”: null,
“displayName”: “”,
“failureName”: "Failed ",
“type”: “failure”
},
“End Step - Failure 1”: {
“actionId”: “sp:operator-failure”,
“displayName”: “”,
“failureName”: “Failed”,
“type”: “failure”
},
“End Step - Success”: {
“actionId”: “sp:operator-success”,
“description”: null,
“displayName”: “”,
“type”: “success”
},
“Get Accounts”: {
“actionId”: “sp:get-accounts”,
“attributes”: {
“getAccountsBy”: “specificIdentity”,
“identity.$”: “$.getIdentity.id”
},
“displayName”: “”,
“nextStep”: “Define Variable”,
“type”: “action”,
“versionNumber”: 1
},
“Get Identity”: {
“actionId”: “sp:get-identity”,
“attributes”: {
“id.$”: “$.trigger.recipient.id”
},
“description”: “Id of the identity”,
“displayName”: “”,
“nextStep”: “Get Accounts”,
“type”: “action”,
“versionNumber”: 2
},
“HTTP Request”: {
“actionId”: “sp:http”,
“attributes”: {
“authenticationType”: “OAuth”,
“method”: “get”,
“oAuthClientId”: “1234”,
“oAuthClientSecret”: “5678”,
“oAuthCredentialLocation”: “oAuthInBody”,
“oAuthScope”: null,
“oAuthTokenUrl”: “https://test.my.salesforce.com/services/oauth2/token”,
“requestContentType”: “json”,
“url.$”: “$.defineVariable.uRL”
},
“catch”: [
{
“next”: “Send Email”
}
],
“displayName”: “”,
“nextStep”: “Define Variable 1”,
“type”: “action”,
“versionNumber”: 2
},
“HTTP Request 1”: {
“actionId”: “sp:http”,
“attributes”: {
“authenticationType”: “OAuth”,
“jsonRequestBody”: {
“IsFrozen”: true
},
“method”: “patch”,
“oAuthClientId”: “1234”,
“oAuthClientSecret”: “5678”,
“oAuthCredentialLocation”: “oAuthInBody”,
“oAuthScope”: null,
“oAuthTokenUrl”: “https://test.my.salesforce.com/services/oauth2/token”,
“requestContentType”: “json”,
“url.$”: “$.defineVariable1.userLoginPatchURL”
},
“catch”: [
{
“next”: “Send Email 2”
}
],
“displayName”: “”,
“nextStep”: “Send Email 1”,
“type”: “action”,
“versionNumber”: 2
},
“Send Email”: {
“actionId”: “sp:send-email”,
“attributes”: {
“body”: “<p class=\“p1\”>Dear Admin,

\n<p class=\“p1\”>The User ${displayName} from Salesforce failed to disable due to an account dependency. The account  frozen Failed. Please take the necessary action as required..

\n<p class=\“p1\”><span data-teams=\“true\”>Please assign the ticket to salesforce team for the further action

\n<p class=\“p1\”>Account: ${EMAIL}

\n<p class=\“p1\”>Error message: ${error}

\n<p class=\“p1\”> 

\n<p class=\“p1\”>Thank you,

\n<p class=\“p1\”> Identity Management Team

”,
“context”: {
“EMAIL.$”: “$.getIdentity.attributes.email”,
“displayName.$”: “$.getIdentity.attributes.displayName”,
“error.$”: “$.hTTPRequest.error.workflowErrorMessage”
},
“recipientEmailList”: [
“ithelpdesk@test4567.com”,
“departure-sailpointnotifications@test4567.com”,
“infoseciag@test4567.com”
],
“subject”: "The salesforce account for ${displayName} failed to disable and freeze. "
},
“displayName”: “”,
“nextStep”: “End Step - Failure”,
“type”: “action”,
“versionNumber”: 2
},
“Send Email 1”: {
“actionId”: “sp:send-email”,
“attributes”: {
“body”: “<p class=\“p1\”>Dear Admin,

\n<p class=\“p1\”>The User ${displayName} from Salesforce failed to disable due to an account dependency. The account has been frozen. Please take the necessary action as required..

\n<p class=\“p1\”><span data-teams=\“true\”>Please assign the ticket to salesforce team for the further action

\n<p class=\“p1\”>Account: ${EMAIL}

\n

Thank you,

\n

 Identity Management Team

”,
“context”: {
“EMAIL.$”: “$.getIdentity.attributes.email”,
“displayName.$”: “$.getIdentity.attributes.displayName”
},
“recipientEmailList”: [
“departure-sailpointnotifications@test4567.com”,
“ithelpdesk@test4567.com”,
“infoseciag@test4567.com”
],
“subject”: “Salesforce account disable Failed for ${displayName}”
},
“displayName”: “”,
“nextStep”: “End Step - Success”,
“type”: “action”,
“versionNumber”: 2
},
“Send Email 2”: {
“actionId”: “sp:send-email”,
“attributes”: {
“body”: “<p class=\“p1\”>Dear Admin,

\n<p class=\“p1\”>The User ${displayName} from Salesforce failed to disable due to an account dependency. The account  frozen Failed. Please take the necessary action as required..

\n<p class=\“p1\”><span data-teams=\“true\”>Please assign the ticket to salesforce team for the further action

\n<p class=\“p1\”>Account: ${EMAIL}

\n<p class=\“p1\”>Error message: ${error}

\n<p class=\“p1\”> 

\n<p class=\“p1\”>Thank you,

\n<p class=\“p1\”> Identity Management Team

”,
“context”: {
“EMAIL.$”: “$.getIdentity.attributes.email”,
“displayName.$”: “$.getIdentity.attributes.displayName”,
“error.$”: “$.hTTPRequest1.error.workflowErrorMessage”
},
“recipientEmailList”: [
“infoseciag@test4567.com”,
“ithelpdesk@test4567.com”,
“departure-sailpointnotifications@test4567.com”
],
“subject”: “The salesforce account for ${displayName} failed to disable and freeze.”
},
“displayName”: “”,
“nextStep”: “End Step - Failure 1”,
“type”: “action”,
“versionNumber”: 2
}
}
},
“trigger”: {
“type”: “EVENT”,
“mode”: “advanced”,
“attributes”: {
“filter.$”: “$.accountRequests[?(@.provisioningResult == ‘failed’ && @.accountOperation == ‘Disable’ && @.provisioningTarget == ‘Salesforce’)]”,
“id”: “idn:post-provisioning”,
“integrationId”: null
}
},
“name”: “test-Freezeaccount”,
“description”: “This workflow ensures that when a Salesforce account fails to disable, an automated attempt is made to freeze the account ,an notification email is sent to IT HelpDesk for further action.”
}

Thanks

Hi,

We are getting error while executing the step as below:

{
“id”: “sp:define-variable”,
“variables”: [
{
“description”: “Building dynamic URL to fetch userlogin.Id from userlogin table”,
“name”: “URL”,
“transforms”: [
{
“id”: “sp:transform:concatenate:string”,
“input”: {
“variableB”: [
“12234”,
“5678”
]
}
},
{
“id”: “sp:transform:concatenate:string”,
“input”: {
“variableB”: “'”
}
}
],
“variableA”: “https://test.my.salesforce.com/services/data/v61.0/query/?q=select+ID+FROM+UserLogin+WHERE+USERId='”
}
]
}

Thanks

Hello @kalyannambi2010,

As a quick test, you can try updating your variableB definition as follows:

"variableB.$": "$.getAccounts.accounts[?(@.sourceId == '1234')][0].nativeIdentity"

I added [0] to select the first account matching the specified sourceId when multiple accounts are returned.

Note: This is only a workaround if you intend to process a single account. It will not handle all matching accounts.

If you need to process all matching accounts, you should use a Loop with your original expression as the input array and perform the Salesforce API calls for each account individually.

Thanks

Hi @baoussounda thank you for reply.

I have tried this option for a single account but would like to process all matching accounts. How to process all matching accounts using Loop with original expression as the input array and perform the Salesforce API calls for each account individually.?

Thanks

Hello Kalyana. As Ousmane mentioned, [0] will only pick the first account. Since you need to process all Salesforce accounts, I would suggest adding a Loop after Get Accounts.

Set the Loop Input to $.getAccounts.accounts[?(@.sourceId == '1234')]. Remove .nativeIdentity from the end so the loop receives each account separately.

Then move your existing Define Variable and HTTP GET/PATCH steps inside the Loop. In the first Define Variable, use $.loop.loopInput.nativeIdentity. This way, each iteration picks one account ID and builds the URL for that account. Your second Define Variable can keep using the GET response, since that call runs in the same iteration.

For your email steps, you can set the Loop Context to $.getIdentity and access the details using $.loop.context.attributes.email and $.loop.context.attributes.displayName. Also, make sure each branch inside the Loop has an End Step, along with a separate End Step outside the Loop to close the workflow.

I would check before testing: your trigger is for a failed Salesforce Disable operation, but Get Accounts will return all Salesforce accounts for that identity. Do you want to freeze all of them or only the accounts where Disable failed? If it is only the failed ones, we should use the trigger’s accountRequests to identify those accounts.

I would test with two Salesforce accounts and check the execution history to confirm each GET and PATCH uses the correct account ID.

Hi @punna0001 thank you for the updates, we would like to implement this workflow ensures that when a Salesforce account fails to disable, an automated attempt is made to freeze the account ,an notification email is sent to IT HelpDesk for further action. So for this implemenation how this workflow JSON could be updated?

Thanks

Hello @kalyannambi2010

As another alternative, I’ve attached a workflow that you can use as a starting point.

I haven’t fully tested it end to end, so it may require some adjustments before it can be used.

As Harish mentioned, rather than retrieving all Salesforce accounts associated with the identity, this workflow works directly with the accounts included in the triggering account requests and filters for failed Salesforce account-disable operations.

You can import the workflow directly from VS Code or edit the JSON file to update the owner object before importing it through the UI.

You’ll also need to adapt other parts of the workflow to match your environment, particularly the Salesforce configuration and endpoint details. You can also review and integrated your initial logic of defining variables.

I hope this provides a useful starting point!

EDIT : not sure if the loop will work if there only one account as the json path expression will be evaluated to string, but you have to test.

SalesforceDisableFailureFreezeAccountsV1 (2).json (3,8 Ko)

Hi Kalyana,

The issue is caused by multiple Salesforce accounts being returned as an array, while the concatenate:string transform expects a single value.

To resolve this, add a Loop operator and iterate through each Salesforce account individually, using $.loop.loopInput.nativeIdentity within the loop to build the URL and execute the GET/PATCH requests for each account.

This will support both single-account and multiple-account scenarios.

Hi @rameshcba thank you and please check the below updated workflow JSON and provide your feedback on the same?

{
“name”: “Salesforce Disable Failure - Freeze Accounts”,
“description”: “Triggers on provisioning completed events for Salesforce disable failures, loops through each failed account request, freezes them individually, and notifies IT HelpDesk”,
“trigger”: {
“type”: “EVENT”,
“attributes”: {
“filter.$”: “$.accountRequests[?(@.provisioningResult == ‘failed’ && @.accountOperation == ‘Disable’ && @.provisioningTarget == ‘Salesforce’)]”,
“id”: “idn:post-provisioning”,
“integrationId”: null
}
},
“definition”: {
“start”: “Get Identity”,
“steps”: {
“Get Identity”: {
“actionId”: “sp:get-identity”,
“attributes”: {
“id.$”: “$.trigger.recipient.id”
},
“displayName”: “Get Recipient Details”,
“nextStep”: “Loop Through Failed Account Requests”,
“type”: “action”,
“versionNumber”: 2
},
“Loop Through Failed Account Requests”: {
“actionId”: “sp :loop:iterator”,
“attributes”: {
“input.$”: “$.trigger.accountRequests[?(@.provisioningResult == ‘failed’ && @.accountOperation == ‘Disable’ && @.provisioningTarget == ‘Salesforce’)]”,
“loopInput.$”: “$.trigger”,
“start”: “Query Salesforce UserLogin”,
“steps”: {
“Query Salesforce UserLogin”: {
“actionId”: “sp:http”,
“attributes”: {
“authenticationType”: “OAuth”,
“method”: “get”,
“oAuthClientId”: “salesforce_client_id”,
“oAuthClientSecret”: “$.secrets.47641d96-88e9-45ec-b513-f35378a08ec9”,
“oAuthCredentialLocation”: “oAuthInHeader”,
“oAuthTokenUrl”: “https://login.salesforce.com/services/oauth2/token”,
“url”: “https://your-instance.salesforce.com/services/data/v58.0/query”,
“urlParams”: {
“q”: “select+ID+FROM+UserLogin+WHERE+USERId=‘{{$.loop.loopInput.nativeIdentity}}’”
}
},
“displayName”: “Query UserLogin ID”,
“nextStep”: “Freeze Salesforce Account”,
“type”: “action”,
“versionNumber”: 2
},
“Freeze Salesforce Account”: {
“actionId”: “sp:http”,
“attributes”: {
“authenticationType”: “OAuth”,
“jsonRequestBody”: {
“IsFrozen”: true
},
“method”: “patch”,
“oAuthClientId”: “salesforce_client_id”,
“oAuthClientSecret”: “$.secrets.salesforce_client_secret”,
“oAuthCredentialLocation”: “oAuthInHeader”,
“oAuthTokenUrl”: “https://login.salesforce.com/services/oauth2/token”,
“requestContentType”: “json”,
“url”: “https://your-instance.salesforce.com/services/data/v58.0/sobjects/UserLogin/{{$.querySalesforceUserLogin.records[0].Id}}”
},
“displayName”: “Freeze Salesforce Account”,
“catch”: [
{
“next”: “Send Freeze Failure Email”
}
],
“nextStep”: “End Loop Step - Success”,
“type”: “action”,
“versionNumber”: 2
},
“Send Freeze Failure Email”: {
“actionId”: “sp:send-email”,
“attributes”: {
“body”: “CRITICAL: Automated remediation failed.\n\nFailed to freeze Salesforce Account ID: ${accountId} for user ${displayName}.\n\nPlease investigate immediately.”,
“context”: {
“accountId.$”: “$.loop.loopInput.accountId”,
“displayName.$”: “$.getIdentity.attributes.displayName”
},
“from”: “noreply@sailpoint.com”,
“recipientEmailList”: “ithelpdesk@company.com”,
“subject”: “CRITICAL: Salesforce Account Freeze FAILED for ${displayName}”
},
“displayName”: “Send Freeze Failure Email”,
“nextStep”: “End Loop Step - Failure”,
“type”: “action”,
“versionNumber”: 2
},
“End Loop Step - Success”: {
“actionId”: “sp:operator-success”,
“type”: “success”
},
“End Loop Step - Failure”: {
“actionId”: “sp:operator-failure”,
“type”: “failure”
}
}
},
“displayName”: “Process Account Iterations”,
“nextStep”: “Send Success Notification Email”,
“type”: “action”,
“versionNumber”: 1
},
“Send Success Notification Email”: {
“actionId”: “sp:send-email”,
“attributes”: {
“body”: “The following identity had Salesforce account(s) that failed to disable and have been successfully frozen:\n\nIdentity: ${displayName}\nEmail: ${email}\n\nPlease review and take appropriate action.”,
“carbonCopy.$”: “”,
“context”: {
“displayName.$”: “$.getIdentity.attributes.displayName”,
“email.$”: “$.getIdentity.attributes.email”
},
“from”: “noreply@sailpoint.com”,
“recipientEmailList”: “ithelpdesk@company.com”,
“replyTo”: “”,
“subject”: “Salesforce Accounts Frozen Due to Failed Disable Operations”
},
“displayName”: “Send Success Notification Email”,
“nextStep”: “End Step - Success”,
“type”: “action”,
“versionNumber”: 2
},
“End Step - Success”: {
“actionId”: “sp:operator-success”,
“type”: “success”
}
}
}
}

Hi @baoussounda thank you and could you please check below updated workflow JSON and provide your feedback on the same?

{
“name”: “Salesforce Disable Failure - Freeze Accounts”,
“description”: “Triggers on provisioning completed events for Salesforce disable failures, freezes the accounts, and notifies IT HelpDesk”,
“trigger”: {
“type”: “EVENT”,
“attributes”: {
“filter.$”: “$.accountRequests[?(@.provisioningResult == ‘failed’ && @.accountOperation == ‘Disable’ && @.provisioningTarget == ‘Salesforce’)]”,
“id”: “idn:post-provisioning”,
“integrationId”: null
}
},
“definition”: {
“start”: “Get Identity”,
“steps”: {
“Get Identity”: {
“actionId”: “sp:get-identity”,
“attributes”: {
“id.$”: “$.trigger.recipient.id”
},
“displayName”: “Get Recipient Details”,
“nextStep”: “Loop Through Failed Account Requests”,
“type”: “action”,
“versionNumber”: 2
},
“Loop Through Failed Account Requests”: {
“actionId”: “sp :loop:iterator”,
“attributes”: {
“input.$”: “$.trigger.accountRequests[?(@.provisioningResult == ‘failed’ && @.accountOperation == ‘Disable’ && @.provisioningTarget == ‘Salesforce’)]”,
“start”: “Query Salesforce UserLogin”,
“steps”: {
“Query Salesforce UserLogin”: {
“actionId”: “sp:http”,
“attributes”: {
“authenticationType”: “OAuth”,
“method”: “get”,
“oAuthClientId”: “salesforce_client_id”,
“oAuthClientSecret”: “$.secrets.47641d96-88e9-45ec-b513-f35378a08ec9”,
“oAuthCredentialLocation”: “oAuthInHeader”,
“oAuthTokenUrl”: “https://login.salesforce.com/services/oauth2/token”,
“url”: “https://your-instance.salesforce.com/services/data/v58.0/query”,
“urlParams”: {
“q”: “select+ID+FROM+UserLogin+WHERE+USERId=‘{{$.loop.item.accountId}}’”
}
},
“displayName”: “Query UserLogin ID”,
“nextStep”: “Freeze Salesforce Account”,
“type”: “action”,
“versionNumber”: 2
},
“Freeze Salesforce Account”: {
“actionId”: “sp:http”,
“attributes”: {
“authenticationType”: “OAuth”,
“jsonRequestBody”: {
“IsFrozen”: true
},
“method”: “patch”,
“oAuthClientId”: “salesforce_client_id”,
“oAuthClientSecret”: “$.secrets.salesforce_client_secret”,
“oAuthCredentialLocation”: “oAuthInHeader”,
“oAuthTokenUrl”: “https://login.salesforce.com/services/oauth2/token”,
“requestContentType”: “json”,
“url”: “https://your-instance.salesforce.com/services/data/v58.0/sobjects/UserLogin/{{$.querySalesforceUserLogin.body.records[0].Id}}”
},
“displayName”: “Freeze Salesforce Account”,
“catch”: [
{
“next”: “Send Freeze Failure Email”
}
],
“nextStep”: “End Loop Step - Success”,
“type”: “action”,
“versionNumber”: 2
},
“Send Freeze Failure Email”: {
“actionId”: “sp:send-email”,
“attributes”: {
“body”: “CRITICAL: Automated remediation failed.\n\nFailed to freeze Salesforce Account ID: ${accountId} for user ${displayName}.\n\nError: ${freezeSalesforceAccount.error.message}\n\nPlease investigate immediately.”,
“context”: {
“accountId.$”: “$.loop.item.accountId”,
“displayName.$”: “$.getIdentity.attributes.displayName”
},
“from”: “noreply@sailpoint.com”,
“recipientEmailList”: “ithelpdesk@company.com”,
“subject”: “CRITICAL: Salesforce Account Freeze FAILED for ${displayName}”
},
“displayName”: “Send Freeze Failure Email”,
“nextStep”: “End Loop Step - Failure”,
“type”: “action”,
“versionNumber”: 2
},
“End Loop Step - Success”: {
“actionId”: “sp:operator-success”,
“type”: “success”
},
“End Loop Step - Failure”: {
“actionId”: “sp:operator-failure”,
“type”: “failure”
}
}
},
“displayName”: “Process Account Iterations”,
“nextStep”: “Send Success Notification Email”,
“type”: “action”,
“versionNumber”: 1
},
“Send Success Notification Email”: {
“actionId”: “sp:send-email”,
“attributes”: {
“body”: “The following identity had Salesforce account(s) that failed to disable and have been successfully frozen:\n\nIdentity: ${displayName}\nEmail: ${email}\n\nPlease review and take appropriate action.”,
“context”: {
“displayName.$”: “$.getIdentity.attributes.displayName”,
“email.$”: “$.getIdentity.attributes.email”
},
“from”: “noreply@sailpoint.com”,
“recipientEmailList”: “ithelpdesk@company.com”,
“subject”: “Salesforce Accounts Frozen Due to Failed Disable Operations”
},
“displayName”: “Send Success Notification Email”,
“nextStep”: “End Step - Success”,
“type”: “action”,
“versionNumber”: 2
},
“End Step - Success”: {
“actionId”: “sp:operator-success”,
“type”: “success”
}
}
}
}

Hi,

Thanks for sharing the updated JSON. The loop over accountRequests is the right approach for handling multiple accounts. These are the remaining points:

  1. Loop input: loopInput.$ is set to $.trigger, so each iteration receives the whole event. Make it the current account request, so $.loop.loopInput.nativeIdentity resolves per account. Check an execution’s step details to confirm what it holds.
  2. Loop actionId: "sp :loop:iterator" has a stray space. Copy the exact actionId from a loop built in the UI.
  3. Freeze URL: $.querySalesforceUserLogin.records[0].Id probably needs .body (...body.records[0].Id). Confirm in the execution details.
  4. Identifier: the query uses nativeIdentity but the failure email uses accountId. Use the field that holds the Salesforce User ID.
  5. Failure path: ending in sp:operator-failure can stop the remaining accounts. Test it, or end with a success operator after the email.
  6. Success email: it says “successfully frozen” even if a freeze failed. Send one email per account, or a summary of actual results.
  7. Cleanup: use straight quotes, remove the empty carbonCopy.$ and replyTo, and use the same secret name in both HTTP steps.

Hello Kalyana. v2 is close. A few changes before testing:

  1. Change {{$.loop.item.accountId}} to {{$.loop.loopInput.accountId}}. The current item is $.loop.loopInput, and since you loop over accountRequests, accountId is the right field (not nativeIdentity). Confirm it holds the Salesforce User ID your query expects.
  2. Your Loop Input filter (failed, Disable, Salesforce) is the right idea. Just verify those values and casing against a real payload.
  3. Add a failure branch on the GET, not just the PATCH, and check records[0].Id exists before the PATCH, since a no-match query returns HTTP 200 with an empty records array.
  4. For emails, set Loop Context to $.getIdentity and use $.loop.context.attributes.email. GET and PATCH can reference each other within the same iteration.
  5. Your success email runs after the loop, so it reports success even if a freeze failed. Send success on the PATCH success branch and failure on the GET/PATCH failure branch, each with its own end step plus one outside the loop. The parallel loop keeps processing other accounts if one fails, so keep the failure end step so that iteration is not counted as a success.

Test with one successful Disable and one failed Disable to confirm that only the account whose Disable failed is frozen. Test Workflow runs real actions, so use a safe test account.

Hi @rameshcba and @punna0001 thank you for the updates and could you please check the updated workflow JSON and provide your feedback on the same?

{
“name”: “Salesforce Disable Failure - Freeze Accounts”,
“description”: “Triggers on provisioning completed events for Salesforce disable failures, freezes the accounts, and notifies IT HelpDesk”,
“trigger”: {
“type”: “EVENT”,
“attributes”: {
“filter.$”: “$.accountRequests[?(@.provisioningResult == ‘failed’ && @.accountOperation == ‘Disable’ && @.provisioningTarget == ‘Salesforce’)]”,
“id”: “idn:post-provisioning”,
“integrationId”: null
}
},
“definition”: {
“start”: “Get Identity”,
“steps”: {
“Get Identity”: {
“actionId”: “sp:get-identity”,
“attributes”: {
“id.$”: “$.trigger.recipient.id”
},
“displayName”: “Get Recipient Details”,
“nextStep”: “Loop Through Failed Account Requests”,
“type”: “action”,
“versionNumber”: 2
},
“Loop Through Failed Account Requests”: {
“actionId”: “sp :loop:iterator”,
“attributes”: {
“input.$”: “$.trigger.accountRequests[?(@.provisioningResult == ‘failed’ && @.accountOperation == ‘Disable’ && @.provisioningTarget == ‘Salesforce’)]”,
“start”: “Query Salesforce UserLogin”,
“steps”: {
“Query Salesforce UserLogin”: {
“actionId”: “sp:http”,
“attributes”: {
“authenticationType”: “OAuth”,
“method”: “get”,
“oAuthClientId”: “salesforce_client_id”,
“oAuthClientSecret”: “$.secrets.salesforce_client_secret”,
“oAuthCredentialLocation”: “oAuthInHeader”,
“oAuthTokenUrl”: “https://login.salesforce.com/services/oauth2/token”,
“url”: “https://your-instance.salesforce.com/services/data/v58.0/query”,
“urlParams”: {
“q”: “select+ID+FROM+UserLogin+WHERE+USERId=‘{{$.loop.loopInput.accountId}}’”
}
},
“displayName”: “Query UserLogin ID”,
“catch”: [
{
“next”: “Send Query Failure Email”
}
],
“nextStep”: “Verify UserLogin Exists”,
“type”: “action”,
“versionNumber”: 2
},
“Verify UserLogin Exists”: {
“actionId”: “sp:compare-boolean”,
“attributes”: {
“context”: {
“hasRecords.$”: “$.querySalesforceUserLogin.body.records[0].Id”
},
“expression”: “$.context.hasRecords != null”
},
“displayName”: “Verify UserLogin Exists”,
“choiceList”: [
{
“nextStep”: “Freeze Salesforce Account”,
“comparator”: “BooleanEquals”,
“value”: true
}
],
“defaultStep”: “Send UserNotFound Failure Email”,
“type”: “choice”
},
“Freeze Salesforce Account”: {
“actionId”: “sp:http”,
“attributes”: {
“authenticationType”: “OAuth”,
“jsonRequestBody”: {
“IsFrozen”: true
},
“method”: “patch”,
“oAuthClientId”: “salesforce_client_id”,
“oAuthClientSecret”: “$.secrets.salesforce_client_secret”,
“oAuthCredentialLocation”: “oAuthInHeader”,
“oAuthTokenUrl”: “https://login.salesforce.com/services/oauth2/token”,
“requestContentType”: “json”,
“url”: “https://your-instance.salesforce.com/services/data/v58.0/sobjects/UserLogin/{{$.querySalesforceUserLogin.body.records[0].Id}}”
},
“displayName”: “Freeze Salesforce Account”,
“catch”: [
{
“next”: “Send Freeze Failure Email”
}
],
“nextStep”: “Send Success Notification Email”,
“type”: “action”,
“versionNumber”: 2
},
“Send Query Failure Email”: {
“actionId”: “sp:send-email”,
“attributes”: {
“body”: “CRITICAL: Salesforce Query Failed.\n\nFailed to query Salesforce UserLogin for Account ID: ${accountId} associated with identity ${displayName}.\n\nError: ${queryError}\n\nPlease investigate.”,
“context”: {
“accountId.$”: “$.loop.loopInput.accountId”,
“displayName.$”: “$.getIdentity.attributes.displayName”,
“queryError.$”: “$.querySalesforceUserLogin.error.message”
},
“from”: “noreply@sailpoint.com”,
“recipientEmailList”: “ithelpdesk@company.com”,
“subject”: “CRITICAL: Salesforce Query FAILED for ${displayName}”
},
“displayName”: “Send Query Failure Email”,
“nextStep”: “End Loop Step - Failure”,
“type”: “action”,
“versionNumber”: 2
},
“Send UserNotFound Failure Email”: {
“actionId”: “sp:send-email”,
“attributes”: {
“body”: “CRITICAL: Salesforce UserLogin Record Not Found.\n\nNo UserLogin record was returned by the query for Salesforce Account ID: ${accountId} (Identity: ${displayName}).\n\nAutomated freeze cannot proceed.”,
“context”: {
“accountId.$”: “$.loop.loopInput.accountId”,
“displayName.$”: “$.getIdentity.attributes.displayName”
},
“from”: “noreply@sailpoint.com”,
“recipientEmailList”: “ithelpdesk@company.com”,
“subject”: “CRITICAL: Salesforce UserLogin Not Found for ${displayName}”
},
“displayName”: “Send UserNotFound Failure Email”,
“nextStep”: “End Loop Step - Failure”,
“type”: “action”,
“versionNumber”: 2
},
“Send Freeze Failure Email”: {
“actionId”: “sp:send-email”,
“attributes”: {
“body”: “CRITICAL: Automated remediation failed.\n\nFailed to freeze Salesforce Account ID: ${accountId} for user ${displayName}.\n\nError: ${freezeError}\n\nPlease investigate immediately.”,
“context”: {
“accountId.$”: “$.loop.loopInput.accountId”,
“displayName.$”: “$.getIdentity.attributes.displayName”,
“freezeError.$”: “$.freezeSalesforceAccount.error.message”
},
“from”: “noreply@sailpoint.com”,
“recipientEmailList”: “ithelpdesk@company.com”,
“subject”: “CRITICAL: Salesforce Account Freeze FAILED for ${displayName}”
},
“displayName”: “Send Freeze Failure Email”,
“nextStep”: “End Loop Step - Failure”,
“type”: “action”,
“versionNumber”: 2
},
“Send Success Notification Email”: {
“actionId”: “sp:send-email”,
“attributes”: {
“body”: “The following identity had a Salesforce account that failed to disable and has been successfully frozen:\n\nIdentity: ${displayName}\nEmail: ${email}\nSalesforce Account ID: ${accountId}\n\nPlease review and take appropriate action.”,
“context”: {
“displayName.$”: “$.getIdentity.attributes.displayName”,
“email.$”: “$.getIdentity.attributes.email”,
“accountId.$”: “$.loop.loopInput.accountId”
},
“from”: “noreply@sailpoint.com”,
“recipientEmailList”: “ithelpdesk@company.com”,
“subject”: “Salesforce Account Frozen Due to Failed Disable Operation”
},
“displayName”: “Send Success Notification Email”,
“nextStep”: “End Loop Step - Success”,
“type”: “action”,
“versionNumber”: 2
},
“End Loop Step - Success”: {
“actionId”: “sp:operator-success”,
“type”: “success”
},
“End Loop Step - Failure”: {
“actionId”: “sp:operator-failure”,
“type”: “failure”
}
}
},
“displayName”: “Process Account Iterations”,
“nextStep”: “End Step - Success”,
“type”: “action”,
“versionNumber”: 1
},
“End Step - Success”: {
“actionId”: “sp:operator-success”,
“type”: “success”
}
}
}
}

Hi @punna0001 and @rameshcba thank you for the update and could you please check below updated JSON workflow and can we restrict the workflow should trigger only for failure disable accounts instaed of all accounts iterating?

{
“name”: “Salesforce Disable Failure - Freeze Accounts”,
“description”: “Triggers on provisioning completed events for Salesforce disable failures, freezes the accounts, and notifies IT HelpDesk”,
“trigger”: {
“type”: “EVENT”,
“attributes”: {
“filter.$”: “$.accountRequests[?(@.provisioningResult == ‘failed’ && @.accountOperation == ‘Disable’ && @.provisioningTarget == ‘Salesforce’)]”,
“id”: “idn:post-provisioning”,
“integrationId”: null
}
},
“definition”: {
“start”: “Get Identity”,
“steps”: {
“Get Identity”: {
“actionId”: “sp:get-identity”,
“attributes”: {
“id.$”: “$.trigger.recipient.id”
},
“displayName”: “Get Recipient Details”,
“nextStep”: “Loop Through Failed Account Requests”,
“type”: “action”,
“versionNumber”: 2
},
“Loop Through Failed Account Requests”: {
“actionId”: “sp :loop:iterator”,
“attributes”: {
“input.$”: “$.trigger.accountRequests[?(@.provisioningResult == ‘failed’ && @.accountOperation == ‘Disable’ && @.provisioningTarget == ‘Salesforce’)]”,
“start”: “Query Salesforce UserLogin”,
“steps”: {
“Query Salesforce UserLogin”: {
“actionId”: “sp:http”,
“attributes”: {
“authenticationType”: “OAuth”,
“method”: “get”,
“oAuthClientId”: “salesforce_client_id”,
“oAuthClientSecret”: “$.secrets.salesforce_client_secret”,
“oAuthCredentialLocation”: “oAuthInHeader”,
“oAuthTokenUrl”: “https://login.salesforce.com/services/oauth2/token”,
“url”: “https://your-instance.salesforce.com/services/data/v58.0/query”,
“urlParams”: {
“q”: “select+ID+FROM+UserLogin+WHERE+USERId=‘{{$.loop.loopInput.accountId}}’”
}
},
“displayName”: “Query UserLogin ID”,
“catch”: [
{
“next”: “Send Query Failure Email”
}
],
“nextStep”: “Verify UserLogin Exists”,
“type”: “action”,
“versionNumber”: 2
},
“Verify UserLogin Exists”: {
“actionId”: “sp:compare-boolean”,
“attributes”: {
“context”: {
“hasRecords.$”: “$.querySalesforceUserLogin.body.records[0].Id”
},
“expression”: “$.context.hasRecords != null”
},
“displayName”: “Verify UserLogin Exists”,
“choiceList”: [
{
“nextStep”: “Freeze Salesforce Account”,
“comparator”: “BooleanEquals”,
“value”: true
}
],
“defaultStep”: “Send UserNotFound Failure Email”,
“type”: “choice”
},
“Freeze Salesforce Account”: {
“actionId”: “sp:http”,
“attributes”: {
“authenticationType”: “OAuth”,
“jsonRequestBody”: {
“IsFrozen”: true
},
“method”: “patch”,
“oAuthClientId”: “salesforce_client_id”,
“oAuthClientSecret”: “$.secrets.salesforce_client_secret”,
“oAuthCredentialLocation”: “oAuthInHeader”,
“oAuthTokenUrl”: “https://login.salesforce.com/services/oauth2/token”,
“requestContentType”: “json”,
“url”: “https://your-instance.salesforce.com/services/data/v58.0/sobjects/UserLogin/{{$.querySalesforceUserLogin.body.records[0].Id}}”
},
“displayName”: “Freeze Salesforce Account”,
“catch”: [
{
“next”: “Send Freeze Failure Email”
}
],
“nextStep”: “Send Success Notification Email”,
“type”: “action”,
“versionNumber”: 2
},
“Send Query Failure Email”: {
“actionId”: “sp:send-email”,
“attributes”: {
“body”: “CRITICAL: Salesforce Query Failed.\n\nFailed to query Salesforce UserLogin for Account ID: ${accountId} associated with identity ${displayName}.\n\nError: ${queryError}\n\nPlease investigate.”,
“context”: {
“accountId.$”: “$.loop.loopInput.accountId”,
“displayName.$”: “$.getIdentity.attributes.displayName”,
“queryError.$”: “$.querySalesforceUserLogin.error.message”
},
“from”: “noreply@sailpoint.com”,
“recipientEmailList”: “ithelpdesk@company.com”,
“subject”: “CRITICAL: Salesforce Query FAILED for ${displayName}”
},
“displayName”: “Send Query Failure Email”,
“nextStep”: “End Loop Step - Failure”,
“type”: “action”,
“versionNumber”: 2
},
“Send UserNotFound Failure Email”: {
“actionId”: “sp:send-email”,
“attributes”: {
“body”: “CRITICAL: Salesforce UserLogin Record Not Found.\n\nNo UserLogin record was returned by the query for Salesforce Account ID: ${accountId} (Identity: ${displayName}).\n\nAutomated freeze cannot proceed.”,
“context”: {
“accountId.$”: “$.loop.loopInput.accountId”,
“displayName.$”: “$.getIdentity.attributes.displayName”
},
“from”: “noreply@sailpoint.com”,
“recipientEmailList”: “ithelpdesk@company.com”,
“subject”: “CRITICAL: Salesforce UserLogin Not Found for ${displayName}”
},
“displayName”: “Send UserNotFound Failure Email”,
“nextStep”: “End Loop Step - Failure”,
“type”: “action”,
“versionNumber”: 2
},
“Send Freeze Failure Email”: {
“actionId”: “sp:send-email”,
“attributes”: {
“body”: “CRITICAL: Automated remediation failed.\n\nFailed to freeze Salesforce Account ID: ${accountId} for user ${displayName}.\n\nError: ${freezeError}\n\nPlease investigate immediately.”,
“context”: {
“accountId.$”: “$.loop.loopInput.accountId”,
“displayName.$”: “$.getIdentity.attributes.displayName”,
“freezeError.$”: “$.freezeSalesforceAccount.error.message”
},
“from”: “noreply@sailpoint.com”,
“recipientEmailList”: “ithelpdesk@company.com”,
“subject”: “CRITICAL: Salesforce Account Freeze FAILED for ${displayName}”
},
“displayName”: “Send Freeze Failure Email”,
“nextStep”: “End Loop Step - Failure”,
“type”: “action”,
“versionNumber”: 2
},
“Send Success Notification Email”: {
“actionId”: “sp:send-email”,
“attributes”: {
“body”: “The following identity had a Salesforce account that failed to disable and has been successfully frozen:\n\nIdentity: ${displayName}\nEmail: ${email}\nSalesforce Account ID: ${accountId}\n\nPlease review and take appropriate action.”,
“context”: {
“displayName.$”: “$.getIdentity.attributes.displayName”,
“email.$”: “$.getIdentity.attributes.email”,
“accountId.$”: “$.loop.loopInput.accountId”
},
“from”: “noreply@sailpoint.com”,
“recipientEmailList”: “ithelpdesk@company.com”,
“subject”: “Salesforce Account Frozen Due to Failed Disable Operation”
},
“displayName”: “Send Success Notification Email”,
“nextStep”: “End Loop Step - Success”,
“type”: “action”,
“versionNumber”: 2
},
“End Loop Step - Success”: {
“actionId”: “sp:operator-success”,
“type”: “success”
},
“End Loop Step - Failure”: {
“actionId”: “sp:operator-failure”,
“type”: “failure”
}
}
},
“displayName”: “Process Account Iterations”,
“nextStep”: “End Step - Success”,
“type”: “action”,
“versionNumber”: 1
},
“End Step - Success”: {
“actionId”: “sp:operator-success”,
“type”: “success”
}
}
}
}

Hello @kalyannambi2010. I went through your updated workflow JSON, made a few corrections, and tested the workflow logic and notification paths in my ISC demo tenant. I have attached the revised JSON and a screenshot showing the four notification emails I received during testing.

Salesforce_Freeze_Workflow_Restored.json (8.4 KB)

Here is what I was able to verify:

  1. Workflow validation: The review-copy workflow imported without validation errors.
  2. Loop processing: Tested single and multiple account requests, including mixed provisioning results. The Loop selected only the failed Salesforce Disable requests from the supplied test events.
  3. Identity and account references: Confirmed that $.loop.loopInput.accountId and the identity attributes passed through Loop Context resolved correctly.
  4. Success notification: Simulated a successful Salesforce GET and PATCH. The email arrived with the correct identity name, email, and account ID.
  5. UserLogin not found: Simulated HTTP 200 with an empty records array. The workflow followed the UserNotFound branch and sent the corresponding email.
  6. GET and PATCH failures: Tested HTTP 404 responses. Both the Query Failure and Freeze Failure emails arrived with the correct account details and error messages.

In the attached JSON, I have used the oAuthInBody and API v61.0 settings from your initial workflow while incorporating the Loop and notification corrections. Since your later JSON has different authentication and API settings, please verify which configuration is appropriate for your Salesforce environment.

One thing I noticed during testing: failed iterations are recorded under loopOutput.failureItems, but the overall workflow can still show as Completed because the outer end step is Success.

Just to clarify, I tested the workflow logic and notifications, not an actual Salesforce account freeze. The Salesforce responses were simulated, and the HTTP failure tests used a test endpoint.

Before using the attached JSON, please update the Salesforce instance URL, OAuth credentials, secret reference, and HelpDesk recipients. I would also verify the OAuth token URL against your Salesforce My Domain, and check the actual Provisioning Completed event values, particularly provisioningResult and provisioningTarget, since those drive the trigger. I would start with one Salesforce test account, confirm that accountId contains the Salesforce User ID expected by your query, and verify IsFrozen directly in Salesforce after the PATCH.

I hope the attached workflow and test screenshot help. If you encounter any issues during import or testing, please share the error or failing step output, and we can take a look.

Hi @punna0001 thank you so much for updating and sharing the workflow JSON. Is it mandatory/better to have “Send UserNotFound Failure Email” step and if we do not have this step does it cause any impact on the workflow processing? If not required and if we remove this step what could be the workflow path?

Hi @punna0001 thank you so much for updating and sharing the workflow JSON. Is it mandatory/better to have “Send UserNotFound Failure Email” step and if we do not have this step does it cause any impact on the workflow processing? If not required and if we remove this step what could be the workflow path? Does it support multiple accounts failure freeze email notifications?

No, the Send UserNotFound Failure Email step is not mandatory. I added it mainly to notify HelpDesk when the Salesforce GET succeeds but does not return a matching UserLogin record.

If you prefer to remove it, I would suggest keeping Verify UserLogin Exists and updating its paths:

  • IsPresent → Freeze Salesforce Account (no change)
  • Default → End Loop Step - Failure

This way, if no UserLogin is found, the PATCH is skipped and only that Loop iteration is marked as failed. The remaining requests can still be processed. Just update the Default path before deleting the email step. I would also change the failure message, since it currently refers to a HelpDesk notification.

Personally, I would suggest keeping the email if the workflow runs unattended. Otherwise, the failure might go unnoticed, as the overall workflow can still show Completed even when loopOutput.failureItems contains failed iterations.

If you remove it, I would recommend one quick test with an empty records array to confirm the new path.

Hi @punna0001 thank you for the feedback. Does this workflow JSON support multiple accounts by sending a separate email for every failed account or if 3 accounts fail and are frozen, IT HelpDesk will receive 3 individual emails (one per account), each containing that specific account’s accountId and status.

If the first account encounters an error (e.g., Query Failure or Freeze Failure) and transitions to End Loop Step - Failure, the sp:operator-failure operator can abort the entire workflow loop immediately?

Yes, the workflow is designed to send one email per account.

For example, if 3 failed Salesforce Disable requests enter the Loop and all 3 freeze PATCH requests succeed, HelpDesk should receive 3 separate success emails, each containing the respective accountId and outcome message.

Regarding End Loop Step - Failure, it will not stop the entire Loop. Since this uses the regular Loop operator, each account is processed independently and in parallel. If one account encounters a Query or Freeze Failure, only that iteration fails and sends its failure notification. The remaining accounts continue processing.

You can refer to SailPoint’s Loop documentation for this behavior.

If you remove Send UserNotFound Failure Email, that scenario will not generate an email unless you connect it to another notification step. I tested multiple-account processing and the individual notification paths in my demo tenant, but not 3 email deliveries in one execution. I would suggest one final test with 3 accounts, including one failure, to confirm the behavior in your tenant.

Also, the success email confirms the PATCH was accepted. It does not independently verify that IsFrozen is set in Salesforce.