Workflow to Add MemberS to a Governance Group

Hello guys,

I`m biulding a workflow that automatically reassigns Role Ownership and Governance-Group membership when an identity is terminated.
The logic is straightforward:

Detect the termination event.

Retrieve every Governance Group the user belongs to.

Add the user’s Line Manager (LM) to those same groups.

However, one of my HTTP steps keeps failing, and I’m not sure why.

action:

"HTTP Request 3": {
  "actionId": "sp:http",
  "attributes": {
    "authenticationType": "OAuth",
    "basicAuthUserName": "m12312312312-qas",
    "basicAuthPassword": "$.secrets.123123321",

    "oAuthClientId": "1231231231231",
    "oAuthClientSecret": "$.secrets.12312312331212312321",
    "oAuthTokenUrl": "https://tenant-qas.api.identitynow.com/oauth/token",
    "oAuthCredentialLocation": "oAuthInBody",

    "method": "post",
    "url": "https://tenant-qas.api.identitynow.com/v2025/12312312312/members/bulk-add",
    "requestContentType": "json",
    "requestHeaders": {
      "X-SailPoint-Experimental": "true"
    },

    "jsonRequestBody": [
      {
        "id":   "123123123123321",
        "name": "12312312312321-qas",
        "type": "IDENTITY"
      }
    ]
  },
  "displayName": "",
  "nextStep": "End Step - Success 2",
  "type": "action",
  "versionNumber": 2
}

error:

{
  "loopOutput": {
    "failureItems": [
      {
        "errorMessage": "task failed: activity error (type: sp:external:http:v2, scheduledEventID: 5, startedEventID: 6, identity: 1@sp-workflow-worker-stg-us-east-1-5bd8f5d8dd-qdsgd@sp-workflow-engine): request failed (type: HTTP Response Returned a Client Error, retryable: false): request failed: 404 - 404  - {\"error\":\"No message available\"}",
        "payload": null
      }
    ],
    "successfulItems": []
  }
}

The same call succeeds in Postman, but returns HTTP 207 (Multi-Status) instead of 200.

Could the HTTP 207 response be causing the workflow step to throw the 404?

Any tips?

Best regards!!

I found the issue!

When performing an HTTP Request where the body is a list with only one item, you need to wrap it with double square brackets, like this:

[[{...}]]

If you send it like this:

[{...}]

The workflow engine will automatically convert it to:

{...}

This causes the payload to be interpreted as a single object instead of an array, which breaks the expected behavior.

@colin_mckibben this is a expected behavior?

Thanks

This feature is already available in the Integrating SailPoint with Identity Security Cloud Governance connector.

This doesn’t need any wfs to be configured, configuration is straight forward.

Owners of the below objects get updated to LM post termination:

Sources, Roles, Access Profiles, Governance Groups, Workflows, Pending Certification Decisions, Pending Request Decisions

Thanks,
Abhi

Yes as @totsy_cfs mentioned you can use this connector and enable the reassignment settings in the source connection. More over this connector can also be used to enable users to request access to any governance group and other objects instead of them reaching out to HelpDesk. This is a nice feature by ISC check it out once and convey to your team to use this.

Thank you, I really didn’t know that. Unfortunately, it seems that I can’t define logic in this connector, and I need to validate whether the new owner has a high hierarchy.

On a separate note, I was struggling the whole day adding members to a governance group in the http request in my workflow for a different requirement. The entire day i got 400 error, but just realised the thing with the double square brackets you have mentioned earlier. Worked like a charm after adding double square brackets . Thank you :slight_smile:

Thank you, I really didn’t know that. Unfortunately, it seems that I can’t define logic in this connector, and I need to validate whether the new owner has a high hierarchy.

we are getting 400 error in ps script. can you please share the body to check.

Hello!!

{
	"name": "Change Role Owner And Gov Members",
	"description": "Change Role Owner",
	"modified": "2025-06-24T13:22:33.666047122Z",
	"modifiedBy": {
		"type": "IDENTITY",
		"id": "12321312",
		"name": "user"
	},
	"definition": {
		"start": "Get Identity",
		"steps": {
			"Compare Numbers": {
				"actionId": "sp:compare-numbers",
				"choiceList": [
					{
						"comparator": "NumericGreaterThanEquals",
						"nextStep": "Compare Strings",
						"variableA.$": "$.hTTPRequest.body.length()",
						"variableB": 1
					}
				],
				"defaultStep": "Compare Numbers 1",
				"displayName": "Check if user is Owner of any role",
				"type": "choice"
			},
			"Compare Numbers 1": {
				"actionId": "sp:compare-numbers",
				"choiceList": [
					{
						"comparator": "NumericGreaterThanEquals",
						"nextStep": "Compare Strings 1",
						"variableA.$": "$.hTTPRequest2.body.length()",
						"variableB": 1
					}
				],
				"defaultStep": "End Step - Success",
				"displayName": "Check if the user belongs to any group",
				"type": "choice"
			},
			"Compare Strings": {
				"actionId": "sp:compare-strings",
				"choiceList": [
					{
						"comparator": "StringContains",
						"nextStep": "Send Email 1",
						"variableA.$": "$.getIdentity1.attributes.title",
						"variableB": "SUPERINTENDENTE"
					}
				],
				"defaultStep": "Loop",
				"displayName": "Check if the User LM is SuperIntendente",
				"type": "choice"
			},
			"Compare Strings 1": {
				"actionId": "sp:compare-strings",
				"choiceList": [
					{
						"comparator": "StringContains",
						"nextStep": "Send Email 2",
						"variableA.$": "$.getIdentity1.attributes.title",
						"variableB": "SUPERINTENDENTE"
					}
				],
				"defaultStep": "Loop 1",
				"displayName": "Check if the User LM is SuperIntendente",
				"type": "choice"
			},
			"End Step - Success": {
				"actionId": "sp:operator-success",
				"displayName": "",
				"type": "success"
			},
			"End Step - Success 4": {
				"actionId": "sp:operator-success",
				"displayName": "",
				"type": "success"
			},
			"Get Identity": {
				"actionId": "sp:get-identity",
				"attributes": {
					"id.$": "$.trigger.identity.id"
				},
				"displayName": "",
				"nextStep": "Get Identity 1",
				"type": "action",
				"versionNumber": 2
			},
			"Get Identity 1": {
				"actionId": "sp:get-identity",
				"attributes": {
					"id.$": "$.trigger.changes[?(@.attribute == 'manager')].oldValue.id"
				},
				"displayName": "Get Manager Identity",
				"nextStep": "HTTP Request 2",
				"type": "action",
				"versionNumber": 2
			},
			"HTTP Request": {
				"actionId": "sp:http",
				"attributes": {
					"authenticationType": "OAuth",
					"method": "get",
					"oAuthClientId": "asdasdas",
					"oAuthClientSecret": "$.secrets.asdasdas",
					"oAuthCredentialLocation": "oAuthInBody",
					"oAuthTokenUrl": "https://my-tenant.api.identitynow.com/oauth/token",
					"url": "https://my-tenant.api.identitynow.com/beta/roles",
					"urlParams": {
						"filters": "owner.id eq \"{{$.trigger.identity.id}}\""
					}
				},
				"displayName": "Get Roles that the user owns",
				"nextStep": "Compare Numbers",
				"type": "action",
				"versionNumber": 2
			},
			"HTTP Request 2": {
				"actionId": "sp:http",
				"attributes": {
					"authenticationType": "OAuth",
					"method": "get",
					"oAuthClientId": "asdasdas",
					"oAuthClientSecret": "$.secrets.12312321",
					"oAuthCredentialLocation": "oAuthInBody",
					"oAuthTokenUrl": "https://my-tenant.api.identitynow.com/oauth/token",
					"url": "https://my-tenant.api.identitynow.com/beta/workgroups",
					"urlParams": {
						"filters": "memberships.identityId eq \"{{$.trigger.identity.id}}\""
					}
				},
				"displayName": "Get gov group membership",
				"nextStep": "HTTP Request",
				"type": "action",
				"versionNumber": 2
			},
			"Loop": {
				"actionId": "sp:loop:iterator",
				"attributes": {
					"context.$": "$.getIdentity1",
					"input.$": "$.hTTPRequest.body",
					"start": "HTTP Request 1",
					"steps": {
						"End Step - Success 1": {
							"actionId": "sp:operator-success",
							"displayName": "",
							"type": "success"
						},
						"HTTP Request 1": {
							"actionId": "sp:http",
							"attributes": {
								"authenticationType": "OAuth",
								"basicAuthPassword": "$.secrets.123123231321",
								"basicAuthUserName": "user",
								"jsonPatchRequestBody": [
									{
										"op": "add",
										"path": "/owner",
										"value": {
											"id": "{{$.loop.context.id}}",
											"type": "IDENTITY"
										}
									}
								],
								"jsonRequestBody": [
									{
										"op": "add",
										"path": "/owner",
										"value": {
											"id": "ebeb6ba2f5124543a2090639525eea7",
											"type": "IDENTITY"
										}
									}
								],
								"method": "patch",
								"oAuthClientId": "asdasdas",
								"oAuthClientSecret": "$.secrets.asdasdas1231231",
								"oAuthCredentialLocation": "oAuthInBody",
								"oAuthTokenUrl": "https://my-tenant.api.identitynow.com/oauth/token",
								"requestContentType": "json-patch+json",
								"url": "https://my-tenant.api.identitynow.com/beta/roles/{{$.loop.loopInput.id}}",
								"urlParams": null
							},
							"displayName": "Change role owner",
							"nextStep": "End Step - Success 1",
							"type": "action",
							"versionNumber": 2
						}
					}
				},
				"displayName": "Add LM as Owner",
				"nextStep": "Send Email",
				"type": "action",
				"versionNumber": 1
			},
			"Loop 1": {
				"actionId": "sp:loop:iterator",
				"attributes": {
					"context.$": "$.getIdentity1",
					"input.$": "$.hTTPRequest2.body",
					"start": "HTTP Request 3",
					"steps": {
						"End Step - Success 3": {
							"actionId": "sp:operator-success",
							"displayName": "",
							"type": "success"
						},
						"HTTP Request 3": {
							"actionId": "sp:http",
							"attributes": {
								"authenticationType": "OAuth",
								"basicAuthPassword": "$.secrets.123123213",
								"basicAuthUserName": "user",
								"jsonPatchRequestBody.$": "",
								"jsonRequestBody": [
									[
										{
											"id": "{{$.loop.context.id}}",
											"type": "IDENTITY"
										}
									]
								],
								"method": "post",
								"oAuthClientId": "asdasdas",
								"oAuthClientSecret": "$.secrets.asdasdas1231231",
								"oAuthCredentialLocation": "oAuthInBody",
								"oAuthTokenUrl": "https://my-tenant.api.identitynow.com/oauth/token",
								"requestContentType": "json",
								"requestHeaders": {
									"Accept": "application/json",
									"Content-Type": "application/json"
								},
								"url": "https://my-tenant.api.identitynow.com/beta/workgroups/{{$.loop.loopInput.id}}/members/bulk-add",
								"urlParams": null
							},
							"displayName": "Add LM to Gov Group",
							"nextStep": "End Step - Success 3",
							"type": "action",
							"versionNumber": 2
						}
					}
				},
				"displayName": "Add LM as Group Member",
				"nextStep": "Loop 2",
				"type": "action",
				"versionNumber": 1
			},
			"Loop 2": {
				"actionId": "sp:loop:iterator",
				"attributes": {
					"context.$": "$.getIdentity",
					"input.$": "$.hTTPRequest2.body",
					"start": "HTTP Request 4",
					"steps": {
						"End Step - Success 2": {
							"actionId": "sp:operator-success",
							"displayName": "",
							"type": "success"
						},
						"HTTP Request 4": {
							"actionId": "sp:http",
							"attributes": {
								"authenticationType": "OAuth",
								"basicAuthPassword": "$.secrets.123123213",
								"basicAuthUserName": "user",
								"jsonPatchRequestBody.$": "",
								"jsonRequestBody": [
									[
										{
											"id": "{{$.loop.context.id}}",
											"type": "IDENTITY"
										}
									]
								],
								"method": "post",
								"oAuthClientId": "asdasdas",
								"oAuthClientSecret": "$.secrets.asdasdas1231231",
								"oAuthCredentialLocation": "oAuthInBody",
								"oAuthTokenUrl": "https://my-tenant.api.identitynow.com/oauth/token",
								"requestContentType": "json",
								"requestHeaders": {
									"Accept": "application/json",
									"Content-Type": "application/json"
								},
								"url": "https://my-tenant.api.identitynow.com/beta/workgroups/{{$.loop.loopInput.id}}/members/bulk-delete",
								"urlParams": null
							},
							"displayName": "",
							"nextStep": "End Step - Success 2",
							"type": "action",
							"versionNumber": 2
						}
					}
				},
				"displayName": "Remove User from Groups",
				"nextStep": "Send Email 3",
				"type": "action",
				"versionNumber": 1
			},
			"Send Email": {
				"actionId": "sp:send-email",
				"attributes": {
					"body": "emailBody",
					"context": {
						"LmImediato.$": "$.getIdentity1.attributes.displayName",
						"roles.$": "$.hTTPRequest.body",
						"today.$": "$.now()",
						"userName.$": "$.getIdentity.attributes.displayName"
					},
					"recipientEmailList": [
						"yan.coelho@domai.com"
					],
					"subject": "Notificação - Mudança de custódia de Role - Desligamento ${userName}"
				},
				"displayName": "Notify Changes",
				"nextStep": "Compare Numbers 1",
				"type": "action",
				"versionNumber": 2
			},
			"Send Email 1": {
				"actionId": "sp:send-email",
				"attributes": {
					"body": "emailBody",
					"context": {
						"roles.$": "$.hTTPRequest.body",
						"userName.$": "$.getIdentity.attributes.displayName"
					},
					"recipientEmailList": [
						"yan.coelho@domai.com"
					],
					"subject": "Notificação - Mudança de custódia de Role - Desligamento ${userName}"
				},
				"description": null,
				"displayName": "Notify ADMs to change manually",
				"nextStep": "Compare Numbers 1",
				"type": "action",
				"versionNumber": 2
			},
			"Send Email 2": {
				"actionId": "sp:send-email",
				"attributes": {
					"body": "emailBody",
					"context": {
						"groups.$": "$.hTTPRequest2.body",
						"userName.$": "$.getIdentity.attributes.displayName"
					},
					"recipientEmailList": [
						"yan.coelho@domai.com"
					],
					"subject": "Notificação - Mudança de custódia de Role - Desligamento ${userName}"
				},
				"description": null,
				"displayName": "Notify ADMs to change manually",
				"nextStep": "End Step - Success 4",
				"type": "action",
				"versionNumber": 2
			},
			"Send Email 3": {
				"actionId": "sp:send-email",
				"attributes": {
					"body": "emailBody",
					"context": {
						"LmImediato.$": "$.getIdentity1.attributes.displayName",
						"groups.$": "$.hTTPRequest2.body",
						"today.$": "$.now()",
						"userName.$": "$.getIdentity.attributes.displayName"
					},
					"recipientEmailList": [
						"yan.coelho@domai.com"
					],
					"subject": "Notificação - Mudança de custódia de Role - Desligamento ${userName}"
				},
				"displayName": "Notify Gov Group Changes",
				"nextStep": "End Step - Success 4",
				"type": "action",
				"versionNumber": 2
			}
		}
	},
	"creator": {
		"type": "IDENTITY",
		"id": "39c744461eaf411381717786aa18fc08",
		"name": "user"
	},
	"trigger": {
		"type": "EVENT",
		"attributes": {
			"filter.$": "$.changes[?(@.attribute == \"cloudLifecycleState\" && (@.newValue == \"inactive\"))]",
			"id": "idn:identity-attributes-changed"
		}
	}
}

In the UI you should put a list like this [[…]], always two brackets if in the body is just one item in the list.