---
title: Manage configuration placeholders using the API
description: PingOne Advanced Identity Cloud lets you add placeholders to your configuration so you can reference the value of an ESV variable or an ESV secret instead of defining a static value.
component: pingoneaic
page_id: pingoneaic:tenants:configuration-placeholders-api
canonical_url: https://docs.pingidentity.com/pingoneaic/tenants/configuration-placeholders-api.html
section_ids:
  examples: Examples
  configure-tenant-email-provider: Insert ESV placeholders into tenant email provider configuration
  configure-cors: Insert ESV placeholders into CORS configuration
  configure-pingone-worker-service: Insert ESV placeholders into the secondary configuration of a PingOne worker service
  configure-a-journey-node: Insert ESV placeholders into journey node configuration
  configure-ldap-connector: Insert an ESV placeholder into an LDAP connector
---

# Manage configuration placeholders using the API

PingOne Advanced Identity Cloud lets you add placeholders to your configuration so you can reference the value of an [ESV variable](esvs.html#variables) or an [ESV secret](esvs.html#secrets) instead of defining a static value.

For example, if you created an ESV variable named `esv-email-provider-port`, you could reference its value by adding a placeholder of `{"$int" : "&{esv.email.provider.port}"}` to your configuration.

To set a default value in a configuration placeholder, include it after a vertical bar. For example, the following expression sets a default email provider port of 465: `{"$int" : "&{esv.email.provider.port|465}"}`. If no ESV is set, the default value of 465 is used instead.

If you add a placeholder to your configuration and do not set a corresponding ESV or specify a default value, you will not be able to complete a successful promotion.

A configuration property can include a mix of static values and placeholders. For example, if you set `esv-hostname` to `id`, then `&{esv.hostname}.example.com` evaluates to `id.example.com`.

## Examples

### Insert ESV placeholders into tenant email provider configuration

This example shows how to configure placeholders in your tenant email provider configuration. Learn more in [Email provider](email-provider.html).

|   |                                                                                                                                                  |
| - | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|   | The configuration is using an SMTP external email provider, but the same approach can be used to update an MS Graph API external email provider. |

1. [Get an access token](../developer-docs/authenticate-to-rest-api-with-access-token.html#get_an_access_token) that has the `fr:idm:*` scope.

2. Get the email provider configuration:

   > **Collapse: Show request**
   >
   > ```bash
   > $ curl \
   > --request GET 'https://<tenant-env-fqdn>/openidm/config/external.email' \(1)
   > --header 'Content-Type: application/json' \
   > --header 'Accept-API-Version: resource=1.0' \
   > --header 'Authorization: Bearer <access-token>'(2)
   > ```
   >
   > |       |                                                                      |
   > | ----- | -------------------------------------------------------------------- |
   > | **1** | Replace \<tenant-env-fqdn> with the FQDN of your tenant environment. |
   > | **2** | Replace \<access-token> with the access token created in step 1.     |

   > **Collapse: Show response**
   >
   > ```json
   > {
   >     "_id": "external.email",
   >     "auth": {
   >         "enable": true,
   >         "password": "changeit",
   >         "username": "example.user"
   >     },
   >     "connectiontimeout": 30000,
   >     "debug": false,
   >     "from": "\"Example User\" <example.user@example.com>",
   >     "host": "smtp.example.com",
   >     "port": 465,
   >     "smtpProperties": [],
   >     "ssl": {
   >         "enable": true
   >     },
   >     "starttls": {
   >         "enable": false
   >     },
   >     "threadPoolSize": 21,
   >     "timeout": 30000,
   >     "writetimeout": 30000
   > }
   > ```

3. Create a local copy of the email provider configuration from step 2, then substitute in ESV placeholders:

   ```json
   {
       "auth": {
           "enable": true,
           "password": "&{esv.email.provider.password}", (1)
           "username": "&{esv.email.provider.username}" (2)
       },
       "connectiontimeout": 30000,
       "debug": false,
       "from": "\"Example User\" <&{esv.email.provider.from.email}>", (3)
       "host": "&{esv.email.provider.host}", (4)
       "port": {
           "$int": "&{esv.email.provider.port}" (5)
       },
       "smtpProperties": [],
       "ssl": {
           "enable": {
               "$bool": "&{esv.email.provider.use.ssl}" (6)
           }
       },
       "starttls": {
           "enable": false
       },
       "threadPoolSize": 21,
       "timeout": 30000,
       "writetimeout": 30000
   }
   ```

   |       |                                                                      |
   | ----- | -------------------------------------------------------------------- |
   | **1** | Substitution for ESV placeholder `&{esv.email.provider.password}`.   |
   | **2** | Substitution for ESV placeholder `&{esv.email.provider.username}`.   |
   | **3** | Substitution for ESV placeholder `&{esv.email.provider.from.email}`. |
   | **4** | Substitution for ESV placeholder `&{esv.email.provider.host}`        |
   | **5** | Substitution for ESV placeholder `&{esv.email.provider.port}`.       |
   | **6** | Substitution for ESV placeholder `&{esv.email.provider.use.ssl}`.    |

   The following table summarizes the ESVs that correspond with the above placeholders:

   | ESV name                        | ESV type | Expression type | Example value             |
   | ------------------------------- | -------- | --------------- | ------------------------- |
   | `esv-email-provider-password`   | Secret   | n/a             |                           |
   | `esv-email-provider-username`   | Variable | String          | example.user              |
   | `esv-email-provider-from-email` | Variable | String          | example.user\@example.com |
   | `esv-email-provider-host`       | Variable | String          | smtp.example.com          |
   | `esv-email-provider-port`       | Variable | Integer         | 465                       |
   | `esv-email-provider-use-ssl`    | Variable | Boolean         | true                      |

4. Update the email provider configuration:

   > **Collapse: Show request**
   >
   > ```bash
   > $ curl \
   > --request PUT 'https://<tenant-env-fqdn>/openidm/config/external.email' \(1)
   > --header 'Content-Type: application/json' \
   > --header 'Accept-API-Version: resource=1.0' \
   > --header 'Authorization: Bearer <access-token>' \(2)
   > --data-raw '<email-provider-configuration>'(3)
   > ```
   >
   > |       |                                                                                                                     |
   > | ----- | ------------------------------------------------------------------------------------------------------------------- |
   > | **1** | Replace \<tenant-env-fqdn> with the FQDN of your tenant environment.                                                |
   > | **2** | Replace \<access-token> with the access token created in step 1.                                                    |
   > | **3** | Replace \<email-provider-configuration> with the local copy of the email-provider configuration modified in step 3. |

   > **Collapse: Show response**
   >
   > ```json
   > {
   >     "_id": "external.email",
   >     "auth": {
   >         "enable": true,
   >         "password": "&{esv.email.provider.password}",
   >         "username": "&{esv.email.provider.username}"
   >     },
   >     "connectiontimeout": 30000,
   >     "debug": false,
   >     "from": "\"Example User\" <&{esv.email.provider.from.email}>",
   >     "host": "&{esv.email.provider.host}",
   >     "port": {
   >         "$int": "&{esv.email.provider.port}"
   >     },
   >     "smtpProperties": [],
   >     "ssl": {
   >         "enable": {
   >             "$bool": "&{esv.email.provider.use.ssl}"
   >         }
   >     },
   >     "starttls": {
   >         "enable": false
   >     },
   >     "threadPoolSize": 20,
   >     "timeout": 30000,
   >     "writetimeout": 30000
   > }
   > ```

### Insert ESV placeholders into CORS configuration

This example shows how to configure placeholders in your tenant CORS configuration. Learn more in [Allow cross-domain requests with CORS](cors.html).

1. [Get an access token](../developer-docs/authenticate-to-rest-api-with-access-token.html#get_an_access_token) that has the `fr:am:*` scope.

2. Get the CORS configuration:

   > **Collapse: Show request**
   >
   > ```bash
   > $ curl \
   > --request POST 'https://<tenant-env-fqdn>/am/json/global-config/services/CorsService/?_action=nextdescendents' \(1)
   > --header 'Content-Type: application/json' \
   > --header 'Accept-API-Version: resource=1.0' \
   > --header 'Authorization: Bearer <access-token>'(2)
   > ```
   >
   > |       |                                                                      |
   > | ----- | -------------------------------------------------------------------- |
   > | **1** | Replace \<tenant-env-fqdn> with the FQDN of your tenant environment. |
   > | **2** | Replace \<access-token> with the access token created in step 1.     |

   > **Collapse: Show response**
   >
   > ```json
   > {
   >     "result": [
   >         {
   >             "maxAge": 600,
   >             "exposedHeaders": [],
   >             "acceptedHeaders": [
   >                 "accept-api-version",
   >                 "x-requested-with"
   >             ],
   >             "allowCredentials": true,
   >             "acceptedMethods": [
   >                 "GET",
   >                 "POST",
   >                 "PUT",
   >                 "DELETE"
   >             ],
   >             "acceptedOrigins": [
   >                 "https://example.org",
   >                 "https://example.com",
   >                 "https://openam-example.forgeblocks.com"
   >             ],
   >             "enabled": true,
   >             "_id": "example-cors-config", (1)
   >             "_type": {
   >                 "_id": "configuration",
   >                 "name": "Cors Configuration",
   >                 "collection": true
   >             }
   >         }
   >     ]
   > }
   > ```
   >
   > |       |                                                                                |
   > | ----- | ------------------------------------------------------------------------------ |
   > | **1** | The ID of the CORS configuration; in this example it is `example-cors-config`. |

3. Create a local copy of the CORS configuration from step 2, then substitute in an ESV placeholder:

   ```json
   {
       "maxAge": 600,
       "exposedHeaders": [],
       "acceptedHeaders": [
           "accept-api-version",
           "x-requested-with"
       ],
       "allowCredentials": true,
       "acceptedMethods": [
           "GET",
           "POST",
           "PUT",
           "DELETE"
       ],
       "acceptedOrigins": {
           "$array": "&{esv.cors.accepted.origins}" (1)
       },
       "enabled": true,
       "_id": "example-cors-config",
       "_type": {
           "_id": "configuration",
           "name": "Cors Configuration",
           "collection": true
       }
   }
   ```

   |       |                                                                  |
   | ----- | ---------------------------------------------------------------- |
   | **1** | Substitution for ESV placeholder `&{esv.cors.accepted.origins}`. |

   The following table summarizes the ESV that corresponds with the above placeholder:

   | ESV name                    | ESV type | Expression type | Example value                                                                              |
   | --------------------------- | -------- | --------------- | ------------------------------------------------------------------------------------------ |
   | `esv-cors-accepted-origins` | Variable | Array           | \["https\://example.org","https\://example.com","https\://openam-example.forgeblocks.com"] |

4. Update the CORS configuration:

   > **Collapse: Show request**
   >
   > ```bash
   > $ curl \
   > --request PUT 'https://<tenant-env-fqdn>/am/json/global-config/services/CorsService/configuration/<cors-id>' \(1)(2)
   > --header 'Content-Type: application/json' \
   > --header 'Accept-API-Version: resource=1.0' \
   > --header 'Authorization: Bearer <access-token>' \(3)
   > --data-raw '<cors-configuration>'(4)
   > ```
   >
   > |       |                                                                                                            |
   > | ----- | ---------------------------------------------------------------------------------------------------------- |
   > | **1** | Replace \<tenant-env-fqdn> with the FQDN of your tenant environment.                                       |
   > | **2** | Replace \<cors-id> with the CORS configuration ID you found in step 2. For example, `example-cors-config`. |
   > | **3** | Replace \<access-token> with the access token created in step 1.                                           |
   > | **4** | Replace \<cors-configuration> with the local copy of the CORS configuration modified in step 3.            |

   > **Collapse: Show response**
   >
   > ```json
   > {
   >     "_id": "example-cors-settings",
   >     "_rev": "1594160724",
   >     "maxAge": 600,
   >     "exposedHeaders": [],
   >     "acceptedHeaders": [
   >         "accept-api-version",
   >         "x-requested-with"
   >     ],
   >     "allowCredentials": true,
   >     "acceptedMethods": [
   >         "GET",
   >         "POST",
   >         "PUT",
   >         "DELETE"
   >     ],
   >     "acceptedOrigins": {
   >         "$array": "&{esv.cors.accepted.origins}"
   >     },
   >     "enabled": true,
   >     "_type": {
   >         "_id": "configuration",
   >         "name": "Cors Configuration",
   >         "collection": true
   >     }
   > }
   > ```

### Insert ESV placeholders into the secondary configuration of a PingOne worker service

This example shows how to configure placeholders in the secondary configuration of a PingOne worker service. Learn more in [Use PingOne Protect for risk-based authentication and fraud detection](../integrations/pingone-protect.html).

1. [Get an access token](../developer-docs/authenticate-to-rest-api-with-access-token.html#get_an_access_token) that has the `fr:am:*` scope.

2. Get the PingOne worker service secondary configurations:

   > **Collapse: Show request**
   >
   > ```bash
   > $ curl \
   > --request POST 'https://<tenant-env-fqdn>/am/json/realms/root/realms/alpha/realm-config/services/pingOneWorkerService?_action=nextdescendents' \(1)
   > --header 'Content-Type: application/json' \
   > --header 'Accept-API-Version: resource=1.0' \
   > --header 'Authorization: Bearer <access-token>'(2)
   > ```
   >
   > |       |                                                                      |
   > | ----- | -------------------------------------------------------------------- |
   > | **1** | Replace \<tenant-env-fqdn> with the FQDN of your tenant environment. |
   > | **2** | Replace \<access-token> with the access token created in step 1.     |

   > **Collapse: Show response**
   >
   > ```json
   > {
   >     "result": [
   >         {
   >             "clientId": "faa...3c0",
   >             "clientSecretPurpose": "pingoneworkeraic",
   >             "apiUrl": "https://api.pingone.eu/v1",
   >             "authUrl": "https://auth.pingone.eu",
   >             "environmentId": "219...43e",
   >             "_id": "PingOne Worker", (1)
   >             "_type": {
   >                 "_id": "workers",
   >                 "name": "PingOne Worker AIC",
   >                 "collection": true
   >             }
   >         }
   >     ]
   > }
   > ```
   >
   > |       |                                                                                                           |
   > | ----- | --------------------------------------------------------------------------------------------------------- |
   > | **1** | The ID of the PingOne worker service secondary configuration. In this example, it's `PingOne Worker AIC`. |

3. Create a local copy of the PingOne worker service secondary configuration from step 2, then substitute in ESV placeholders:

   ```json
   {
       "result": [
           {
               "clientId": "&{esv.pingone.worker.client.id}", (1)
               "clientSecretPurpose": "pingoneworkeraic",
               "apiUrl": "https://api.pingone.eu/v1",
               "authUrl": "https://auth.pingone.eu",
               "environmentId": "&{esv.pingone.environment.id}", (2)
               "_id": "PingOne Worker",
               "_type": {
                   "_id": "workers",
                   "name": "PingOne Worker AIC",
                   "collection": true
               }
           }
       ]
   }
   ```

   |       |                                                                     |
   | ----- | ------------------------------------------------------------------- |
   | **1** | Substitution for ESV placeholder `&{esv.pingone.worker.client.id}`. |
   | **2** | Substitution for ESV placeholder `&{esv.pingone.environment.id}`.   |

   The following table summarizes the ESVs that correspond with the above placeholders:

   | ESV name                       | ESV type | Expression type | Example value |
   | ------------------------------ | -------- | --------------- | ------------- |
   | `esv-pingone-worker-client-id` | Variable | String          | faa...3c0     |
   | `esv-pingone-environment-id`   | Variable | String          | 219...43e     |

4. Update the PingOne worker service secondary configuration:

   > **Collapse: Show request**
   >
   > ```bash
   > $ curl \
   > --request PUT 'https://<tenant-env-fqdn>/am/json/realms/root/realms/alpha/realm-config/services/pingOneWorkerService/workers/<pingone-worker-secondary-configuration-id>' \(1)(2)
   > --header 'Content-Type: application/json' \
   > --header 'Accept-API-Version: resource=1.0' \
   > --header 'Authorization: Bearer <access-token>' \(3)
   > --data-raw '<pingone-worker-secondary-configuration>'(4)
   > ```
   >
   > |       |                                                                                                                                                                                                             |
   > | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
   > | **1** | Replace \<tenant-env-fqdn> with the FQDN of your tenant environment.                                                                                                                                        |
   > | **2** | Replace \<pingone-worker-secondary-configuration-id> with the PingOne worker service configuration ID you found in step 2, ensuring that any spaces are URL encoded. For example, `PingOne%20Worker%20AIC`. |
   > | **3** | Replace \<access-token> with the access token created in step 1.                                                                                                                                            |
   > | **4** | Replace \<pingone-worker-secondary-configuration> with the local copy of the CORS configuration modified in step 3.                                                                                         |

   > **Collapse: Show response**
   >
   > ```json
   > {
   >     "clientId": "&{esv.pingone.worker.client.id}",
   >     "clientSecretPurpose": "pingoneworkeraic",
   >     "apiUrl": "https://api.pingone.eu/v1",
   >     "authUrl": "https://auth.pingone.eu",
   >     "environmentId": "&{esv.pingone.environment.id}",
   >     "_id": "PingOne Worker AIC",
   >     "_type": {
   >         "_id": "workers",
   >         "name": "PingOne Worker",
   >         "collection": true
   >     }
   > }
   > ```

### Insert ESV placeholders into journey node configuration

This example shows how to configure placeholders in an LDAP decision node, but the approach can be adapted to configure placeholders in any journey node.

1. [Get an access token](../developer-docs/authenticate-to-rest-api-with-access-token.html#get_an_access_token) that has the `fr:am:*` scope.

2. Get the configuration of the journey that contains the LDAP decision node so you can extract the ID of the node:

   > **Collapse: Show request**
   >
   > ```bash
   > $ curl \
   > --request GET 'https://<tenant-env-fqdn>/am/json/realms/root/realms/<realm>/realm-config/authentication/authenticationtrees/trees/<journey-name>' \(1)(2)(3)
   > --header 'Content-Type: application/json' \
   > --header 'Accept-API-Version: resource=1.0' \
   > --header 'Authorization: Bearer <access-token>'(4)
   > ```
   >
   > |       |                                                                                                                           |
   > | ----- | ------------------------------------------------------------------------------------------------------------------------- |
   > | **1** | Replace \<tenant-env-fqdn> with the FQDN of your tenant environment.                                                      |
   > | **2** | Replace \<realm> with the realm that contains the journey that contains the LDAP decision node. For example, `alpha`.     |
   > | **3** | Replace \<journey-name> with the name of the journey that contains the LDAP decision node. For example, `UpdatePassword`. |
   > | **4** | Replace \<access-token> with the access token created in step 1.                                                          |

   > **Collapse: Show response**
   >
   > ```json
   > {
   >     "_id": "ldapJourney",
   >     "_rev": "1341035508",
   >     "identityResource": "managed/alpha_user",
   >     "uiConfig": {
   >         "categories": "[]"
   >     },
   >     "entryNodeId": "76e74888-73e1-46e2-aa33-5e4c8b07ccec",
   >     "nodes": {
   >         "76e74888-73e1-46e2-aa33-5e4c8b07ccec": {
   >             "x": 249,
   >             "y": 171.015625,
   >             "connections": {
   >                 "outcome": "c12abfe7-ae71-42e6-a6b3-e8f4d4d05549"
   >             },
   >             "nodeType": "PageNode",
   >             "displayName": "Page Node"
   >         },
   >         "2082c1ad-f5ad-4b6d-aada-dd4fff4dc6f3": { (1)
   >             "x": 510,
   >             "y": 181.015625,
   >             "connections": {
   >                 "CANCELLED": "e301438c-0bd0-429c-ab0c-66126501069a",
   >                 "EXPIRED": "e301438c-0bd0-429c-ab0c-66126501069a",
   >                 "FALSE": "e301438c-0bd0-429c-ab0c-66126501069a",
   >                 "LOCKED": "e301438c-0bd0-429c-ab0c-66126501069a",
   >                 "TRUE": "70e691a5-1e33-4ac3-a356-e7b6d60d92e0"
   >             },
   >             "nodeType": "LdapDecisionNode",
   >             "displayName": "LDAP Decision"
   >         }
   >     },
   >     "staticNodes": {
   >         "startNode": {
   >             "x": 50,
   >             "y": 250
   >         },
   >         "70e691a5-1e33-4ac3-a356-e7b6d60d92e0": {
   >             "x": 792,
   >             "y": 181
   >         },
   >         "e301438c-0bd0-429c-ab0c-66126501069a": {
   >             "x": 795,
   >             "y": 307
   >         }
   >     },
   >     "enabled": true
   > }
   > ```
   >
   > |       |                                                                                                       |
   > | ----- | ----------------------------------------------------------------------------------------------------- |
   > | **1** | The ID of the `LdapDecisionNode` node; in this example, it is `2082c1ad-f5ad-4b6d-aada-dd4fff4dc6f3`. |

3. Get the configuration of the LDAP decision node:

   > **Collapse: Show request**
   >
   > ```bash
   > $ curl \
   > --request GET 'https://<tenant-env-fqdn>/am/json/realms/root/realms/<realm>/realm-config/authentication/authenticationtrees/nodes/LdapDecisionNode/<node-id>' \(1)(2)(3)(4)
   > --header 'Content-Type: application/json' \
   > --header 'Accept-API-Version: resource=1.0' \
   > --header 'Authorization: Bearer <access-token>'(5)
   > ```
   >
   > |       |                                                                                                                       |
   > | ----- | --------------------------------------------------------------------------------------------------------------------- |
   > | **1** | Replace \<tenant-env-fqdn> with the FQDN of your tenant environment.                                                  |
   > | **2** | Replace \<realm> with the realm that contains the journey that contains the LDAP decision node. For example, `alpha`. |
   > | **3** | The node name specified is `LdapDecisionNode`.                                                                        |
   > | **4** | Replace \<node-id> with the node ID you found in step 2. For example, `2082c1ad-f5ad-4b6d-aada-dd4fff4dc6f3`.         |
   > | **5** | Replace \<access-token> with the access token created in step 1.                                                      |

   > **Collapse: Show response**
   >
   > ```json
   > {
   >     "_id": "2082c1ad-f5ad-4b6d-aada-dd4fff4dc6f3",
   >     "_rev": "-752122233",
   >     "searchFilterAttributes": [
   >         "mail"
   >     ],
   >     "userProfileAttribute": "uid",
   >     "primaryServers": [
   >         "userstore-0.userstore:1389",
   >         "userstore-1.userstore:1389",
   >         "userstore-2.userstore:1389"
   >     ],
   >     "ldapConnectionMode": "LDAP",
   >     "trustAllServerCertificates": false,
   >     "heartbeatInterval": 10,
   >     "returnUserDn": true,
   >     "searchScope": "SUBTREE",
   >     "heartbeatTimeUnit": "SECONDS",
   >     "secondaryServers": [],
   >     "ldapOperationsTimeout": 0,
   >     "userCreationAttrs": [],
   >     "minimumPasswordLength": 8,
   >     "accountSearchBaseDn": [
   >         "o=example"
   >     ],
   >     "adminPassword": null,
   >     "adminDn": "uid=admin",
   >     "beheraEnabled": true,
   >     "_type": {
   >         "_id": "LdapDecisionNode",
   >         "name": "LDAP Decision",
   >         "collection": true
   >     },
   >     "_outcomes": [
   >         {
   >             "id": "TRUE",
   >             "displayName": "True"
   >         },
   >         {
   >             "id": "FALSE",
   >             "displayName": "False"
   >         },
   >         {
   >             "id": "LOCKED",
   >             "displayName": "Locked"
   >         },
   >         {
   >             "id": "CANCELLED",
   >             "displayName": "Cancelled"
   >         },
   >         {
   >             "id": "EXPIRED",
   >             "displayName": "Expired"
   >         }
   >     ]
   > }
   > ```

4. Create a local copy of the node configuration from step 3, then substitute in ESV placeholders:

   ```json
   {
       "searchFilterAttributes": [
           "mail"
       ],
       "userProfileAttribute": "uid",
       "primaryServers" : {
           "$list": "&{esv.journey.ldap.primary.servers}" (1)
       },
       "ldapConnectionMode": "LDAP",
       "trustAllServerCertificates": false,
       "heartbeatInterval": {
           "$int": "&{esv.journey.ldap.heartbeat.interval}" (2)
       },
       "returnUserDn": true,
       "searchScope": "SUBTREE",
       "heartbeatTimeUnit": "&{esv.journey.ldap.heartbeat.unit}", (3)
       "secondaryServers": [],
       "ldapOperationsTimeout": 0,
       "userCreationAttrs": [],
       "minimumPasswordLength": 8,
       "accountSearchBaseDn": [
           "o=example"
       ],
       "adminPassword": {
           "$string": "&{esv.journey.ldap.password}" (4)
       },
       "adminDn": "&{esv.journey.ldap.username}", (5)
       "beheraEnabled": {
           "$bool": "&{esv.journey.ldap.behera.enabled}" (6)
       },
       "_type": {
           "_id": "LdapDecisionNode",
           "name": "LDAP Decision",
           "collection": true
       },
       "_outcomes": [
           {
               "id": "TRUE",
               "displayName": "True"
           },
           {
               "id": "FALSE",
               "displayName": "False"
           },
           {
               "id": "LOCKED",
               "displayName": "Locked"
           },
           {
               "id": "CANCELLED",
               "displayName": "Cancelled"
           },
           {
               "id": "EXPIRED",
               "displayName": "Expired"
           }
       ]
   }
   ```

   |       |                                                                            |
   | ----- | -------------------------------------------------------------------------- |
   | **1** | Substitution for ESV placeholder `&{esv.journey.ldap.primary.servers}`.    |
   | **2** | Substitution for ESV placeholder `&{esv.journey.ldap.heartbeat.interval}`. |
   | **3** | Substitution for ESV placeholder `&{esv.journey.ldap.heartbeat.unit}`.     |
   | **4** | Substitution for ESV placeholder `&{esv.journey.ldap.password}`.           |
   | **5** | Substitution for ESV placeholder `&{esv.journey.ldap.username}`.           |
   | **6** | Substitution for ESV placeholder `&{esv.journey.ldap.behera.enabled}`.     |

   The following table summarizes the ESVs that correspond with the above placeholders:

   | ESV name                              | ESV type | Expression type | Example value                                                                    |
   | ------------------------------------- | -------- | --------------- | -------------------------------------------------------------------------------- |
   | `esv-journey-ldap-primary-servers`    | Variable | List            | userstore-0.userstore:1389,userstore-1.userstore:1389,userstore-2.userstore:1389 |
   | `esv-journey-ldap-heartbeat-interval` | Variable | Integer         | 10                                                                               |
   | `esv-journey-ldap-heartbeat-unit`     | Variable | String          | SECONDS                                                                          |
   | `esv-journey-ldap-password`           | Secret   | n/a             | changeit                                                                         |
   | `esv-journey-ldap-username`           | Variable | String          | uid=myadmin                                                                      |
   | `esv-journey-ldap-behera-enabled`     | Variable | Boolean         | false                                                                            |

5. Update the configuration of the LDAP decision node:

   > **Collapse: Show request**
   >
   > ```bash
   > $ curl \
   > --request PUT 'https://<tenant-env-fqdn>/am/json/realms/root/realms/<realm>/realm-config/authentication/authenticationtrees/nodes/LdapDecisionNode/<node-id>' \(1)(2)(3)(4)
   > --header 'Content-Type: application/json' \
   > --header 'Accept-API-Version: resource=1.0' \
   > --header 'Authorization: Bearer <access-token>' \(5)
   > --data-raw '<node-configuration>'(6)
   > ```
   >
   > |       |                                                                                                                       |
   > | ----- | --------------------------------------------------------------------------------------------------------------------- |
   > | **1** | Replace \<tenant-env-fqdn> with the FQDN of your tenant environment.                                                  |
   > | **2** | Replace \<realm> with the realm that contains the journey that contains the LDAP decision node. For example, `alpha`. |
   > | **3** | The node name specified is `LdapDecisionNode`.                                                                        |
   > | **4** | Replace \<node-id> with the node ID you found in step 2. For example, `2082c1ad-f5ad-4b6d-aada-dd4fff4dc6f3`.         |
   > | **5** | Replace \<access-token> with the access token created in step 1.                                                      |
   > | **6** | Replace \<node-configuration> with the local copy of the node configuration modified in step 4.                       |

   > **Collapse: Show response**
   >
   > ```json
   > {
   >     "_id": "2082c1ad-f5ad-4b6d-aada-dd4fff4dc6f3",
   >     "_rev": "1359037709",
   >     "searchFilterAttributes": [
   >         "mail"
   >     ],
   >     "userProfileAttribute": "uid",
   >     "primaryServers": {
   >         "$list": "&{esv.journey.ldap.servers}"
   >     },
   >     "ldapConnectionMode": "LDAP",
   >     "trustAllServerCertificates": false,
   >     "heartbeatInterval": {
   >         "$int": "&{esv.journey.ldap.heartbeat.interval}"
   >     },
   >     "returnUserDn": true,
   >     "searchScope": "SUBTREE",
   >     "heartbeatTimeUnit": "&{esv.journey.ldap.heartbeat.unit}",
   >     "secondaryServers": [],
   >     "ldapOperationsTimeout": 0,
   >     "userCreationAttrs": [],
   >     "minimumPasswordLength": 8,
   >     "accountSearchBaseDn": [
   >         "o=example"
   >     ],
   >     "adminPassword": {
   >         "$string": "&{esv.journey.ldap.password}"
   >     },
   >     "adminDn": "&{esv.journey.ldap.username}",
   >     "beheraEnabled": {
   >         "$bool": "&{esv.journey.ldap.behera.enabled}"
   >     },
   >     "_type": {
   >         "_id": "LdapDecisionNode",
   >         "name": "LDAP Decision",
   >         "collection": true
   >     },
   >     "_outcomes": [
   >         {
   >             "id": "TRUE",
   >             "displayName": "True"
   >         },
   >         {
   >             "id": "FALSE",
   >             "displayName": "False"
   >         },
   >         {
   >             "id": "LOCKED",
   >             "displayName": "Locked"
   >         },
   >         {
   >             "id": "CANCELLED",
   >             "displayName": "Cancelled"
   >         },
   >         {
   >             "id": "EXPIRED",
   >             "displayName": "Expired"
   >         }
   >     ]
   > }
   > ```

### Insert an ESV placeholder into an LDAP connector

This example shows how to configure a placeholder for an LDAP connector configured with the password for an LDAP server.

When a connector is created, Advanced Identity Cloud stores any secret or password in the connector's configuration as an encrypted object. If the encrypted object is promoted, it cannot be unencrypted in an upper environment because the encryption keys are different in each environment. Therefore, the encrypted object must be stored in an ESV secret and replaced in the configuration with an ESV placeholder.

1. [Get an access token](../developer-docs/authenticate-to-rest-api-with-access-token.html#get_an_access_token) that has the `fr:idm:*` scope.

2. Get the configuration of the LDAP connector:

   > **Collapse: Show request**
   >
   > ```bash
   > $ curl \
   > --request GET 'https://<tenant-env-fqdn>/openidm/config/provisioner.openicf/<connector-id>' \(1)(2)
   > --header 'Content-Type: application/json' \
   > --header 'Authorization: Bearer <access-token>'(3)
   > ```
   >
   > |       |                                                                                          |
   > | ----- | ---------------------------------------------------------------------------------------- |
   > | **1** | Replace \<tenant-env-fqdn> with the FQDN of your tenant environment.                     |
   > | **2** | Replace \<connector-id> with the name of your connector. For example, `myldapconnector`. |
   > | **3** | Replace \<access-token> with the access token created in step 1.                         |

   > **Collapse: Show response**
   >
   > ```json
   > {
   >   "_id": "provisioner.openicf/myldapconnector",
   >   "configurationProperties": {
   >     "accountObjectClasses": [
   >       "top",
   >       "person",
   >       "organizationalPerson",
   >       "inetOrgPerson"
   >     ],
   >     "accountSearchFilter": null,
   >     "accountSynchronizationFilter": null,
   >     "accountUserNameAttributes": [
   >       "uid"
   >     ],
   >     "attributesToSynchronize": [],
   >     "baseContexts": [
   >       "ou=identities"
   >     ],
   >     "baseContextsToSynchronize": [
   >       "ou=identities"
   >     ],
   >     "blockSize": "100",
   >     "changeLogBlockSize": "100",
   >     "changeNumberAttribute": "changeNumber",
   >     "credentials": { (1)
   >       "$crypto": {
   >         "type": "x-simple-encryption",
   >         "value": {
   >           "cipher": "AES/CBC/PKCS5Padding",
   >           "data": "hfJKTFhe+c6wozK/OEKMEw==",
   >           "iv": "G/1aF6oKS5/bzlkEzsmK0A==",
   >           "keySize": 16,
   >           "mac": "QSp/OAIEPWp9vkDhyZtK5Q==",
   >           "purpose": "idm.config.encryption",
   >           "salt": "6gSguT4PDQdKsPOrcItx7Q==",
   >           "stableId": "openidm-sym-default"
   >         }
   >       } (2)
   >     },
   >     "failover": [],
   >     "filterWithOrInsteadOfAnd": false,
   >     "groupMemberAttribute": "uniqueMember",
   >     "groupObjectClasses": [],
   >     "groupSearchFilter": null,
   >     ...
   >     },
   >   ...
   > }
   > ```
   >
   > |       |                                                                              |
   > | ----- | ---------------------------------------------------------------------------- |
   > | **1** | Opening bracket of the encrypted object containing the connector's password. |
   > | **2** | Closing bracket of the encrypted object containing the connector's password. |

3. Create a local copy of the connector configuration from step 2, then substitute in an ESV placeholder for the encrypted object:

   ```json
   {
     "_id": "provisioner.openicf/myldapconnector",
     "configurationProperties": {
       "accountObjectClasses": [
         "top",
         "person",
         "organizationalPerson",
         "inetOrgPerson"
       ],
       "accountSearchFilter": null,
       "accountSynchronizationFilter": null,
       "accountUserNameAttributes": [
         "uid"
       ],
       "attributesToSynchronize": [],
       "baseContexts": [
         "ou=identities"
       ],
       "baseContextsToSynchronize": [
         "ou=identities"
       ],
       "blockSize": "100",
       "changeLogBlockSize": "100",
       "changeNumberAttribute": "changeNumber",
       "credentials": "&{esv.connector.ldap.password}", (1)
       "failover": [],
       "filterWithOrInsteadOfAnd": false,
       "groupMemberAttribute": "uniqueMember",
       "groupObjectClasses": [],
       "groupSearchFilter": null,
       ...
       },
     ...
   }
   ```

   |       |                                                                                            |
   | ----- | ------------------------------------------------------------------------------------------ |
   | **1** | Substitution of ESV placeholder `&{esv.connector.ldap.password}` for the encrypted object. |

   The following table summarizes the ESV that corresponds with the above placeholder and contains the encrypted object from the connector configuration returned in step 2:

   | ESV name                      | ESV type | Expression type | Example value                                                                                                                                                                                                                                                                                                                |
   | ----------------------------- | -------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
   | `esv-connector-ldap-password` | Secret   | n/a             | {"$crypto": {"type": "x-simple-encryption", "value": {"cipher": "AES/CBC/PKCS5Padding", "data": "hfJKTFhe+c6wozK/OEKMEw==", "iv": "G/1aF6oKS5/bzlkEzsmK0A==", "keySize": 16, "mac": "QSp/OAIEPWp9vkDhyZtK5Q==", "purpose": "idm.config.encryption", "salt": "6gSguT4PDQdKsPOrcItx7Q==", "stableId": "openidm-sym-default"}}} |

4. Update the connector configuration:

   > **Collapse: Show request**
   >
   > ```bash
   > $ curl \
   > --request PUT 'https://<tenant-env-fqdn>/openidm/config/provisioner.openicf/<connector-id>' \(1)(2)
   > --header 'Content-Type: application/json' \
   > --header 'Authorization: Bearer <access-token>'\(3)
   > --data-raw '<connector-configuration>'(4)
   > ```
   >
   > |       |                                                                                                           |
   > | ----- | --------------------------------------------------------------------------------------------------------- |
   > | **1** | Replace \<tenant-env-fqdn> with the FQDN of your tenant environment.                                      |
   > | **2** | Replace \<connector-id> with the name of your connector. For example, `myldapconnector`.                  |
   > | **3** | Replace \<access-token> with the access token created in step 1.                                          |
   > | **4** | Replace \<connector-configuration> with the local copy of the connector configuration modified in step 3. |

   > **Collapse: Show response**
   >
   > ```json
   > {
   >   "_id": "provisioner.openicf/myldapconnector",
   >   "configurationProperties": {
   >     "accountObjectClasses": [
   >       "top",
   >       "person",
   >       "organizationalPerson",
   >       "inetOrgPerson"
   >     ],
   >     "accountSearchFilter": null,
   >     "accountSynchronizationFilter": null,
   >     "accountUserNameAttributes": [
   >       "uid"
   >     ],
   >     "attributesToSynchronize": [],
   >     "baseContexts": [
   >       "ou=identities"
   >     ],
   >     "baseContextsToSynchronize": [
   >       "ou=identities"
   >     ],
   >     "blockSize": "100",
   >     "changeLogBlockSize": "100",
   >     "changeNumberAttribute": "changeNumber",
   >     "credentials": "&{esv.connector.ldap.password}",
   >     "failover": [],
   >     "filterWithOrInsteadOfAnd": false,
   >     "groupMemberAttribute": "uniqueMember",
   >     "groupObjectClasses": [],
   >     "groupSearchFilter": null,
   >     ...
   >     },
   >   ...
   > }
   > ```
