Provisioning DepartmentID via Web Service Connector Based on Identity Cost Center

Hi everyone,

I’ve configured a Web Services Connector in SailPoint IdentityNow to provision accounts to a target system.

I need help with the following scenario:

  • While provisioning a new account, I must send the DepartmentID (GUID) to the target system.
  • The target system has multiple departments, which can be fetched via this endpoint:
GET https://api.xyz.com/departments

A sample response looks like this:

{
  "ID": "eb1d4b5b-34da-47cd-8a58-c6fdf6940633",
  "Code": "1261-8040128",
  ...
}
  • In SailPoint, the identity has a costCenter attribute (e.g., "1261_8040128"), and I need to match this with the department’s Code value (where Code = 1261-8040128 — note the difference in delimiter).
  • Once matched, I need to extract the corresponding ID and send that in the provisioning payload as DepartmentID.

:counterclockwise_arrows_button: Dynamic Requirement

  • New departments are frequently added in the target system, so the solution needs to dynamically call the departments API and pick the matching one based on the current identity’s costCenter.

Hi @hkhandale

If i am not wrong, To achieve this you have to write the BeforeOperation Rule where you can execute your API: GET https://api.xyz.com/departments
Use the API response to check -match the costCentric values to department code and set that value in your create account body.

I hope this will work for you!

Thank you!

Hi @Sagar_18

Could please assist me how can i use it for Attribute Sync

i need to pass value while attribute sync so it will change on existing account as well

We have a similar issue. We didn’t need the cost center value for any other automation, so we added a transform to the Identity Profile for the source that replaces any _ with a -. Then we were able to use the Identity attribute in its current form for our other automation. If you don’t need that Identity attribute for anything else, you could do the same.
I know it’s not ideal but if you did need that attribute with the _ in it then you could add an additional Identity attribute that has the transform. So you will end up having the existing Identity attribute where the value contains the _ and then another that is nearly the same but contains the - instead.

Hi @hkhandale

If you want to update the DepartmentId for an existing user based on changes to the CostCentric values, it likely won’t be updated using a rule alone. If I’m not mistaken, you’ll need to write a transform where you map the CostCentric values to the corresponding DepartmentId. Then, populate that value in the DepartmentId identity attribute and sync it to the Account attribute in source. Also configure update operation in source for same.

Note:- So in this case if any new values are added in target application, every time you need to update transform.

Kindly try it out and let me know whether it helps you.

Thank you.

Hi @delaudee

Yes, I got it to create attribute for replacing “_” to “-”.
but my question is how can we search exact department via matching cost center with code

@hkhandale

As these departments are dynamic in nature , we cannot use transform here unless we have departments data in the tenant . I suggest to use a before operation on create account .

If you want to bring in department data into tenant and compare the values , you need to implement a transform using Lookup operation .

{
  "attributes": {
    "table": {
      "departmenet": "costCenterAttribute",
      "default": "Unknow"
    }
  },
  "type": "lookup",
  "name": "Lookup Transform"
}

This transform is static in nature considering the departments are dynamic . We must update the transform to add new department values in the transforms to compare .

This alone doesn’t full fill the requirement. But this answers your question how can we search exact department via matching cost center with code

@hkhandale -

Implementation Approach for Dynamic DepartmentID Resolution - High level steps:

  1. Attribute Mapping in Create Operation
    Configure the Web Services connector to map the DepartmentID field in the Create operation to the identity’s costCenter attribute. This temporary mapping is used as an intermediate input for the rule logic.
  2. Before Operation Rule Configuration
    Develop a Before Operation Rule to dynamically resolve the actual DepartmentID (GUID) prior to provisioning. The rule will:
  • Extract the Input Value
    Retrieve the DepartmentID value from the provisioning plan. At this stage, it holds the identity’s costCenter value.
  • Perform External API Lookup
    Use the provided restClient to invoke the target system’s /departments API and fetch the corresponding department object based on the transformed costCenter code.
  • Update the Provisioning Payload
    Once the department record is retrieved, extract the ID field and update the DepartmentID key in the JSON payload with this value, ensuring that the final request body contains the correct GUID required by the target system.

Sample Before Operation Rule -

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import connector.common.JsonUtil;
import connector.common.Util;
import sailpoint.connector.webservices.Endpoint;
import sailpoint.connector.webservices.WebServicesClient;
import sailpoint.object.Application;
import sailpoint.object.ProvisioningPlan;
import sailpoint.object.ProvisioningPlan.AccountRequest;
import sailpoint.object.ProvisioningPlan.AttributeRequest;
import sailpoint.tools.GeneralException;

Map body = requestEndPoint.getBody();
String jsonBody = (String) body.get("jsonBody");
log.info("Rule - Modify Body: running");

try {
    Map jsonMap = JsonUtil.toMap(jsonBody);
    if (jsonMap != null) {

        // Step 1: Get costCenter from provisioningPlan (passed in as DepartmentID attr)
        String costCenter = null;
        if (provisioningPlan != null) {
            for (AccountRequest accReq : Util.iterate(provisioningPlan.getAccountRequests())) {
                for (AttributeRequest attReq : Util.iterate(accReq.getAttributeRequests())) {
                    if ("DepartmentID".equalsIgnoreCase(attReq.getName())) {
                        costCenter = (String) attReq.getValue();
                        log.info("Fetched DepartmentID (costCenter) from plan: " + costCenter);
                    }
                }
            }
        }

        // Step 2: Convert to expected Code format
        String formattedCode = null;
        if (costCenter != null) {
            formattedCode = costCenter.replace("_", "-");
            log.info("Formatted department Code: " + formattedCode);
        }

        // Step 3: Call /departments?code=XYZ and extract ID
        String departmentID = null;
        if (formattedCode != null && restClient != null) {
            // Build the endpoint (adjust if API requires path param or other format)
            String endpoint = "/departments?code=" + java.net.URLEncoder.encode(formattedCode, "UTF-8");
            log.info("Calling department lookup endpoint: " + endpoint);

            Map deptResponse = restClient.execute("GET", endpoint, null, null, null);

            if (deptResponse != null && deptResponse.containsKey("body")) {
                Map deptData = (Map) deptResponse.get("body");
                if (deptData.containsKey("ID")) {
                    departmentID = (String) deptData.get("ID");
                    log.info("Retrieved Department ID: " + departmentID);
                } else {
                    log.warn("No ID found in department response: " + deptData);
                }
            } else {
                log.warn("Invalid department lookup response: " + deptResponse);
            }
        }

        // Step 4: Inject into final JSON payload
        if (departmentID != null) {
            jsonMap.put("DepartmentID", departmentID);
        } else {
            log.warn("Department ID not found for code: " + formattedCode);
        }

        String finalBody = JsonUtil.render(jsonMap);
        body.put("jsonBody", finalBody);
        requestEndPoint.setBody(body);
    }
} catch (Exception ex) {
    log.error("Rule - Modify Body Exception: ", ex);
}

return requestEndPoint;

Cheers!!!

Hi @Sagar_18

I know this one, but how can i automate it , if daily department will add so it will be difficult to update in transform every day.